likhonsheikh commited on
Commit
650c99a
·
verified ·
1 Parent(s): 4ebde41

Upload app.py - Ubuntu Sandbox v2.0

Browse files
Files changed (1) hide show
  1. app.py +1440 -0
app.py ADDED
@@ -0,0 +1,1440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Ubuntu Sandbox Environment for HuggingFace Spaces
4
+ A production-grade, secure, and AI-accessible Ubuntu development environment.
5
+ Version: 2.0.0
6
+ Author: MiniMax Agent
7
+ License: MIT
8
+
9
+ Features:
10
+ - Secure command execution with sandboxing
11
+ - AI-friendly REST API
12
+ - Real-time monitoring and logging
13
+ - Resource management and limits
14
+ - Session management
15
+ - File system operations
16
+ - System monitoring
17
+ """
18
+
19
+ import os
20
+ import sys
21
+ import subprocess
22
+ import pty
23
+ import select
24
+ import signal
25
+ import json
26
+ import time
27
+ import threading
28
+ import logging
29
+ import hashlib
30
+ import tempfile
31
+ import contextlib
32
+ from pathlib import Path
33
+ from datetime import datetime, timezone
34
+ from typing import Dict, List, Optional, Any, Union
35
+ import uuid
36
+ import re
37
+ import functools
38
+ import asyncio
39
+ from dataclasses import dataclass
40
+ from enum import Enum
41
+
42
+ # Core dependencies
43
+ import gradio as gr
44
+ import psutil
45
+ import requests
46
+ from werkzeug.exceptions import HTTPException
47
+
48
+ # Version info
49
+ __version__ = "2.0.0"
50
+ __author__ = "MiniMax Agent"
51
+ __license__ = "MIT"
52
+
53
+ # Configuration
54
+ @dataclass
55
+ class Config:
56
+ """Application configuration"""
57
+ # Security settings
58
+ MAX_COMMAND_LENGTH: int = 1000
59
+ MAX_FILE_SIZE: int = 10 * 1024 * 1024 # 10MB
60
+ COMMAND_TIMEOUT: int = 30
61
+ SESSION_TIMEOUT: int = 3600
62
+ RATE_LIMIT_REQUESTS: int = 100
63
+ RATE_LIMIT_WINDOW: int = 60 # seconds
64
+
65
+ # Resource limits
66
+ MAX_MEMORY_MB: int = 4096
67
+ MAX_DISK_GB: int = 10
68
+ MAX_CPU_PERCENT: int = 80
69
+
70
+ # File paths
71
+ WORKSPACE_PATH: str = "/workspace"
72
+ LOGS_PATH: str = "/workspace/logs"
73
+
74
+ # Security - restricted commands
75
+ RESTRICTED_COMMANDS: List[str] = None
76
+
77
+ def __post_init__(self):
78
+ if self.RESTRICTED_COMMANDS is None:
79
+ self.RESTRICTED_COMMANDS = [
80
+ "sudo", "su", "passwd", "useradd", "userdel", "usermod",
81
+ "chmod", "chown", "mount", "umount", "fdisk", "mkfs",
82
+ "dd", "shred", "docker", "systemctl", "service",
83
+ "init", "reboot", "shutdown", "halt", "poweroff",
84
+ "crontab", "at", "screen", "tmux", "vim", "emacs",
85
+ "nohup", "setsid", "exec", "source"
86
+ ]
87
+
88
+ # Global configuration
89
+ config = Config()
90
+
91
+ # Setup logging
92
+ def setup_logging():
93
+ """Setup comprehensive logging"""
94
+ os.makedirs(config.LOGS_PATH, exist_ok=True)
95
+
96
+ # Create logger
97
+ logger = logging.getLogger("ubuntu_sandbox")
98
+ logger.setLevel(logging.INFO)
99
+
100
+ # File handler
101
+ file_handler = logging.FileHandler(
102
+ Path(config.LOGS_PATH) / "sandbox.log"
103
+ )
104
+ file_handler.setLevel(logging.INFO)
105
+
106
+ # Console handler
107
+ console_handler = logging.StreamHandler()
108
+ console_handler.setLevel(logging.WARNING)
109
+
110
+ # Formatter
111
+ formatter = logging.Formatter(
112
+ '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
113
+ )
114
+ file_handler.setFormatter(formatter)
115
+ console_handler.setFormatter(formatter)
116
+
117
+ logger.addHandler(file_handler)
118
+ logger.addHandler(console_handler)
119
+
120
+ return logger
121
+
122
+ logger = setup_logging()
123
+
124
+ # Session management
125
+ class SessionManager:
126
+ """Manage user sessions with timeout and tracking"""
127
+
128
+ def __init__(self):
129
+ self.sessions: Dict[str, Dict] = {}
130
+ self.command_history: Dict[str, List[str]] = {}
131
+ self.current_directories: Dict[str, str] = {}
132
+ self.last_activity: Dict[str, float] = {}
133
+
134
+ def create_session(self, session_id: Optional[str] = None) -> str:
135
+ """Create a new session"""
136
+ if not session_id:
137
+ session_id = str(uuid.uuid4())
138
+
139
+ self.sessions[session_id] = {
140
+ "id": session_id,
141
+ "created_at": datetime.now(timezone.utc).isoformat(),
142
+ "commands_executed": 0,
143
+ "files_created": 0,
144
+ "memory_usage": 0
145
+ }
146
+ self.command_history[session_id] = []
147
+ self.current_directories[session_id] = config.WORKSPACE_PATH
148
+ self.last_activity[session_id] = time.time()
149
+
150
+ logger.info(f"Session created: {session_id}")
151
+ return session_id
152
+
153
+ def get_session(self, session_id: str) -> Optional[Dict]:
154
+ """Get session information"""
155
+ return self.sessions.get(session_id)
156
+
157
+ def update_activity(self, session_id: str):
158
+ """Update last activity timestamp"""
159
+ if session_id in self.last_activity:
160
+ self.last_activity[session_id] = time.time()
161
+
162
+ def cleanup_expired_sessions(self):
163
+ """Clean up expired sessions"""
164
+ current_time = time.time()
165
+ expired_sessions = []
166
+
167
+ for session_id, last_activity in self.last_activity.items():
168
+ if current_time - last_activity > config.SESSION_TIMEOUT:
169
+ expired_sessions.append(session_id)
170
+
171
+ for session_id in expired_sessions:
172
+ self.delete_session(session_id)
173
+
174
+ def delete_session(self, session_id: str):
175
+ """Delete a session and all associated data"""
176
+ self.sessions.pop(session_id, None)
177
+ self.command_history.pop(session_id, None)
178
+ self.current_directories.pop(session_id, None)
179
+ self.last_activity.pop(session_id, None)
180
+
181
+ logger.info(f"Session deleted: {session_id}")
182
+
183
+ def get_current_directory(self, session_id: str) -> str:
184
+ """Get current working directory for session"""
185
+ return self.current_directories.get(session_id, config.WORKSPACE_PATH)
186
+
187
+ def set_current_directory(self, session_id: str, directory: str):
188
+ """Set current working directory for session"""
189
+ self.current_directories[session_id] = directory
190
+
191
+ def add_command_to_history(self, session_id: str, command: str):
192
+ """Add command to session history"""
193
+ if session_id in self.command_history:
194
+ self.command_history[session_id].append(command)
195
+ # Keep only last 100 commands
196
+ if len(self.command_history[session_id]) > 100:
197
+ self.command_history[session_id] = self.command_history[session_id][-100:]
198
+
199
+ # Security validation
200
+ class SecurityValidator:
201
+ """Validate commands and inputs for security"""
202
+
203
+ @staticmethod
204
+ def validate_command(command: str) -> tuple[bool, str]:
205
+ """Validate command for security issues"""
206
+ if not command or not command.strip():
207
+ return False, "Empty command"
208
+
209
+ if len(command) > config.MAX_COMMAND_LENGTH:
210
+ return False, f"Command too long (max {config.MAX_COMMAND_LENGTH} characters)"
211
+
212
+ # Check for restricted commands
213
+ command_lower = command.lower().strip()
214
+ for restricted in config.RESTRICTED_COMMANDS:
215
+ if command_lower.startswith(restricted):
216
+ return False, f"Restricted command: {restricted}"
217
+
218
+ # Check for dangerous patterns
219
+ dangerous_patterns = [
220
+ r'rm\s+-rf\s+/',
221
+ r'mkdir\s+.*/\.\.',
222
+ r'cd\s+\.\.\.\s*/',
223
+ r'>\s*/dev/',
224
+ r'&\s*\|\s*',
225
+ r';\s*shutdown',
226
+ r';\s*reboot',
227
+ r';\s*halt',
228
+ ]
229
+
230
+ for pattern in dangerous_patterns:
231
+ if re.search(pattern, command):
232
+ return False, f"Dangerous pattern detected: {pattern}"
233
+
234
+ return True, "Command is valid"
235
+
236
+ @staticmethod
237
+ def validate_filename(filename: str) -> tuple[bool, str]:
238
+ """Validate filename for security"""
239
+ if not filename or not filename.strip():
240
+ return False, "Empty filename"
241
+
242
+ # Check for path traversal
243
+ if ".." in filename or filename.startswith("/"):
244
+ return False, "Path traversal not allowed"
245
+
246
+ # Check for dangerous characters
247
+ dangerous_chars = ['<', '>', ':', '"', '|', '?', '*']
248
+ if any(char in filename for char in dangerous_chars):
249
+ return False, f"Invalid characters in filename: {dangerous_chars}"
250
+
251
+ # Check length
252
+ if len(filename) > 255:
253
+ return False, "Filename too long"
254
+
255
+ return True, "Filename is valid"
256
+
257
+ @staticmethod
258
+ def validate_file_content(content: str) -> tuple[bool, str]:
259
+ """Validate file content for size"""
260
+ if len(content.encode('utf-8')) > config.MAX_FILE_SIZE:
261
+ return False, f"File too large (max {config.MAX_FILE_SIZE} bytes)"
262
+
263
+ return True, "Content is valid"
264
+
265
+ # Command execution
266
+ class SecureExecutor:
267
+ """Secure command execution with resource management"""
268
+
269
+ def __init__(self, session_manager: SessionManager):
270
+ self.session_manager = session_manager
271
+ self.running_processes: Dict[str, subprocess.Popen] = {}
272
+
273
+ def execute_command(self, command: str, session_id: str) -> Dict[str, Any]:
274
+ """Execute command securely"""
275
+ # Validate command
276
+ is_valid, validation_message = SecurityValidator.validate_command(command)
277
+ if not is_valid:
278
+ logger.warning(f"Command validation failed for session {session_id}: {validation_message}")
279
+ return {
280
+ "success": False,
281
+ "error": f"Command validation failed: {validation_message}",
282
+ "output": "",
283
+ "exit_code": -1,
284
+ "timestamp": datetime.now(timezone.utc).isoformat()
285
+ }
286
+
287
+ # Get session info
288
+ session = self.session_manager.get_session(session_id)
289
+ if not session:
290
+ return {
291
+ "success": False,
292
+ "error": "Invalid session",
293
+ "output": "",
294
+ "exit_code": -1,
295
+ "timestamp": datetime.now(timezone.utc).isoformat()
296
+ }
297
+
298
+ # Update activity
299
+ self.session_manager.update_activity(session_id)
300
+
301
+ # Get current directory
302
+ current_dir = self.session_manager.get_current_directory(session_id)
303
+
304
+ start_time = time.time()
305
+ process_id = str(uuid.uuid4())
306
+
307
+ try:
308
+ # Check resource usage before execution
309
+ memory_usage = psutil.virtual_memory().percent
310
+ cpu_usage = psutil.cpu_percent(interval=0.1)
311
+
312
+ if memory_usage > config.MAX_CPU_PERCENT:
313
+ return {
314
+ "success": False,
315
+ "error": f"System resources overloaded: Memory {memory_usage:.1f}%",
316
+ "output": "",
317
+ "exit_code": -1,
318
+ "timestamp": datetime.now(timezone.utc).isoformat()
319
+ }
320
+
321
+ # Execute command
322
+ process = subprocess.Popen(
323
+ command,
324
+ shell=True,
325
+ stdout=subprocess.PIPE,
326
+ stderr=subprocess.PIPE,
327
+ text=True,
328
+ cwd=current_dir,
329
+ preexec_fn=os.setsid
330
+ )
331
+
332
+ self.running_processes[process_id] = process
333
+
334
+ # Wait for completion with timeout
335
+ try:
336
+ stdout, stderr = process.communicate(timeout=config.COMMAND_TIMEOUT)
337
+ execution_time = time.time() - start_time
338
+
339
+ # Update session stats
340
+ session["commands_executed"] += 1
341
+ self.session_manager.add_command_to_history(session_id, command)
342
+
343
+ # Log execution
344
+ logger.info(
345
+ f"Command executed - Session: {session_id}, "
346
+ f"Command: {command[:50]}..., "
347
+ f"Exit code: {process.returncode}, "
348
+ f"Time: {execution_time:.2f}s"
349
+ )
350
+
351
+ result = {
352
+ "success": process.returncode == 0,
353
+ "output": stdout + stderr if stderr else stdout,
354
+ "exit_code": process.returncode,
355
+ "execution_time": execution_time,
356
+ "timestamp": datetime.now(timezone.utc).isoformat(),
357
+ "process_id": process_id
358
+ }
359
+
360
+ # Handle directory changes
361
+ if command.strip().startswith("cd "):
362
+ new_path = command.strip()[3:].strip()
363
+ if new_path:
364
+ try:
365
+ if new_path.startswith("/"):
366
+ full_path = new_path
367
+ else:
368
+ full_path = str(Path(current_dir) / new_path).resolve()
369
+ self.session_manager.set_current_directory(session_id, str(full_path))
370
+ result["new_directory"] = str(full_path)
371
+ except Exception as e:
372
+ logger.warning(f"Directory change failed: {e}")
373
+
374
+ return result
375
+
376
+ except subprocess.TimeoutExpired:
377
+ # Kill the process
378
+ try:
379
+ os.killpg(os.getpgid(process.pid), signal.SIGTERM)
380
+ process.wait(timeout=5)
381
+ except:
382
+ process.kill()
383
+
384
+ logger.warning(f"Command timeout - Session: {session_id}, Command: {command[:50]}...")
385
+ return {
386
+ "success": False,
387
+ "error": f"Command timed out after {config.COMMAND_TIMEOUT} seconds",
388
+ "output": "",
389
+ "exit_code": -1,
390
+ "timestamp": datetime.now(timezone.utc).isoformat(),
391
+ "process_id": process_id
392
+ }
393
+
394
+ except Exception as e:
395
+ logger.error(f"Command execution error - Session: {session_id}, Error: {e}")
396
+ return {
397
+ "success": False,
398
+ "error": f"Execution error: {str(e)}",
399
+ "output": "",
400
+ "exit_code": -1,
401
+ "timestamp": datetime.now(timezone.utc).isoformat(),
402
+ "process_id": process_id
403
+ }
404
+
405
+ finally:
406
+ # Clean up process
407
+ self.running_processes.pop(process_id, None)
408
+
409
+ def kill_process(self, process_id: str) -> bool:
410
+ """Kill a running process"""
411
+ process = self.running_processes.get(process_id)
412
+ if process:
413
+ try:
414
+ os.killpg(os.getpgid(process.pid), signal.SIGTERM)
415
+ process.wait(timeout=5)
416
+ self.running_processes.pop(process_id, None)
417
+ return True
418
+ except:
419
+ process.kill()
420
+ self.running_processes.pop(process_id, None)
421
+ return True
422
+ return False
423
+
424
+ # File operations
425
+ class FileManager:
426
+ """Secure file operations"""
427
+
428
+ def __init__(self, session_manager: SessionManager):
429
+ self.session_manager = session_manager
430
+
431
+ def list_directory(self, path: str, session_id: str) -> Dict[str, Any]:
432
+ """List directory contents"""
433
+ try:
434
+ # Validate path
435
+ if not path or path == ".":
436
+ path = self.session_manager.get_current_directory(session_id)
437
+
438
+ directory = Path(path)
439
+ if not directory.exists():
440
+ return {
441
+ "success": False,
442
+ "error": "Directory not found",
443
+ "files": [],
444
+ "timestamp": datetime.now(timezone.utc).isoformat()
445
+ }
446
+
447
+ if not directory.is_dir():
448
+ return {
449
+ "success": False,
450
+ "error": "Not a directory",
451
+ "files": [],
452
+ "timestamp": datetime.now(timezone.utc).isoformat()
453
+ }
454
+
455
+ files = []
456
+ for item in directory.iterdir():
457
+ try:
458
+ stat = item.stat()
459
+ files.append({
460
+ "name": item.name,
461
+ "type": "directory" if item.is_dir() else "file",
462
+ "size": stat.st_size if item.is_file() else 0,
463
+ "modified": datetime.fromtimestamp(stat.st_mtime, timezone.utc).isoformat(),
464
+ "permissions": oct(stat.st_mode)[-3:]
465
+ })
466
+ except (PermissionError, OSError) as e:
467
+ logger.warning(f"Error accessing file {item.name}: {e}")
468
+ continue
469
+
470
+ logger.info(f"Directory listed - Session: {session_id}, Path: {path}, Items: {len(files)}")
471
+
472
+ return {
473
+ "success": True,
474
+ "files": files,
475
+ "path": str(path),
476
+ "timestamp": datetime.now(timezone.utc).isoformat()
477
+ }
478
+
479
+ except Exception as e:
480
+ logger.error(f"Directory listing error - Session: {session_id}, Path: {path}, Error: {e}")
481
+ return {
482
+ "success": False,
483
+ "error": f"Directory listing failed: {str(e)}",
484
+ "files": [],
485
+ "timestamp": datetime.now(timezone.utc).isoformat()
486
+ }
487
+
488
+ def read_file(self, filename: str, session_id: str) -> Dict[str, Any]:
489
+ """Read file content"""
490
+ try:
491
+ # Validate filename
492
+ is_valid, validation_message = SecurityValidator.validate_filename(filename)
493
+ if not is_valid:
494
+ return {
495
+ "success": False,
496
+ "error": f"Invalid filename: {validation_message}",
497
+ "content": "",
498
+ "timestamp": datetime.now(timezone.utc).isoformat()
499
+ }
500
+
501
+ # Get file path
502
+ current_dir = self.session_manager.get_current_directory(session_id)
503
+ file_path = Path(current_dir) / filename
504
+
505
+ if not file_path.exists():
506
+ return {
507
+ "success": False,
508
+ "error": "File not found",
509
+ "content": "",
510
+ "timestamp": datetime.now(timezone.utc).isoformat()
511
+ }
512
+
513
+ if not file_path.is_file():
514
+ return {
515
+ "success": False,
516
+ "error": "Not a file",
517
+ "content": "",
518
+ "timestamp": datetime.now(timezone.utc).isoformat()
519
+ }
520
+
521
+ # Check file size
522
+ if file_path.stat().st_size > config.MAX_FILE_SIZE:
523
+ return {
524
+ "success": False,
525
+ "error": f"File too large (max {config.MAX_FILE_SIZE} bytes)",
526
+ "content": "",
527
+ "timestamp": datetime.now(timezone.utc).isoformat()
528
+ }
529
+
530
+ # Read file
531
+ content = file_path.read_text(encoding='utf-8')
532
+
533
+ logger.info(f"File read - Session: {session_id}, File: {filename}, Size: {len(content)}")
534
+
535
+ return {
536
+ "success": True,
537
+ "content": content,
538
+ "size": file_path.stat().st_size,
539
+ "filename": filename,
540
+ "timestamp": datetime.now(timezone.utc).isoformat()
541
+ }
542
+
543
+ except Exception as e:
544
+ logger.error(f"File read error - Session: {session_id}, File: {filename}, Error: {e}")
545
+ return {
546
+ "success": False,
547
+ "error": f"File read failed: {str(e)}",
548
+ "content": "",
549
+ "timestamp": datetime.now(timezone.utc).isoformat()
550
+ }
551
+
552
+ def create_file(self, filename: str, content: str, session_id: str) -> Dict[str, Any]:
553
+ """Create a new file"""
554
+ try:
555
+ # Validate filename
556
+ is_valid, validation_message = SecurityValidator.validate_filename(filename)
557
+ if not is_valid:
558
+ return {
559
+ "success": False,
560
+ "error": f"Invalid filename: {validation_message}",
561
+ "timestamp": datetime.now(timezone.utc).isoformat()
562
+ }
563
+
564
+ # Validate content
565
+ is_valid, validation_message = SecurityValidator.validate_file_content(content)
566
+ if not is_valid:
567
+ return {
568
+ "success": False,
569
+ "error": f"Invalid content: {validation_message}",
570
+ "timestamp": datetime.now(timezone.utc).isoformat()
571
+ }
572
+
573
+ # Get file path
574
+ current_dir = self.session_manager.get_current_directory(session_id)
575
+ file_path = Path(current_dir) / filename
576
+
577
+ # Check if file already exists
578
+ if file_path.exists():
579
+ return {
580
+ "success": False,
581
+ "error": "File already exists",
582
+ "timestamp": datetime.now(timezone.utc).isoformat()
583
+ }
584
+
585
+ # Create file
586
+ file_path.write_text(content, encoding='utf-8')
587
+
588
+ # Update session stats
589
+ session = self.session_manager.get_session(session_id)
590
+ if session:
591
+ session["files_created"] += 1
592
+
593
+ logger.info(f"File created - Session: {session_id}, File: {filename}, Size: {len(content)}")
594
+
595
+ return {
596
+ "success": True,
597
+ "filename": filename,
598
+ "size": len(content.encode('utf-8')),
599
+ "timestamp": datetime.now(timezone.utc).isoformat()
600
+ }
601
+
602
+ except Exception as e:
603
+ logger.error(f"File creation error - Session: {session_id}, File: {filename}, Error: {e}")
604
+ return {
605
+ "success": False,
606
+ "error": f"File creation failed: {str(e)}",
607
+ "timestamp": datetime.now(timezone.utc).isoformat()
608
+ }
609
+
610
+ # System monitoring
611
+ class SystemMonitor:
612
+ """Monitor system resources and health"""
613
+
614
+ @staticmethod
615
+ def get_system_info() -> Dict[str, Any]:
616
+ """Get comprehensive system information"""
617
+ try:
618
+ # System info
619
+ info = {
620
+ "system": {
621
+ "platform": sys.platform,
622
+ "python_version": sys.version,
623
+ "hostname": os.uname().nodename if hasattr(os, 'uname') else "unknown",
624
+ "uptime": time.time() - psutil.boot_time()
625
+ },
626
+ "resources": {
627
+ "cpu_count": psutil.cpu_count(),
628
+ "cpu_usage": psutil.cpu_percent(interval=1),
629
+ "memory": {
630
+ "total": psutil.virtual_memory().total,
631
+ "available": psutil.virtual_memory().available,
632
+ "used": psutil.virtual_memory().used,
633
+ "percent": psutil.virtual_memory().percent
634
+ },
635
+ "disk": {
636
+ "total": psutil.disk_usage('/').total,
637
+ "used": psutil.disk_usage('/').used,
638
+ "free": psutil.disk_usage('/').free,
639
+ "percent": (psutil.disk_usage('/').used / psutil.disk_usage('/').total) * 100
640
+ }
641
+ },
642
+ "environment": {
643
+ "workspace": config.WORKSPACE_PATH,
644
+ "user": os.getenv("USER", "unknown"),
645
+ "python_path": sys.executable,
646
+ "cwd": os.getcwd()
647
+ },
648
+ "sandbox_features": {
649
+ "security": "Command validation and sandboxing",
650
+ "resource_limits": f"Max {config.MAX_MEMORY_MB}MB RAM, {config.MAX_DISK_GB}GB disk",
651
+ "timeout": f"{config.COMMAND_TIMEOUT}s command timeout",
652
+ "restricted_commands": len(config.RESTRICTED_COMMANDS)
653
+ },
654
+ "timestamp": datetime.now(timezone.utc).isoformat()
655
+ }
656
+
657
+ return {
658
+ "success": True,
659
+ "info": info
660
+ }
661
+
662
+ except Exception as e:
663
+ logger.error(f"System info error: {e}")
664
+ return {
665
+ "success": False,
666
+ "error": str(e),
667
+ "timestamp": datetime.now(timezone.utc).isoformat()
668
+ }
669
+
670
+ # Initialize managers
671
+ session_manager = SessionManager()
672
+ executor = SecureExecutor(session_manager)
673
+ file_manager = FileManager(session_manager)
674
+ system_monitor = SystemMonitor()
675
+
676
+ # Create default session
677
+ default_session_id = session_manager.create_session()
678
+
679
+ # API endpoints for AI integration
680
+ class APIEndpoints:
681
+ """REST API endpoints for AI model integration"""
682
+
683
+ @staticmethod
684
+ def execute_command(command: str, session_id: Optional[str] = None) -> Dict[str, Any]:
685
+ """Execute a command via API"""
686
+ if not session_id:
687
+ session_id = default_session_id
688
+
689
+ return executor.execute_command(command, session_id)
690
+
691
+ @staticmethod
692
+ def create_file(filename: str, content: str, session_id: Optional[str] = None) -> Dict[str, Any]:
693
+ """Create a file via API"""
694
+ if not session_id:
695
+ session_id = default_session_id
696
+
697
+ return file_manager.create_file(filename, content, session_id)
698
+
699
+ @staticmethod
700
+ def read_file(filename: str, session_id: Optional[str] = None) -> Dict[str, Any]:
701
+ """Read a file via API"""
702
+ if not session_id:
703
+ session_id = default_session_id
704
+
705
+ return file_manager.read_file(filename, session_id)
706
+
707
+ @staticmethod
708
+ def list_directory(path: str = ".", session_id: Optional[str] = None) -> Dict[str, Any]:
709
+ """List directory via API"""
710
+ if not session_id:
711
+ session_id = default_session_id
712
+
713
+ return file_manager.list_directory(path, session_id)
714
+
715
+ @staticmethod
716
+ def get_system_info() -> Dict[str, Any]:
717
+ """Get system information via API"""
718
+ return system_monitor.get_system_info()
719
+
720
+ @staticmethod
721
+ def create_session() -> Dict[str, Any]:
722
+ """Create a new session"""
723
+ session_id = session_manager.create_session()
724
+ return {
725
+ "success": True,
726
+ "session_id": session_id,
727
+ "timestamp": datetime.now(timezone.utc).isoformat()
728
+ }
729
+
730
+ @staticmethod
731
+ def get_session_info(session_id: str) -> Dict[str, Any]:
732
+ """Get session information"""
733
+ session = session_manager.get_session(session_id)
734
+ if session:
735
+ return {
736
+ "success": True,
737
+ "session": session,
738
+ "timestamp": datetime.now(timezone.utc).isoformat()
739
+ }
740
+ else:
741
+ return {
742
+ "success": False,
743
+ "error": "Session not found",
744
+ "timestamp": datetime.now(timezone.utc).isoformat()
745
+ }
746
+
747
+ # Gradio Interface
748
+ def create_interface():
749
+ """Create the main Gradio interface"""
750
+
751
+ # Custom CSS for professional styling
752
+ css = """
753
+ .ubuntu-terminal {
754
+ background-color: #1e1e1e;
755
+ color: #d4d4d4;
756
+ font-family: 'Courier New', monospace;
757
+ border-radius: 8px;
758
+ padding: 20px;
759
+ min-height: 500px;
760
+ font-size: 14px;
761
+ line-height: 1.4;
762
+ }
763
+
764
+ .header {
765
+ text-align: center;
766
+ padding: 25px;
767
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
768
+ color: white;
769
+ border-radius: 12px;
770
+ margin-bottom: 25px;
771
+ box-shadow: 0 4px 15px rgba(0,0,0,0.2);
772
+ }
773
+
774
+ .feature-box {
775
+ background: #f8f9fa;
776
+ border: 1px solid #e9ecef;
777
+ border-radius: 8px;
778
+ padding: 15px;
779
+ margin: 10px 0;
780
+ transition: all 0.3s ease;
781
+ }
782
+
783
+ .feature-box:hover {
784
+ border-color: #667eea;
785
+ box-shadow: 0 2px 8px rgba(102, 126, 234, 0.15);
786
+ }
787
+
788
+ .api-info {
789
+ background: #e3f2fd;
790
+ border: 1px solid #2196f3;
791
+ border-radius: 8px;
792
+ padding: 20px;
793
+ margin: 15px 0;
794
+ }
795
+
796
+ .status-indicator {
797
+ display: inline-block;
798
+ width: 10px;
799
+ height: 10px;
800
+ border-radius: 50%;
801
+ margin-right: 8px;
802
+ }
803
+
804
+ .status-active { background-color: #4caf50; }
805
+ .status-warning { background-color: #ff9800; }
806
+ .status-error { background-color: #f44336; }
807
+ """
808
+
809
+ with gr.Blocks(css=css, title="Ubuntu Sandbox Environment v2.0", theme=gr.themes.Soft()) as app:
810
+
811
+ # Header
812
+ gr.HTML("""
813
+ <div class="header">
814
+ <h1>🖥️ Ubuntu Sandbox Environment v2.0</h1>
815
+ <p><strong>Production-Grade, Secure, AI-Accessible Development Environment</strong></p>
816
+ <p>Perfect for AI models to build, ship, test, and deploy applications with complete security</p>
817
+ </div>
818
+ """)
819
+
820
+ # Status bar
821
+ with gr.Row():
822
+ with gr.Column(scale=4):
823
+ gr.HTML('<div id="status-bar"><span class="status-indicator status-active"></span>System Online</div>')
824
+ with gr.Column(scale=1):
825
+ session_info = gr.Textbox(
826
+ label="Session ID",
827
+ value=default_session_id,
828
+ interactive=False,
829
+ elem_id="session-info"
830
+ )
831
+
832
+ # Main terminal interface
833
+ with gr.Tab("💻 Terminal"):
834
+ with gr.Row():
835
+ with gr.Column(scale=3):
836
+ command_input = gr.Textbox(
837
+ label="Execute Command",
838
+ placeholder="Enter any Ubuntu command (e.g., ls, pwd, python3 --version, git clone, docker build...)",
839
+ lines=1,
840
+ elem_id="command-input"
841
+ )
842
+ execute_btn = gr.Button("🚀 Execute", variant="primary", size="lg")
843
+
844
+ terminal_output = gr.Textbox(
845
+ label="Terminal Output",
846
+ lines=25,
847
+ max_lines=1000,
848
+ info="Real-time output from executed commands",
849
+ elem_classes=["ubuntu-terminal"],
850
+ elem_id="terminal-output"
851
+ )
852
+
853
+ with gr.Row():
854
+ clear_btn = gr.Button("🗑️ Clear Output", variant="secondary")
855
+ new_session_btn = gr.Button("🆕 New Session", variant="secondary")
856
+ help_btn = gr.Button("❓ Help", variant="secondary")
857
+
858
+ # File management
859
+ with gr.Tab("📁 Files"):
860
+ with gr.Row():
861
+ with gr.Column(scale=1):
862
+ gr.HTML("<h3>📄 File Operations</h3>")
863
+
864
+ with gr.Tab("Create File"):
865
+ create_filename = gr.Textbox(label="Filename", placeholder="example.py", elem_id="create-filename")
866
+ create_content = gr.Textbox(
867
+ label="Content",
868
+ lines=10,
869
+ placeholder="print('Hello from Ubuntu Sandbox!')",
870
+ elem_id="create-content"
871
+ )
872
+ create_btn = gr.Button("📝 Create File", variant="primary")
873
+ create_output = gr.Textbox(label="Result", lines=5, elem_id="create-output")
874
+
875
+ with gr.Tab("Read File"):
876
+ read_filename = gr.Textbox(label="Filename", placeholder="example.py", elem_id="read-filename")
877
+ read_btn = gr.Button("📖 Read File", variant="primary")
878
+ read_output = gr.Textbox(label="Content", lines=15, elem_id="read-output")
879
+
880
+ with gr.Column(scale=1):
881
+ gr.HTML("<h3>📂 Directory Browser</h3>")
882
+ current_path = gr.Textbox(
883
+ label="Current Path",
884
+ value="/workspace",
885
+ interactive=False,
886
+ elem_id="current-path"
887
+ )
888
+ refresh_btn = gr.Button("🔄 Refresh", variant="secondary")
889
+
890
+ files_dataframe = gr.DataFrame(
891
+ headers=["Name", "Type", "Size", "Modified", "Permissions"],
892
+ datatype=["str", "str", "number", "str", "str"],
893
+ label="Directory Contents",
894
+ elem_id="files-df"
895
+ )
896
+
897
+ path_input = gr.Textbox(
898
+ label="Navigate to Path",
899
+ placeholder="/workspace/your-folder",
900
+ elem_id="path-input"
901
+ )
902
+ navigate_btn = gr.Button("🗂️ Navigate", variant="secondary")
903
+
904
+ # System monitoring
905
+ with gr.Tab("📊 System"):
906
+ with gr.Row():
907
+ with gr.Column(scale=1):
908
+ gr.HTML("<h3>🖥️ System Information</h3>")
909
+ info_btn = gr.Button("📈 Update Info", variant="primary")
910
+ system_json = gr.JSON(label="System Details", elem_id="system-json")
911
+
912
+ with gr.Column(scale=1):
913
+ gr.HTML("<h3>⚡ Quick Actions</h3>")
914
+
915
+ quick_commands = [
916
+ ("🐍 Python Version", "python3 --version"),
917
+ ("📦 Node.js Version", "node --version"),
918
+ ("🐙 Git Version", "git --version"),
919
+ ("💾 Memory Info", "free -h"),
920
+ ("💿 Disk Usage", "df -h"),
921
+ ("🖥️ CPU Info", "lscpu | head -5"),
922
+ ("🔍 Network Test", "ping -c 1 8.8.8.8"),
923
+ ("📋 Process List", "ps aux --sort=-%mem | head -10")
924
+ ]
925
+
926
+ for label, command in quick_commands:
927
+ gr.Button(
928
+ label,
929
+ variant="secondary"
930
+ ).click(
931
+ fn=lambda cmd=command: executor.execute_command(cmd, default_session_id)["output"],
932
+ inputs=None,
933
+ outputs=terminal_output
934
+ )
935
+
936
+ # API documentation
937
+ with gr.Tab("🔌 API"):
938
+ gr.HTML("""
939
+ <div class="api-info">
940
+ <h3>🤖 AI Model Integration API</h3>
941
+ <p>All endpoints support JSON requests and return JSON responses with timestamps and status information.</p>
942
+
943
+ <h4>Base URL: <code>/api/v1/</code></h4>
944
+
945
+ <h5>Execute Command</h5>
946
+ <p><strong>POST</strong> <code>/api/v1/execute</code></p>
947
+ <pre><code>{
948
+ "command": "ls -la",
949
+ "session_id": "optional-session-id"
950
+ }</code></pre>
951
+
952
+ <h5>Create File</h5>
953
+ <p><strong>POST</strong> <code>/api/v1/create-file</code></p>
954
+ <pre><code>{
955
+ "filename": "test.py",
956
+ "content": "print('Hello World!')",
957
+ "session_id": "optional-session-id"
958
+ }</code></pre>
959
+
960
+ <h5>Read File</h5>
961
+ <p><strong>POST</strong> <code>/api/v1/read-file</code></p>
962
+ <pre><code>{
963
+ "filename": "test.py",
964
+ "session_id": "optional-session-id"
965
+ }</code></pre>
966
+
967
+ <h5>List Directory</h5>
968
+ <p><strong>POST</strong> <code>/api/v1/list-directory</code></p>
969
+ <pre><code>{
970
+ "path": "/workspace",
971
+ "session_id": "optional-session-id"
972
+ }</code></pre>
973
+
974
+ <h5>Get System Info</h5>
975
+ <p><strong>GET</strong> <code>/api/v1/system-info</code></p>
976
+
977
+ <h5>Create Session</h5>
978
+ <p><strong>POST</strong> <code>/api/v1/create-session</code></p>
979
+
980
+ <h4>🔒 Security Features</h4>
981
+ <ul>
982
+ <li>Command validation and sanitization</li>
983
+ <li>Resource limits (memory, CPU, timeout)</li>
984
+ <li>Restricted command list</li>
985
+ <li>Session management with timeouts</li>
986
+ <li>File system security checks</li>
987
+ <li>Comprehensive logging</li>
988
+ </ul>
989
+ </div>
990
+ """)
991
+
992
+ # Features showcase
993
+ with gr.Tab("✨ Features"):
994
+ gr.HTML("""
995
+ <div class="feature-box">
996
+ <h3>🛡️ Enterprise Security</h3>
997
+ <ul>
998
+ <li>Command validation and sandboxing</li>
999
+ <li>Resource limits and monitoring</li>
1000
+ <li>Session isolation and timeout</li>
1001
+ <li>Restricted command list</li>
1002
+ <li>Comprehensive audit logging</li>
1003
+ </ul>
1004
+ </div>
1005
+
1006
+ <div class="feature-box">
1007
+ <h3>🤖 AI-First Design</h3>
1008
+ <ul>
1009
+ <li>RESTful API for programmatic access</li>
1010
+ <li>Session management for stateful interactions</li>
1011
+ <li>Real-time command execution</li>
1012
+ <li>Structured JSON responses</li>
1013
+ <li>Error handling and validation</li>
1014
+ </ul>
1015
+ </div>
1016
+
1017
+ <div class="feature-box">
1018
+ <h3>🛠️ Development Ready</h3>
1019
+ <ul>
1020
+ <li>Pre-installed development tools</li>
1021
+ <li>Git version control</li>
1022
+ <li>Docker and container support</li>
1023
+ <li>Multiple programming languages</li>
1024
+ <li>Cloud CLI tools</li>
1025
+ </ul>
1026
+ </div>
1027
+
1028
+ <div class="feature-box">
1029
+ <h3>📊 Monitoring & Observability</h3>
1030
+ <ul>
1031
+ <li>Real-time system monitoring</li>
1032
+ <li>Resource usage tracking</li>
1033
+ <li>Command execution logging</li>
1034
+ <li>Session analytics</li>
1035
+ <li>Health check endpoints</li>
1036
+ </ul>
1037
+ </div>
1038
+ """)
1039
+
1040
+ # Event handlers
1041
+ def execute_command_ui(command):
1042
+ if not command or not command.strip():
1043
+ return "Please enter a command"
1044
+
1045
+ result = executor.execute_command(command.strip(), default_session_id)
1046
+ if result["success"]:
1047
+ return f"✅ Command executed successfully\n\n{result['output']}"
1048
+ else:
1049
+ return f"❌ Command failed: {result['error']}\n\nOutput: {result.get('output', 'No output')}"
1050
+
1051
+ def create_file_ui(filename, content):
1052
+ if not filename or not content:
1053
+ return "Please provide both filename and content"
1054
+
1055
+ result = file_manager.create_file(filename, content, default_session_id)
1056
+ if result["success"]:
1057
+ return f"✅ File created successfully: {filename} ({result['size']} bytes)"
1058
+ else:
1059
+ return f"❌ File creation failed: {result['error']}"
1060
+
1061
+ def read_file_ui(filename):
1062
+ if not filename:
1063
+ return "Please provide a filename"
1064
+
1065
+ result = file_manager.read_file(filename, default_session_id)
1066
+ if result["success"]:
1067
+ return f"📄 {filename} ({result['size']} bytes):\n\n{result['content']}"
1068
+ else:
1069
+ return f"❌ File read failed: {result['error']}"
1070
+
1071
+ def list_directory_ui(path="/workspace"):
1072
+ result = file_manager.list_directory(path, default_session_id)
1073
+ if result["success"]:
1074
+ files = result["files"]
1075
+ if files:
1076
+ data = [[f["name"], f["type"], f["size"], f["modified"], f["permissions"]] for f in files]
1077
+ return data, result["path"]
1078
+ else:
1079
+ return [], result["path"]
1080
+ else:
1081
+ return [], path
1082
+
1083
+ def show_help():
1084
+ return get_help_text()
1085
+
1086
+ def clear_output():
1087
+ return ""
1088
+
1089
+ def new_session_ui():
1090
+ new_id = session_manager.create_session()
1091
+ return new_id
1092
+
1093
+ # Connect event handlers
1094
+ execute_btn.click(
1095
+ fn=execute_command_ui,
1096
+ inputs=command_input,
1097
+ outputs=terminal_output
1098
+ )
1099
+
1100
+ command_input.submit(
1101
+ fn=execute_command_ui,
1102
+ inputs=command_input,
1103
+ outputs=terminal_output
1104
+ )
1105
+
1106
+ create_btn.click(
1107
+ fn=create_file_ui,
1108
+ inputs=[create_filename, create_content],
1109
+ outputs=create_output
1110
+ )
1111
+
1112
+ read_btn.click(
1113
+ fn=read_file_ui,
1114
+ inputs=read_filename,
1115
+ outputs=read_output
1116
+ )
1117
+
1118
+ refresh_btn.click(
1119
+ fn=list_directory_ui,
1120
+ inputs=None,
1121
+ outputs=[files_dataframe, current_path]
1122
+ )
1123
+
1124
+ navigate_btn.click(
1125
+ fn=list_directory_ui,
1126
+ inputs=path_input,
1127
+ outputs=[files_dataframe, current_path]
1128
+ )
1129
+
1130
+ info_btn.click(
1131
+ fn=lambda: system_monitor.get_system_info()["info"],
1132
+ inputs=None,
1133
+ outputs=system_json
1134
+ )
1135
+
1136
+ help_btn.click(
1137
+ fn=show_help,
1138
+ inputs=None,
1139
+ outputs=terminal_output
1140
+ )
1141
+
1142
+ clear_btn.click(
1143
+ fn=clear_output,
1144
+ inputs=None,
1145
+ outputs=terminal_output
1146
+ )
1147
+
1148
+ new_session_btn.click(
1149
+ fn=new_session_ui,
1150
+ inputs=None,
1151
+ outputs=session_info
1152
+ )
1153
+
1154
+ # Initialize with system info
1155
+ system_json.update(
1156
+ system_monitor.get_system_info()["info"]
1157
+ )
1158
+
1159
+ # Initialize file list
1160
+ files_dataframe.update(
1161
+ list_directory_ui()[0]
1162
+ )
1163
+
1164
+ return app
1165
+
1166
+ def get_help_text():
1167
+ """Get comprehensive help text"""
1168
+ return """
1169
+ 🔧 Ubuntu Sandbox Environment v2.0 - Help
1170
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1171
+
1172
+ 📋 Available Commands:
1173
+ • help - Show this help text
1174
+ • pwd - Show current working directory
1175
+ • ls - List files and directories
1176
+ • cd <dir> - Change directory
1177
+ • cat <file> - Display file content
1178
+ • touch <file> - Create empty file
1179
+ • history - Show command history
1180
+ • new_session - Start new terminal session
1181
+
1182
+ 🤖 AI Model Integration:
1183
+ • Use REST API endpoints for programmatic access
1184
+ • All commands are validated and sandboxed
1185
+ • Session management for stateful interactions
1186
+ • Structured JSON responses with error handling
1187
+ • Rate limiting and resource monitoring
1188
+
1189
+ 🛠️ Development Tools:
1190
+ • Python 3.x with scientific libraries
1191
+ • Node.js and npm for JavaScript development
1192
+ • Git for version control
1193
+ • Docker for containerization
1194
+ • Cloud CLI tools (AWS, GCP, Azure)
1195
+ • Text editors (vim, nano)
1196
+
1197
+ 🛡️ Security Features:
1198
+ • Command validation and sanitization
1199
+ • Resource limits (CPU, memory, disk)
1200
+ • 30-second command timeout
1201
+ • Restricted command list
1202
+ • Session isolation and cleanup
1203
+ • Comprehensive audit logging
1204
+
1205
+ 📊 System Monitoring:
1206
+ • Real-time resource usage
1207
+ • Command execution statistics
1208
+ • Session management
1209
+ • Health monitoring
1210
+ • Performance metrics
1211
+
1212
+ 💡 Tips:
1213
+ • Use 'system_info' command for detailed system information
1214
+ • All file operations are validated for security
1215
+ • Sessions automatically clean up after inactivity
1216
+ • Check logs in /workspace/logs/ for debugging
1217
+ • Use the API for AI model integration
1218
+
1219
+ ━━━━━��━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1220
+ Ready for AI agents to build, ship, and create anything! 🚀
1221
+ """
1222
+
1223
+ # Custom Flask routes
1224
+ def create_custom_routes(app):
1225
+ """Add custom API routes to the Gradio app"""
1226
+
1227
+ from flask import Flask, request, jsonify
1228
+
1229
+ @app.route("/api/v1/execute", methods=["POST"])
1230
+ def execute_endpoint():
1231
+ """Execute command API endpoint"""
1232
+ try:
1233
+ data = request.get_json()
1234
+ if not data or "command" not in data:
1235
+ return jsonify({
1236
+ "success": False,
1237
+ "error": "Missing 'command' in request body",
1238
+ "timestamp": datetime.now(timezone.utc).isoformat()
1239
+ }), 400
1240
+
1241
+ command = data["command"]
1242
+ session_id = data.get("session_id", default_session_id)
1243
+
1244
+ result = executor.execute_command(command, session_id)
1245
+ return jsonify(result)
1246
+
1247
+ except Exception as e:
1248
+ logger.error(f"API execute error: {e}")
1249
+ return jsonify({
1250
+ "success": False,
1251
+ "error": f"Internal server error: {str(e)}",
1252
+ "timestamp": datetime.now(timezone.utc).isoformat()
1253
+ }), 500
1254
+
1255
+ @app.route("/api/v1/create-file", methods=["POST"])
1256
+ def create_file_endpoint():
1257
+ """Create file API endpoint"""
1258
+ try:
1259
+ data = request.get_json()
1260
+ if not data or "filename" not in data or "content" not in data:
1261
+ return jsonify({
1262
+ "success": False,
1263
+ "error": "Missing 'filename' or 'content' in request body",
1264
+ "timestamp": datetime.now(timezone.utc).isoformat()
1265
+ }), 400
1266
+
1267
+ filename = data["filename"]
1268
+ content = data["content"]
1269
+ session_id = data.get("session_id", default_session_id)
1270
+
1271
+ result = file_manager.create_file(filename, content, session_id)
1272
+ return jsonify(result)
1273
+
1274
+ except Exception as e:
1275
+ logger.error(f"API create file error: {e}")
1276
+ return jsonify({
1277
+ "success": False,
1278
+ "error": f"Internal server error: {str(e)}",
1279
+ "timestamp": datetime.now(timezone.utc).isoformat()
1280
+ }), 500
1281
+
1282
+ @app.route("/api/v1/read-file", methods=["POST"])
1283
+ def read_file_endpoint():
1284
+ """Read file API endpoint"""
1285
+ try:
1286
+ data = request.get_json()
1287
+ if not data or "filename" not in data:
1288
+ return jsonify({
1289
+ "success": False,
1290
+ "error": "Missing 'filename' in request body",
1291
+ "timestamp": datetime.now(timezone.utc).isoformat()
1292
+ }), 400
1293
+
1294
+ filename = data["filename"]
1295
+ session_id = data.get("session_id", default_session_id)
1296
+
1297
+ result = file_manager.read_file(filename, session_id)
1298
+ return jsonify(result)
1299
+
1300
+ except Exception as e:
1301
+ logger.error(f"API read file error: {e}")
1302
+ return jsonify({
1303
+ "success": False,
1304
+ "error": f"Internal server error: {str(e)}",
1305
+ "timestamp": datetime.now(timezone.utc).isoformat()
1306
+ }), 500
1307
+
1308
+ @app.route("/api/v1/list-directory", methods=["POST"])
1309
+ def list_directory_endpoint():
1310
+ """List directory API endpoint"""
1311
+ try:
1312
+ data = request.get_json() or {}
1313
+ path = data.get("path", "/workspace")
1314
+ session_id = data.get("session_id", default_session_id)
1315
+
1316
+ result = file_manager.list_directory(path, session_id)
1317
+ return jsonify(result)
1318
+
1319
+ except Exception as e:
1320
+ logger.error(f"API list directory error: {e}")
1321
+ return jsonify({
1322
+ "success": False,
1323
+ "error": f"Internal server error: {str(e)}",
1324
+ "timestamp": datetime.now(timezone.utc).isoformat()
1325
+ }), 500
1326
+
1327
+ @app.route("/api/v1/system-info", methods=["GET"])
1328
+ def system_info_endpoint():
1329
+ """System info API endpoint"""
1330
+ try:
1331
+ result = system_monitor.get_system_info()
1332
+ return jsonify(result)
1333
+
1334
+ except Exception as e:
1335
+ logger.error(f"API system info error: {e}")
1336
+ return jsonify({
1337
+ "success": False,
1338
+ "error": f"Internal server error: {str(e)}",
1339
+ "timestamp": datetime.now(timezone.utc).isoformat()
1340
+ }), 500
1341
+
1342
+ @app.route("/api/v1/create-session", methods=["POST"])
1343
+ def create_session_endpoint():
1344
+ """Create session API endpoint"""
1345
+ try:
1346
+ result = APIEndpoints.create_session()
1347
+ return jsonify(result)
1348
+
1349
+ except Exception as e:
1350
+ logger.error(f"API create session error: {e}")
1351
+ return jsonify({
1352
+ "success": False,
1353
+ "error": f"Internal server error: {str(e)}",
1354
+ "timestamp": datetime.now(timezone.utc).isoformat()
1355
+ }), 500
1356
+
1357
+ @app.route("/api/v1/session-info/<session_id>", methods=["GET"])
1358
+ def session_info_endpoint(session_id):
1359
+ """Get session info API endpoint"""
1360
+ try:
1361
+ result = APIEndpoints.get_session_info(session_id)
1362
+ return jsonify(result)
1363
+
1364
+ except Exception as e:
1365
+ logger.error(f"API session info error: {e}")
1366
+ return jsonify({
1367
+ "success": False,
1368
+ "error": f"Internal server error: {str(e)}",
1369
+ "timestamp": datetime.now(timezone.utc).isoformat()
1370
+ }), 500
1371
+
1372
+ @app.route("/health", methods=["GET"])
1373
+ def health_endpoint():
1374
+ """Health check endpoint"""
1375
+ return jsonify({
1376
+ "status": "healthy",
1377
+ "version": __version__,
1378
+ "timestamp": datetime.now(timezone.utc).isoformat()
1379
+ })
1380
+
1381
+ # Cleanup function
1382
+ def cleanup_on_exit():
1383
+ """Cleanup on application exit"""
1384
+ logger.info("Starting application cleanup...")
1385
+
1386
+ # Kill any running processes
1387
+ for process_id in list(executor.running_processes.keys()):
1388
+ executor.kill_process(process_id)
1389
+
1390
+ # Cleanup expired sessions
1391
+ session_manager.cleanup_expired_sessions()
1392
+
1393
+ logger.info("Application cleanup completed")
1394
+
1395
+ # Main application
1396
+ if __name__ == "__main__":
1397
+ try:
1398
+ # Create workspace structure
1399
+ os.makedirs(config.WORKSPACE_PATH, exist_ok=True)
1400
+ os.makedirs(config.LOGS_PATH, exist_ok=True)
1401
+
1402
+ # Setup signal handlers
1403
+ def signal_handler(sig, frame):
1404
+ logger.info("Received shutdown signal")
1405
+ cleanup_on_exit()
1406
+ sys.exit(0)
1407
+
1408
+ signal.signal(signal.SIGINT, signal_handler)
1409
+ signal.signal(signal.SIGTERM, signal_handler)
1410
+
1411
+ # Log startup
1412
+ logger.info(f"Starting Ubuntu Sandbox Environment v{__version__}")
1413
+ logger.info(f"Workspace: {config.WORKSPACE_PATH}")
1414
+ logger.info(f"Default session: {default_session_id}")
1415
+
1416
+ # Create the interface
1417
+ app = create_interface()
1418
+
1419
+ # Add custom API routes
1420
+ create_custom_routes(app)
1421
+
1422
+ # Launch the app
1423
+ logger.info("Launching Gradio application...")
1424
+ app.launch(
1425
+ server_name="0.0.0.0",
1426
+ server_port=7860,
1427
+ share=True,
1428
+ debug=False, # Set to False for production
1429
+ show_error=True,
1430
+ favicon_path=None,
1431
+ height=800,
1432
+ title="Ubuntu Sandbox Environment v2.0"
1433
+ )
1434
+
1435
+ except Exception as e:
1436
+ logger.error(f"Application startup error: {e}")
1437
+ sys.exit(1)
1438
+
1439
+ finally:
1440
+ cleanup_on_exit()