likhonsheikh commited on
Commit
da5b525
Β·
verified Β·
1 Parent(s): 85719b3

πŸš€ MINIMAL BUILD FIX: Simplified app with core functionality only

Browse files
Files changed (1) hide show
  1. app.py +123 -1401
app.py CHANGED
@@ -1,1440 +1,162 @@
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()
 
1
  #!/usr/bin/env python3
2
  """
3
+ Simplified Ubuntu Sandbox - Core Functionality Only
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  """
5
 
6
+ import gradio as gr
 
7
  import subprocess
8
+ import os
 
 
9
  import json
10
+ from typing import Dict, List, Any
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  import psutil
12
+ import time
13
+ from datetime import datetime
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
+ # Initialize workspace
16
+ WORKSPACE_PATH = "/home/user/workspace"
17
+ os.makedirs(WORKSPACE_PATH, exist_ok=True)
18
 
19
+ class SimpleSandbox:
 
 
 
20
  def __init__(self):
21
+ self.sessions = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ def execute_command(self, command: str, session_id: str = "default") -> str:
24
+ """Execute command safely"""
25
+ if not command.strip():
26
+ return "Empty command"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  try:
29
+ # Simple command execution
30
+ result = subprocess.run(
31
+ command,
32
+ shell=True,
33
+ capture_output=True,
34
+ text=True,
35
+ timeout=10,
36
+ cwd=WORKSPACE_PATH
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  )
38
 
39
+ output = f"Command: {command}\n"
40
+ output += f"Exit Code: {result.returncode}\n"
41
+ if result.stdout:
42
+ output += f"Output:\n{result.stdout}"
43
+ if result.stderr:
44
+ output += f"Error:\n{result.stderr}"
45
+ output += f"\nTimestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
46
 
47
+ return output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ except subprocess.TimeoutExpired:
50
+ return f"Command timed out: {command}"
 
 
 
 
 
51
  except Exception as e:
52
+ return f"Error executing command: {str(e)}"
 
 
 
 
 
 
53
 
54
+ def get_system_info(self) -> str:
55
+ """Get system information"""
56
  try:
57
+ info = {
58
+ "CPU": f"{psutil.cpu_count()} cores",
59
+ "Memory": f"{psutil.virtual_memory().total / (1024**3):.1f} GB",
60
+ "Disk": f"{psutil.disk_usage('/').free / (1024**3):.1f} GB free",
61
+ "Uptime": f"{time.time() - psutil.boot_time():.0f} seconds"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  }
63
+ return json.dumps(info, indent=2)
64
  except Exception as e:
65
+ return f"Error getting system info: {str(e)}"
 
 
 
 
 
 
66
 
67
+ def list_files(self, path: str = "/home/user/workspace") -> str:
68
+ """List files in workspace"""
69
  try:
70
+ if not os.path.exists(path):
71
+ return f"Path does not exist: {path}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
+ files = []
74
+ for item in os.listdir(path):
75
+ item_path = os.path.join(path, item)
76
+ if os.path.isdir(item_path):
77
+ files.append(f"[DIR] {item}")
78
+ else:
79
+ size = os.path.getsize(item_path)
80
+ files.append(f"[FILE] {item} ({size} bytes)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
+ return "\n".join(files) if files else "No files found"
 
 
 
 
83
  except Exception as e:
84
+ return f"Error listing files: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
85
 
86
+ # Create sandbox instance
87
+ sandbox = SimpleSandbox()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
+ def terminal_interface():
90
+ """Create Gradio interface"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
+ with gr.Blocks(title="Ubuntu Sandbox v2.0", theme=gr.themes.Soft()) as demo:
93
+ gr.Markdown("# πŸš€ Ubuntu Sandbox v2.0 - AI Development Environment")
94
+ gr.Markdown("A minimal, secure environment for AI models to execute commands and manage files.")
95
 
96
+ with gr.Tabs():
97
+ with gr.Tab("πŸ’» Terminal"):
98
+ command = gr.Textbox(
99
+ label="Enter Command",
100
+ placeholder="e.g., ls, pwd, python --version, echo 'Hello AI!'"
101
+ )
102
+ output = gr.Textbox(
103
+ label="Output",
104
+ lines=10,
105
+ max_lines=20,
106
+ info="Command execution results will appear here"
 
 
 
 
 
 
 
 
107
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
+ gr.Button("▢️ Execute", variant="primary").click(
110
+ fn=sandbox.execute_command,
111
+ inputs=[command],
112
+ outputs=[output]
113
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
+ with gr.Tab("πŸ“Š System Info"):
116
+ system_output = gr.Textbox(
117
+ label="System Information",
118
+ lines=8,
119
+ info="Current system status and resources"
120
+ )
121
+ gr.Button("πŸ”„ Refresh Info", variant="secondary").click(
122
+ fn=sandbox.get_system_info,
123
+ inputs=[],
124
+ outputs=[system_output]
125
+ )
 
 
 
 
 
 
 
126
 
127
+ with gr.Tab("πŸ“ File Browser"):
128
+ file_path = gr.Textbox(
129
+ label="Directory Path",
130
+ value="/home/user/workspace",
131
+ info="Enter path to list files"
132
+ )
133
+ files_output = gr.Textbox(
134
+ label="Files and Directories",
135
+ lines=10,
136
+ info="Directory contents will be listed here"
137
+ )
138
+ gr.Button("πŸ“‹ List Files", variant="secondary").click(
139
+ fn=sandbox.list_files,
140
+ inputs=[file_path],
141
+ outputs=[files_output]
142
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
 
144
+ gr.Markdown("## πŸ”§ API Endpoints")
145
+ gr.Markdown("""
146
+ - **GET** `/health` - Health check
147
+ - **POST** `/api/execute` - Execute command
148
+ - **GET** `/api/system` - Get system info
149
+ - **GET** `/api/files` - List files
150
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
152
+ return demo
153
 
 
154
  if __name__ == "__main__":
155
+ print("πŸš€ Starting Ubuntu Sandbox v2.0...")
156
+ app = terminal_interface()
157
+ app.launch(
158
+ server_name="0.0.0.0",
159
+ server_port=7860,
160
+ share=False,
161
+ show_error=True
162
+ )