theguywhosucks commited on
Commit
b8b372f
·
verified ·
1 Parent(s): 15b5624

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +269 -1063
app.py CHANGED
@@ -6,35 +6,75 @@ import time
6
  from datetime import datetime, timedelta
7
  import threading
8
 
9
- # Backend engines
10
  BACKEND_ENGINES = [
11
  "ChocoLaboratory/SANDBOXBACKEND2",
12
  "ChocoLaboratory/SANDBOX_BACKEND"
13
  ]
 
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  class SandboxManager:
 
16
  def __init__(self):
17
  self.client = None
18
  self.current_engine = None
19
  self.is_connected = False
20
  self.sandbox_created = False
21
  self.creation_time = None
22
- self.time_limit_hours = 2
23
- self.auto_refresh_thread = None
24
- self.stop_auto_refresh = False
25
-
26
  def select_random_engine(self):
27
- """Select a random backend engine"""
28
  self.current_engine = random.choice(BACKEND_ENGINES)
29
  return self.current_engine
30
-
31
- def get_remaining_time(self):
32
- """Calculate remaining time for the sandbox"""
33
  if not self.creation_time:
34
  return "N/A"
35
 
36
  elapsed = datetime.now() - self.creation_time
37
- remaining = timedelta(hours=self.time_limit_hours) - elapsed
38
 
39
  if remaining.total_seconds() <= 0:
40
  return "EXPIRED"
@@ -42,1131 +82,297 @@ class SandboxManager:
42
  hours, remainder = divmod(int(remaining.total_seconds()), 3600)
43
  minutes, seconds = divmod(remainder, 60)
44
  return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
45
-
46
- def is_sandbox_expired(self):
47
- """Check if sandbox has exceeded the 2-hour limit"""
48
  if not self.creation_time:
49
  return False
50
-
51
- elapsed = datetime.now() - self.creation_time
52
- return elapsed.total_seconds() > (self.time_limit_hours * 3600)
53
-
54
  def connect(self):
55
- """Connect to a backend engine"""
56
- if self.sandbox_created:
57
- return "⚠️ SANDBOX ALREADY ACTIVE - Single session limit enforced", self.current_engine
58
-
59
  engine = self.select_random_engine()
60
  try:
61
  self.client = Client(engine)
62
  self.is_connected = True
63
- return f"✅ ENGINE ONLINE - {engine}", engine
64
  except Exception as e:
65
  self.is_connected = False
66
  self.current_engine = None
67
- return f"❌ CONNECTION FAILED - {engine}: {str(e)}", "CONNECTION FAILED"
68
-
69
  def launch_sandbox(self, main_py_code, requirements_txt):
70
- """Launch the sandbox (only once per session)"""
71
  if self.sandbox_created:
72
- remaining = self.get_remaining_time()
73
- if remaining == "EXPIRED":
74
- return " SESSION EXPIRED - 2-hour limit reached. Refresh page for new session.", ""
75
- return f"⚠️ SANDBOX ACTIVE - Remaining: {remaining}", self.get_status()
76
-
77
  if not self.is_connected:
78
- return "❌ ENGINE OFFLINE - Connect to backend first", ""
79
-
80
  try:
81
  result = self.client.predict(
82
  code=main_py_code,
83
  requirements=requirements_txt,
84
  api_name="/launch_sandbox"
85
  )
86
-
87
- # Mark sandbox as created and record creation time
88
  self.sandbox_created = True
89
  self.creation_time = datetime.now()
 
90
 
91
- # Start auto-refresh thread for time monitoring
92
- self.start_time_monitor()
93
-
94
- remaining = self.get_remaining_time()
95
- return (f"🚀 SANDBOX DEPLOYED - Engine: {self.current_engine}\n"
96
- f"⏱️ SESSION LIMIT: 2 hours | REMAINING: {remaining}\n"
97
- f"📊 STATUS: ACTIVE\n\n"
98
- f"ENGINE RESPONSE:\n{result}"), self.get_status()
99
  except Exception as e:
100
- return f"❌ DEPLOYMENT FAILED - {str(e)}", ""
101
-
102
  def fetch_logs(self):
103
- """Fetch logs from the sandbox"""
104
- if not self.is_connected:
105
- return " ENGINE OFFLINE - No connection to backend"
106
-
107
- if self.is_sandbox_expired():
108
- return "⏰ SESSION EXPIRED - Logs unavailable after 2-hour limit"
109
-
110
  try:
111
  result = self.client.predict(api_name="/fetch_logs")
112
  timestamp = datetime.now().strftime("%H:%M:%S")
113
- remaining = self.get_remaining_time()
114
- return f"[{timestamp}] REMAINING: {remaining} | ENGINE: {self.current_engine}\n{'='*80}\n{result}"
115
  except Exception as e:
116
- return f"[ERROR] LOG FETCH FAILED - {str(e)}"
117
-
118
  def kill_sandbox(self):
119
- """Terminate the sandbox"""
120
- if not self.is_connected:
121
- return "❌ ENGINE OFFLINE - No connection available", ""
122
-
123
  try:
124
  result = self.client.predict(api_name="/kill_sandbox")
125
  self.sandbox_created = False
126
  self.creation_time = None
127
- self.stop_auto_refresh = True
128
- return f"🛑 SANDBOX TERMINATED - Engine: {self.current_engine}\n\nENGINE RESPONSE:\n{result}", "TERMINATED"
129
  except Exception as e:
130
- return f"❌ TERMINATION FAILED - {str(e)}", ""
131
-
132
  def get_status(self):
133
- """Get current sandbox status"""
134
  if not self.is_connected:
135
  return "OFFLINE"
136
-
137
- if self.is_sandbox_expired():
 
138
  return "EXPIRED"
139
 
140
  try:
141
  result = self.client.predict(api_name="/status_sandbox")
142
- remaining = self.get_remaining_time()
143
- if "running" in str(result).lower():
144
- return f"RUNNING | {remaining}"
 
145
  else:
146
- return f"IDLE | {remaining}"
147
  except Exception as e:
148
- return f"ERROR | {str(e)}"
149
-
150
- def start_time_monitor(self):
151
- """Start monitoring thread for time limits"""
 
152
  def monitor():
153
- while not self.stop_auto_refresh and self.sandbox_created:
154
- if self.is_sandbox_expired():
155
- try:
156
- self.kill_sandbox()
157
- except:
158
- pass
159
  break
160
- time.sleep(60) # Check every minute
161
-
162
- self.auto_refresh_thread = threading.Thread(target=monitor, daemon=True)
163
- self.auto_refresh_thread.start()
164
-
165
- # Initialize sandbox manager
166
- sandbox = SandboxManager()
167
-
168
- # Sample files
169
- SAMPLE_MAIN_PY = """#!/usr/bin/env python3
170
- # SANDBOX COMPUTE PLATFORM - ENTERPRISE APPLICATION
171
- # Production-ready Python service with monitoring capabilities
172
-
173
- import os
174
- import sys
175
- import time
176
- import logging
177
- from datetime import datetime, timezone
178
- import json
179
- import socket
180
- import psutil
181
-
182
- # Configure enterprise logging
183
- logging.basicConfig(
184
- level=logging.INFO,
185
- format='[%(asctime)s] %(levelname)s - %(message)s',
186
- handlers=[
187
- logging.StreamHandler(sys.stdout),
188
- logging.FileHandler('/tmp/application.log')
189
- ]
190
- )
191
-
192
- logger = logging.getLogger(__name__)
193
-
194
- class SandboxApplication:
195
- def __init__(self):
196
- self.start_time = datetime.now(timezone.utc)
197
- self.hostname = socket.gethostname()
198
- self.pid = os.getpid()
199
-
200
- def get_system_info(self):
201
- \"\"\"Collect system metrics\"\"\"
202
- return {
203
- 'hostname': self.hostname,
204
- 'pid': self.pid,
205
- 'uptime': str(datetime.now(timezone.utc) - self.start_time),
206
- 'cpu_percent': psutil.cpu_percent(),
207
- 'memory_mb': psutil.virtual_memory().used // 1024 // 1024,
208
- 'disk_usage': psutil.disk_usage('/').percent
209
- }
210
-
211
- def run(self):
212
- \"\"\"Main application loop\"\"\"
213
- logger.info(f"🚀 SANDBOX APPLICATION INITIALIZED")
214
- logger.info(f"📍 HOSTNAME: {self.hostname} | PID: {self.pid}")
215
- logger.info(f"⏰ START TIME: {self.start_time}")
216
- logger.info("="*60)
217
-
218
- cycle = 0
219
- while True:
220
- try:
221
- cycle += 1
222
- current_time = datetime.now(timezone.utc)
223
-
224
- # Log system status every 10 cycles (5 minutes)
225
- if cycle % 10 == 0:
226
- metrics = self.get_system_info()
227
- logger.info(f"📊 METRICS | CPU: {metrics['cpu_percent']:.1f}% | "
228
- f"RAM: {metrics['memory_mb']}MB | "
229
- f"UPTIME: {metrics['uptime']}")
230
-
231
- # Regular heartbeat
232
- logger.info(f"⚡ CYCLE {cycle:04d} | {current_time.strftime('%H:%M:%S UTC')} | STATUS: OPERATIONAL")
233
-
234
- # Application logic here
235
- time.sleep(30) # 30-second intervals
236
-
237
- except KeyboardInterrupt:
238
- logger.info("🛑 SHUTDOWN SIGNAL RECEIVED")
239
- break
240
- except Exception as e:
241
- logger.error(f"❌ ERROR: {str(e)}")
242
- time.sleep(5)
243
 
244
- logger.info("🏁 APPLICATION TERMINATED")
245
-
246
- def main():
247
- app = SandboxApplication()
248
- app.run()
249
-
250
- if __name__ == "__main__":
251
- main()
252
- """
253
-
254
- SAMPLE_REQUIREMENTS_TXT = """# SANDBOX COMPUTE PLATFORM - PRODUCTION DEPENDENCIES
255
- # Enterprise-grade package specifications with version pinning
256
-
257
- # System monitoring and utilities
258
- psutil==5.9.6
259
- requests==2.31.0
260
-
261
- # Web framework and APIs
262
- flask==3.0.0
263
- flask-cors==4.0.0
264
- gunicorn==21.2.0
265
-
266
- # Data processing and analysis
267
- pandas==2.1.3
268
- numpy==1.25.2
269
- scipy==1.11.4
270
-
271
- # Database connectivity
272
- sqlalchemy==2.0.23
273
- pymongo==4.6.0
274
-
275
- # Caching and performance
276
- redis==5.0.1
277
- celery==5.3.4
278
-
279
- # Security and authentication
280
- cryptography==41.0.8
281
- pyjwt==2.8.0
282
 
283
- # Configuration and environment
284
- python-dotenv==1.0.0
285
- pydantic==2.5.0
286
-
287
- # Logging and monitoring
288
- structlog==23.2.0
289
- prometheus-client==0.19.0
290
-
291
- # Development and testing
292
- pytest==7.4.3
293
- black==23.11.0
294
- flake8==6.1.0
295
- """
296
-
297
- # Industrial-grade full-screen CSS
298
- industrial_css = """
299
- @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Inter:wght@300;400;500;600;700;800;900&display=swap');
300
-
301
- /* CSS Variables for Industrial Theme */
302
- :root {
303
- --primary: #0ea5e9;
304
- --primary-dark: #0284c7;
305
- --primary-darker: #0369a1;
306
- --secondary: #64748b;
307
- --accent: #06b6d4;
308
- --success: #059669;
309
- --warning: #d97706;
310
- --danger: #dc2626;
311
- --info: #2563eb;
312
-
313
- --bg-primary: #0f172a;
314
- --bg-secondary: #1e293b;
315
- --bg-tertiary: #334155;
316
- --bg-surface: #475569;
317
- --bg-elevated: #64748b;
318
-
319
- --text-primary: #f8fafc;
320
- --text-secondary: #e2e8f0;
321
- --text-muted: #94a3b8;
322
- --text-inverse: #0f172a;
323
-
324
- --border: #334155;
325
- --border-light: #475569;
326
- --border-accent: #0ea5e9;
327
-
328
- --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
329
- --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
330
- --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
331
- --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.6);
332
-
333
- --glow-primary: 0 0 20px rgba(14, 165, 233, 0.3);
334
- --glow-success: 0 0 20px rgba(5, 150, 105, 0.3);
335
- --glow-danger: 0 0 20px rgba(220, 38, 38, 0.3);
336
- --glow-warning: 0 0 20px rgba(217, 119, 6, 0.3);
337
- }
338
-
339
- /* Global Reset and Base Styles */
340
- * {
341
- margin: 0;
342
- padding: 0;
343
- box-sizing: border-box;
344
- }
345
-
346
- html, body {
347
- height: 100vh !important;
348
- width: 100vw !important;
349
- overflow-x: hidden !important;
350
- background: var(--bg-primary) !important;
351
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif !important;
352
- color: var(--text-primary) !important;
353
- line-height: 1.5 !important;
354
- }
355
 
356
- /* Gradio Container - Full Screen */
357
- .gradio-container {
358
- max-width: 100vw !important;
359
- width: 100vw !important;
360
- min-height: 100vh !important;
361
- height: 100vh !important;
362
- margin: 0 !important;
363
- padding: 0 !important;
364
- background: linear-gradient(135deg, var(--bg-primary) 0%, #0c1420 50%, var(--bg-primary) 100%) !important;
365
- display: flex !important;
366
- flex-direction: column !important;
367
- overflow-y: auto !important;
368
- }
369
-
370
- /* Main Content Wrapper */
371
- .main-content {
372
- flex: 1 !important;
373
- padding: 0 !important;
374
- margin: 0 !important;
375
- }
376
-
377
- /* Industrial Header */
378
- .industrial-header {
379
- background: linear-gradient(135deg, var(--bg-secondary) 0%, var(--bg-tertiary) 100%) !important;
380
- border-bottom: 3px solid var(--primary) !important;
381
- padding: 1.5rem 2rem !important;
382
- box-shadow: var(--shadow-lg) !important;
383
- position: relative !important;
384
- overflow: hidden !important;
385
- }
386
-
387
- .industrial-header::before {
388
- content: '';
389
- position: absolute;
390
- top: 0;
391
- left: 0;
392
- right: 0;
393
- bottom: 0;
394
- background: repeating-linear-gradient(
395
- 90deg,
396
- transparent,
397
- transparent 2px,
398
- rgba(14, 165, 233, 0.1) 2px,
399
- rgba(14, 165, 233, 0.1) 4px
400
- );
401
- pointer-events: none;
402
- }
403
-
404
- .header-content {
405
- position: relative !important;
406
- z-index: 2 !important;
407
- display: flex !important;
408
- justify-content: space-between !important;
409
- align-items: center !important;
410
- }
411
-
412
- .platform-title {
413
- font-size: 2.5rem !important;
414
- font-weight: 900 !important;
415
- color: var(--text-primary) !important;
416
- text-shadow: 0 0 20px var(--primary) !important;
417
- letter-spacing: -0.025em !important;
418
- margin: 0 !important;
419
- }
420
-
421
- .platform-subtitle {
422
- font-size: 1rem !important;
423
- color: var(--text-muted) !important;
424
- margin-top: 0.5rem !important;
425
- font-weight: 400 !important;
426
- text-transform: uppercase !important;
427
- letter-spacing: 0.05em !important;
428
- }
429
-
430
- .system-status {
431
- text-align: right !important;
432
- font-family: 'JetBrains Mono', monospace !important;
433
- font-size: 0.875rem !important;
434
- }
435
-
436
- .status-indicator {
437
- display: flex !important;
438
- align-items: center !important;
439
- gap: 0.5rem !important;
440
- margin-bottom: 0.25rem !important;
441
- color: var(--text-secondary) !important;
442
- }
443
-
444
- .status-dot {
445
- width: 8px !important;
446
- height: 8px !important;
447
- background: var(--success) !important;
448
- border-radius: 50% !important;
449
- box-shadow: var(--glow-success) !important;
450
- animation: pulse-dot 2s ease-in-out infinite !important;
451
- }
452
-
453
- /* Navigation Breadcrumb */
454
- .nav-breadcrumb {
455
- background: var(--bg-secondary) !important;
456
- border-bottom: 1px solid var(--border) !important;
457
- padding: 0.75rem 2rem !important;
458
- font-family: 'JetBrains Mono', monospace !important;
459
- font-size: 0.875rem !important;
460
- color: var(--text-muted) !important;
461
- text-transform: uppercase !important;
462
- letter-spacing: 0.05em !important;
463
- }
464
-
465
- /* Industrial Cards */
466
- .industrial-card {
467
- background: linear-gradient(135deg, var(--bg-secondary) 0%, rgba(30, 41, 59, 0.95) 100%) !important;
468
- border: 1px solid var(--border) !important;
469
- border-radius: 0.75rem !important;
470
- margin: 1rem !important;
471
- overflow: hidden !important;
472
- box-shadow: var(--shadow-lg) !important;
473
- backdrop-filter: blur(10px) !important;
474
- position: relative !important;
475
- }
476
-
477
- .industrial-card::before {
478
- content: '';
479
- position: absolute;
480
- top: 0;
481
- left: 0;
482
- right: 0;
483
- height: 2px;
484
- background: linear-gradient(90deg, var(--primary), var(--accent), var(--primary));
485
- opacity: 0.8;
486
- }
487
-
488
- .card-header {
489
- background: linear-gradient(135deg, var(--bg-tertiary), var(--bg-surface)) !important;
490
- border-bottom: 1px solid var(--border-light) !important;
491
- padding: 1.25rem 2rem !important;
492
- font-weight: 700 !important;
493
- font-size: 1.1rem !important;
494
- color: var(--text-primary) !important;
495
- text-transform: uppercase !important;
496
- letter-spacing: 0.025em !important;
497
- display: flex !important;
498
- align-items: center !important;
499
- gap: 0.75rem !important;
500
- }
501
-
502
- .card-content {
503
- padding: 2rem !important;
504
- }
505
-
506
- /* Industrial Buttons */
507
- .btn-industrial {
508
- background: linear-gradient(135deg, var(--primary), var(--primary-dark)) !important;
509
- color: var(--text-primary) !important;
510
- border: 1px solid var(--primary-darker) !important;
511
- border-radius: 0.5rem !important;
512
- padding: 1rem 2rem !important;
513
- font-weight: 600 !important;
514
- font-size: 0.875rem !important;
515
- text-transform: uppercase !important;
516
- letter-spacing: 0.025em !important;
517
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
518
- box-shadow: var(--shadow-md) !important;
519
- position: relative !important;
520
- overflow: hidden !important;
521
- }
522
-
523
- .btn-industrial:hover {
524
- transform: translateY(-2px) !important;
525
- box-shadow: var(--shadow-xl), var(--glow-primary) !important;
526
- background: linear-gradient(135deg, var(--primary-dark), var(--primary-darker)) !important;
527
- }
528
-
529
- .btn-industrial:active {
530
- transform: translateY(0) !important;
531
- }
532
-
533
- .btn-secondary {
534
- background: linear-gradient(135deg, var(--bg-surface), var(--bg-elevated)) !important;
535
- color: var(--text-primary) !important;
536
- border: 1px solid var(--border-light) !important;
537
- }
538
-
539
- .btn-secondary:hover {
540
- background: linear-gradient(135deg, var(--bg-elevated), var(--secondary)) !important;
541
- box-shadow: var(--shadow-lg) !important;
542
- }
543
-
544
- .btn-danger {
545
- background: linear-gradient(135deg, var(--danger), #b91c1c) !important;
546
- border: 1px solid #7f1d1d !important;
547
- }
548
-
549
- .btn-danger:hover {
550
- box-shadow: var(--shadow-xl), var(--glow-danger) !important;
551
- }
552
-
553
- /* Status Displays */
554
- .status-display {
555
- background: var(--bg-tertiary) !important;
556
- color: var(--text-primary) !important;
557
- border: 1px solid var(--border) !important;
558
- border-radius: 0.5rem !important;
559
- padding: 1rem 1.5rem !important;
560
- font-family: 'JetBrains Mono', monospace !important;
561
- font-weight: 500 !important;
562
- text-align: center !important;
563
- text-transform: uppercase !important;
564
- letter-spacing: 0.05em !important;
565
- }
566
-
567
- .status-online {
568
- background: linear-gradient(135deg, rgba(5, 150, 105, 0.2), rgba(5, 150, 105, 0.1)) !important;
569
- border-color: var(--success) !important;
570
- color: var(--success) !important;
571
- box-shadow: var(--glow-success) !important;
572
- }
573
-
574
- .status-offline {
575
- background: linear-gradient(135deg, rgba(220, 38, 38, 0.2), rgba(220, 38, 38, 0.1)) !important;
576
- border-color: var(--danger) !important;
577
- color: var(--danger) !important;
578
- box-shadow: var(--glow-danger) !important;
579
- }
580
-
581
- .status-warning {
582
- background: linear-gradient(135deg, rgba(217, 119, 6, 0.2), rgba(217, 119, 6, 0.1)) !important;
583
- border-color: var(--warning) !important;
584
- color: var(--warning) !important;
585
- box-shadow: var(--glow-warning) !important;
586
- }
587
-
588
- /* Code Editors */
589
- .code-editor-industrial {
590
- font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace !important;
591
- background: linear-gradient(135deg, #0c1420 0%, #0f172a 100%) !important;
592
- color: var(--text-secondary) !important;
593
- border: 1px solid var(--border) !important;
594
- border-radius: 0.75rem !important;
595
- box-shadow: inset var(--shadow-lg) !important;
596
- line-height: 1.6 !important;
597
- }
598
-
599
- .requirements-editor {
600
- font-family: 'JetBrains Mono', monospace !important;
601
- background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%) !important;
602
- color: #e5e5e5 !important;
603
- border: 1px solid var(--border) !important;
604
- border-radius: 0.75rem !important;
605
- }
606
-
607
- /* Terminal Output */
608
- .terminal-industrial {
609
- font-family: 'JetBrains Mono', monospace !important;
610
- background: linear-gradient(135deg, #000000 0%, #0a0a0a 100%) !important;
611
- color: #00ff88 !important;
612
- border: 1px solid var(--border) !important;
613
- border-radius: 0.75rem !important;
614
- padding: 1.5rem !important;
615
- box-shadow: inset var(--shadow-lg) !important;
616
- line-height: 1.4 !important;
617
- font-size: 0.875rem !important;
618
- white-space: pre-wrap !important;
619
- overflow-y: auto !important;
620
- }
621
-
622
- /* Form Labels */
623
- .form-label-industrial {
624
- font-weight: 700 !important;
625
- color: var(--text-primary) !important;
626
- font-size: 0.875rem !important;
627
- text-transform: uppercase !important;
628
- letter-spacing: 0.05em !important;
629
- margin-bottom: 0.75rem !important;
630
- display: block !important;
631
- border-left: 3px solid var(--primary) !important;
632
- padding-left: 0.75rem !important;
633
- }
634
-
635
- /* Monitoring Panel */
636
- .monitor-panel {
637
- background: linear-gradient(135deg, var(--bg-secondary) 0%, var(--bg-tertiary) 100%) !important;
638
- border: 1px solid var(--border-accent) !important;
639
- border-radius: 0.75rem !important;
640
- box-shadow: var(--shadow-lg), var(--glow-primary) !important;
641
- position: relative !important;
642
- overflow: hidden !important;
643
- }
644
-
645
- .monitor-panel::before {
646
- content: '';
647
- position: absolute;
648
- top: 0;
649
- left: 0;
650
- right: 0;
651
- height: 2px;
652
- background: linear-gradient(90deg, var(--primary), var(--accent), var(--primary));
653
- animation: scan-line 3s ease-in-out infinite;
654
- }
655
-
656
- /* System Metrics */
657
- .metric-grid {
658
- display: grid !important;
659
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) !important;
660
- gap: 1rem !important;
661
- margin-bottom: 1.5rem !important;
662
- }
663
-
664
- .metric-card {
665
- background: rgba(15, 23, 42, 0.8) !important;
666
- border: 1px solid var(--border) !important;
667
- border-radius: 0.5rem !important;
668
- padding: 1rem !important;
669
- text-align: center !important;
670
- }
671
-
672
- .metric-value {
673
- font-family: 'JetBrains Mono', monospace !important;
674
- font-size: 1.5rem !important;
675
- font-weight: 700 !important;
676
- color: var(--primary) !important;
677
- text-shadow: var(--glow-primary) !important;
678
- }
679
-
680
- .metric-label {
681
- font-size: 0.75rem !important;
682
- color: var(--text-muted) !important;
683
- text-transform: uppercase !important;
684
- letter-spacing: 0.05em !important;
685
- margin-top: 0.25rem !important;
686
- }
687
-
688
- /* Layouts */
689
- .main-grid {
690
- display: grid !important;
691
- grid-template-columns: 1fr 350px !important;
692
- gap: 0 !important;
693
- height: calc(100vh - 140px) !important;
694
- overflow: hidden !important;
695
- }
696
-
697
- .content-area {
698
- display: flex !important;
699
- flex-direction: column !important;
700
- gap: 0 !important;
701
- overflow-y: auto !important;
702
- }
703
-
704
- .sidebar {
705
- background: var(--bg-secondary) !important;
706
- border-left: 1px solid var(--border) !important;
707
- overflow-y: auto !important;
708
- }
709
-
710
- /* Animations */
711
- @keyframes pulse-dot {
712
- 0%, 100% { opacity: 1; transform: scale(1); }
713
- 50% { opacity: 0.5; transform: scale(1.2); }
714
- }
715
-
716
- @keyframes scan-line {
717
- 0% { transform: translateX(-100%); }
718
- 100% { transform: translateX(100vw); }
719
- }
720
-
721
- @keyframes glow-pulse {
722
- 0%, 100% { box-shadow: var(--shadow-lg); }
723
- 50% { box-shadow: var(--shadow-lg), var(--glow-primary); }
724
- }
725
-
726
- /* Tab Styling */
727
- .tab-nav {
728
- background: var(--bg-tertiary) !important;
729
- border-bottom: 1px solid var(--border) !important;
730
- }
731
-
732
- .tab-nav button {
733
- background: transparent !important;
734
- color: var(--text-muted) !important;
735
- border: none !important;
736
- padding: 1rem 2rem !important;
737
- font-weight: 500 !important;
738
- text-transform: uppercase !important;
739
- letter-spacing: 0.025em !important;
740
- transition: all 0.3s ease !important;
741
- }
742
-
743
- .tab-nav button.selected {
744
- background: var(--primary) !important;
745
- color: var(--text-primary) !important;
746
- box-shadow: var(--glow-primary) !important;
747
- }
748
-
749
- /* Warning Boxes */
750
- .warning-box {
751
- background: linear-gradient(135deg, rgba(217, 119, 6, 0.1), rgba(217, 119, 6, 0.05)) !important;
752
- border: 1px solid var(--warning) !important;
753
- border-radius: 0.5rem !important;
754
- padding: 1.5rem !important;
755
- margin: 1rem 0 !important;
756
- color: var(--text-primary) !important;
757
- }
758
-
759
- .warning-title {
760
- font-weight: 700 !important;
761
- color: var(--warning) !important;
762
- font-size: 0.875rem !important;
763
- text-transform: uppercase !important;
764
- letter-spacing: 0.05em !important;
765
- margin-bottom: 0.5rem !important;
766
- }
767
-
768
- /* Responsive Design */
769
- @media (max-width: 1200px) {
770
- .main-grid {
771
- grid-template-columns: 1fr !important;
772
- }
773
-
774
- .sidebar {
775
- border-left: none !important;
776
- border-top: 1px solid var(--border) !important;
777
- }
778
- }
779
-
780
- /* Scrollbar Styling */
781
- ::-webkit-scrollbar {
782
- width: 8px !important;
783
- height: 8px !important;
784
- }
785
-
786
- ::-webkit-scrollbar-track {
787
- background: var(--bg-primary) !important;
788
- }
789
-
790
- ::-webkit-scrollbar-thumb {
791
- background: var(--primary) !important;
792
- border-radius: 4px !important;
793
- }
794
-
795
- ::-webkit-scrollbar-thumb:hover {
796
- background: var(--primary-dark) !important;
797
- }
798
-
799
- /* Loading States */
800
- .loading {
801
- animation: glow-pulse 2s ease-in-out infinite !important;
802
- }
803
-
804
- /* Focus States */
805
- .btn-industrial:focus,
806
- .status-display:focus,
807
- input:focus,
808
- textarea:focus {
809
- outline: 2px solid var(--primary) !important;
810
- outline-offset: 2px !important;
811
- }
812
- """
813
-
814
- def auto_refresh_status():
815
- return sandbox.get_status()
816
-
817
- def auto_refresh_logs():
818
- return sandbox.fetch_logs()
819
 
 
820
  def load_sample_files():
821
  return SAMPLE_MAIN_PY, SAMPLE_REQUIREMENTS_TXT
822
 
823
  def get_session_info():
824
  if sandbox.sandbox_created:
825
- remaining = sandbox.get_remaining_time()
826
- created = sandbox.creation_time.strftime("%H:%M:%S") if sandbox.creation_time else "N/A"
827
- return f"SESSION ACTIVE | CREATED: {created} | REMAINING: {remaining}"
828
- return "NO ACTIVE SESSION"
829
-
830
- def get_system_metrics():
831
- return {
832
- 'timestamp': datetime.now().strftime("%H:%M:%S"),
833
- 'engine': sandbox.current_engine or "NONE",
834
- 'status': sandbox.get_status(),
835
- 'connection': "ONLINE" if sandbox.is_connected else "OFFLINE"
836
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
837
 
838
- # Build industrial-grade full-screen interface
839
- with gr.Blocks(css=industrial_css, title="SANDBOX COMPUTE PLATFORM") as demo:
840
-
841
- # Industrial Header
842
  gr.HTML("""
843
- <div class="industrial-header">
844
- <div class="header-content">
845
- <div>
846
- <div class="platform-title">⚡ SANDBOX COMPUTE PLATFORM</div>
847
- <div class="platform-subtitle">ENTERPRISE-GRADE CLOUD COMPUTING INFRASTRUCTURE</div>
848
- </div>
849
- <div class="system-status">
850
- <div class="status-indicator">
851
- <div class="status-dot"></div>
852
- <span>SYSTEM ONLINE</span>
853
- </div>
854
- <div class="status-indicator">
855
- <span>VERSION 2.1.0</span>
856
- </div>
857
- <div class="status-indicator">
858
- <span>SECURITY LEVEL: MAXIMUM</span>
859
- </div>
860
- </div>
861
- </div>
862
  </div>
863
  """)
864
-
865
- # Navigation Breadcrumb
866
- gr.HTML("""
867
- <div class="nav-breadcrumb">
868
- 🏠 CONTROL CENTER → ⚡ COMPUTE SERVICES → 🛠️ SANDBOX MANAGEMENT CONSOLE → 🚀 DEPLOYMENT INTERFACE
869
- </div>
870
- """)
871
-
872
- # Main Grid Layout
873
- with gr.Row(elem_classes=["main-grid"]):
874
-
875
- # Main Content Area
876
- with gr.Column(elem_classes=["content-area"], scale=3):
877
-
878
- # Session Status Bar
879
- with gr.Group(elem_classes=["industrial-card"]):
880
- gr.HTML('<div class="card-header">📊 SESSION CONTROL CENTER</div>')
881
- with gr.Group(elem_classes=["card-content"]):
882
- session_info = gr.Textbox(
883
- value=get_session_info(),
884
- label="",
885
- interactive=False,
886
- elem_classes=["status-display"],
887
- show_label=False
888
- )
889
-
890
- with gr.Row():
891
- with gr.Column():
892
- gr.HTML('<div class="form-label-industrial">⚡ ENGINE STATUS</div>')
893
- current_engine_display = gr.Textbox(
894
- value="NO ENGINE SELECTED",
895
- interactive=False,
896
- elem_classes=["status-display", "status-offline"],
897
- show_label=False
898
- )
899
-
900
- with gr.Column():
901
- gr.HTML('<div class="form-label-industrial">🔗 CONNECTION STATUS</div>')
902
- connection_status = gr.Textbox(
903
- value="DISCONNECTED",
904
- interactive=False,
905
- elem_classes=["status-display", "status-offline"],
906
- show_label=False
907
- )
908
 
909
- # Infrastructure Management
910
- with gr.Group(elem_classes=["industrial-card"]):
911
- gr.HTML('<div class="card-header">🔧 INFRASTRUCTURE MANAGEMENT</div>')
912
- with gr.Group(elem_classes=["card-content"]):
 
 
 
913
  with gr.Row():
914
- connect_btn = gr.Button("🚀 INITIALIZE ENGINE", elem_classes=["btn-industrial"])
915
- load_samples_btn = gr.Button("📋 LOAD TEMPLATES", elem_classes=["btn-secondary"])
916
-
917
- # Warning Box
918
- gr.HTML("""
919
- <div class="warning-box">
920
- <div class="warning-title">⚠️ SESSION RESTRICTIONS</div>
921
- <strong>• SINGLE SANDBOX LIMIT:</strong> One sandbox per browser session<br>
922
- <strong>• TIME LIMIT:</strong> 2 hours maximum runtime<br>
923
- <strong>• AUTO-TERMINATION:</strong> Sandbox terminates when time expires<br>
924
- <strong>• NO PERSISTENCE:</strong> All data is lost after termination
925
- </div>
926
- """)
927
-
928
- # Development Environment
929
- with gr.Group(elem_classes=["industrial-card"]):
930
- gr.HTML('<div class="card-header">💻 DEVELOPMENT ENVIRONMENT</div>')
931
- with gr.Group(elem_classes=["card-content"]):
932
-
933
- with gr.Tab("📝 MAIN.PY - APPLICATION SOURCE"):
934
- gr.HTML('<div class="form-label-industrial">🐍 PYTHON APPLICATION CODE</div>')
935
- main_py_editor = gr.Code(
936
- value=SAMPLE_MAIN_PY,
937
- language="python",
938
- lines=20,
939
- elem_classes=["code-editor-industrial"],
940
- show_label=False
941
- )
942
-
943
- with gr.Tab("📦 REQUIREMENTS.TXT - DEPENDENCIES"):
944
- gr.HTML('<div class="form-label-industrial">📋 PACKAGE SPECIFICATIONS</div>')
945
- requirements_editor = gr.Textbox(
946
- value=SAMPLE_REQUIREMENTS_TXT,
947
- lines=20,
948
- elem_classes=["requirements-editor"],
949
- show_label=False,
950
- max_lines=25
951
- )
952
-
953
- # Deployment Controls
954
- gr.HTML('<hr style="margin: 2rem 0; border: 1px solid var(--border); opacity: 0.5;">')
955
-
956
  with gr.Row():
957
- launch_btn = gr.Button(
958
- "🚀 DEPLOY SANDBOX",
959
- elem_classes=["btn-industrial"],
960
- size="lg"
961
- )
962
- kill_btn = gr.Button(
963
- "🛑 TERMINATE SANDBOX",
964
- elem_classes=["btn-danger"],
965
- size="lg"
966
- )
967
-
968
- gr.HTML('<div class="form-label-industrial">📊 DEPLOYMENT OUTPUT</div>')
969
- launch_output = gr.Textbox(
970
- lines=8,
971
- interactive=False,
972
- placeholder="[SYSTEM] Deployment logs and status information will appear here...",
973
- elem_classes=["terminal-industrial"],
974
- show_label=False
975
- )
976
-
977
- # System Logs
978
- with gr.Group(elem_classes=["industrial-card"]):
979
- gr.HTML('<div class="card-header">📋 SYSTEM LOGS & MONITORING</div>')
980
- with gr.Group(elem_classes=["card-content"]):
981
 
982
- with gr.Row():
983
- fetch_logs_btn = gr.Button("📋 FETCH LOGS", elem_classes=["btn-secondary"])
984
- auto_refresh_btn = gr.Button("🔄 AUTO REFRESH", elem_classes=["btn-secondary"])
985
- clear_logs_btn = gr.Button("🗑️ CLEAR LOGS", elem_classes=["btn-secondary"])
986
 
987
- gr.HTML('<div class="form-label-industrial">📊 RUNTIME LOGS</div>')
988
- logs_display = gr.Textbox(
989
- lines=15,
990
- interactive=False,
991
- elem_classes=["terminal-industrial"],
992
- placeholder="[SYSTEM] Application logs and runtime information will appear here...",
993
- show_label=False
994
- )
995
-
996
- # Monitoring Sidebar
997
- with gr.Column(elem_classes=["sidebar"], scale=1):
998
 
999
- # System Monitor
1000
- with gr.Group(elem_classes=["monitor-panel"]):
1001
- gr.HTML('<div class="card-header">📊 SYSTEM MONITOR</div>')
1002
- with gr.Group(elem_classes=["card-content"]):
1003
-
1004
- gr.HTML('<div class="form-label-industrial">🔍 INSTANCE STATUS</div>')
1005
- status_display = gr.Textbox(
1006
- value="OFFLINE",
1007
- interactive=False,
1008
- elem_classes=["status-display", "status-offline"],
1009
- show_label=False
1010
- )
1011
-
1012
- status_refresh_btn = gr.Button("🔄 REFRESH STATUS", elem_classes=["btn-secondary"], size="sm")
1013
-
1014
- # System Metrics
1015
- gr.HTML('<div class="form-label-industrial">📈 SYSTEM METRICS</div>')
1016
- gr.HTML("""
1017
- <div class="metric-grid">
1018
- <div class="metric-card">
1019
- <div class="metric-value" id="uptime">00:00:00</div>
1020
- <div class="metric-label">UPTIME</div>
1021
- </div>
1022
- <div class="metric-card">
1023
- <div class="metric-value" id="sessions">1</div>
1024
- <div class="metric-label">SESSIONS</div>
1025
- </div>
1026
- <div class="metric-card">
1027
- <div class="metric-value" id="cpu">0%</div>
1028
- <div class="metric-label">CPU USAGE</div>
1029
- </div>
1030
- <div class="metric-card">
1031
- <div class="metric-value" id="memory">0MB</div>
1032
- <div class="metric-label">MEMORY</div>
1033
- </div>
1034
- </div>
1035
- """)
1036
-
1037
- # Time Remaining
1038
- gr.HTML('<div class="form-label-industrial">⏱️ SESSION TIMER</div>')
1039
- time_remaining = gr.Textbox(
1040
- value="NO ACTIVE SESSION",
1041
- interactive=False,
1042
- elem_classes=["status-display"],
1043
- show_label=False
1044
- )
1045
-
1046
- # Auto-refresh Controls
1047
- gr.HTML('<div class="form-label-industrial">🔄 AUTO-REFRESH</div>')
1048
- with gr.Column():
1049
- auto_refresh_status_cb = gr.Checkbox(
1050
- label="Auto-refresh status (5s interval)",
1051
- value=False
1052
- )
1053
- auto_refresh_logs_cb = gr.Checkbox(
1054
- label="Auto-refresh logs (10s interval)",
1055
- value=False
1056
- )
1057
-
1058
- # Security & Compliance
1059
- with gr.Group(elem_classes=["industrial-card"]):
1060
- gr.HTML('<div class="card-header">🔒 SECURITY & COMPLIANCE</div>')
1061
- with gr.Group(elem_classes=["card-content"]):
1062
- gr.HTML("""
1063
- <div style="font-family: 'JetBrains Mono', monospace; font-size: 0.8rem; color: var(--text-muted);">
1064
- <div style="margin-bottom: 0.5rem;"><strong>🔐 ENCRYPTION:</strong> AES-256</div>
1065
- <div style="margin-bottom: 0.5rem;"><strong>🛡️ ISOLATION:</strong> CONTAINER</div>
1066
- <div style="margin-bottom: 0.5rem;"><strong>🚫 NETWORK:</strong> RESTRICTED</div>
1067
- <div style="margin-bottom: 0.5rem;"><strong>📊 MONITORING:</strong> ACTIVE</div>
1068
- <div style="margin-bottom: 0.5rem;"><strong>🗂️ LOGGING:</strong> ENABLED</div>
1069
- <div><strong>⏰ RETENTION:</strong> 2 HOURS</div>
1070
- </div>
1071
- """)
1072
-
1073
- # Resource Usage
1074
- with gr.Group(elem_classes=["industrial-card"]):
1075
- gr.HTML('<div class="card-header">📈 RESOURCE ALLOCATION</div>')
1076
- with gr.Group(elem_classes=["card-content"]):
1077
- gr.HTML("""
1078
- <div style="font-family: 'JetBrains Mono', monospace; font-size: 0.8rem;">
1079
- <div style="margin-bottom: 1rem;">
1080
- <div style="color: var(--text-muted); margin-bottom: 0.25rem;">CPU ALLOCATION</div>
1081
- <div style="background: var(--bg-tertiary); height: 8px; border-radius: 4px; overflow: hidden;">
1082
- <div style="background: var(--primary); height: 100%; width: 25%; transition: width 0.3s;"></div>
1083
- </div>
1084
- <div style="color: var(--text-primary); font-size: 0.75rem; margin-top: 0.25rem;">1 vCPU / 4 vCPU MAX</div>
1085
- </div>
1086
-
1087
- <div style="margin-bottom: 1rem;">
1088
- <div style="color: var(--text-muted); margin-bottom: 0.25rem;">MEMORY ALLOCATION</div>
1089
- <div style="background: var(--bg-tertiary); height: 8px; border-radius: 4px; overflow: hidden;">
1090
- <div style="background: var(--success); height: 100%; width: 40%; transition: width 0.3s;"></div>
1091
- </div>
1092
- <div style="color: var(--text-primary); font-size: 0.75rem; margin-top: 0.25rem;">2GB / 8GB MAX</div>
1093
- </div>
1094
-
1095
- <div>
1096
- <div style="color: var(--text-muted); margin-bottom: 0.25rem;">STORAGE ALLOCATION</div>
1097
- <div style="background: var(--bg-tertiary); height: 8px; border-radius: 4px; overflow: hidden;">
1098
- <div style="background: var(--warning); height: 100%; width: 15%; transition: width 0.3s;"></div>
1099
- </div>
1100
- <div style="color: var(--text-primary); font-size: 0.75rem; margin-top: 0.25rem;">5GB / 20GB MAX</div>
1101
- </div>
1102
- </div>
1103
- """)
1104
-
1105
- # Event Handlers
1106
- connect_btn.click(
1107
- fn=sandbox.connect,
1108
- outputs=[connection_status, current_engine_display]
1109
- )
1110
 
1111
- load_samples_btn.click(
1112
- fn=load_sample_files,
1113
- outputs=[main_py_editor, requirements_editor]
1114
- )
 
 
 
 
 
 
 
 
 
 
 
1115
 
 
 
 
 
 
 
 
 
1116
  launch_btn.click(
1117
- fn=sandbox.launch_sandbox,
1118
  inputs=[main_py_editor, requirements_editor],
1119
- outputs=[launch_output, status_display]
1120
- )
1121
-
1122
- kill_btn.click(
1123
- fn=sandbox.kill_sandbox,
1124
- outputs=[launch_output, status_display]
1125
- )
1126
-
1127
- fetch_logs_btn.click(
1128
- fn=sandbox.fetch_logs,
1129
- outputs=logs_display
1130
- )
1131
-
1132
- status_refresh_btn.click(
1133
- fn=auto_refresh_status,
1134
- outputs=status_display
1135
- )
1136
-
1137
- # Clear logs function
1138
- def clear_logs():
1139
- return "[SYSTEM] Logs cleared by user request."
1140
-
1141
- clear_logs_btn.click(
1142
- fn=clear_logs,
1143
- outputs=logs_display
1144
  )
1145
 
1146
- # Update session info periodically
1147
- def update_session_info():
1148
- return get_session_info()
1149
-
1150
- # Auto-refresh timer updates
1151
- def update_time_remaining():
1152
- if sandbox.sandbox_created:
1153
- return sandbox.get_remaining_time()
1154
- return "NO ACTIVE SESSION"
1155
 
1156
- # Periodic updates every 5 seconds for time display
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1157
  demo.load(
1158
- fn=update_time_remaining,
1159
- outputs=time_remaining,
1160
- every=5
1161
  )
1162
 
1163
- # Launch the industrial-grade application
1164
  if __name__ == "__main__":
1165
- demo.launch(
1166
- server_name="0.0.0.0",
1167
- server_port=7860,
1168
- share=False,
1169
- debug=False,
1170
- show_error=True,
1171
- inbrowser=True
1172
- )
 
6
  from datetime import datetime, timedelta
7
  import threading
8
 
9
+ # --- Constants ---
10
  BACKEND_ENGINES = [
11
  "ChocoLaboratory/SANDBOXBACKEND2",
12
  "ChocoLaboratory/SANDBOX_BACKEND"
13
  ]
14
+ SESSION_TIME_LIMIT_HOURS = 2
15
 
16
+ # --- Sample Code ---
17
+ SAMPLE_MAIN_PY = """# Enterprise Python Application
18
+ import os
19
+ import time
20
+ from datetime import datetime
21
+
22
+ def main():
23
+ print("🚀 Sandbox Compute Platform - Initialized")
24
+ print(f"📅 Started at: {datetime.now()}")
25
+ print("✅ Environment ready for development")
26
+
27
+ # Your application code here
28
+ count = 0
29
+ while True:
30
+ print(f"[{count}] ⏱️ Heartbeat... {datetime.now().strftime('%H:%M:%S')}")
31
+ time.sleep(30)
32
+ count += 1
33
+
34
+ if __name__ == "__main__":
35
+ main()
36
+ """
37
+
38
+ SAMPLE_REQUIREMENTS_TXT = """# Production Dependencies
39
+ # Specify exact versions for reproducibility
40
+
41
+ # Web framework
42
+ flask==2.3.3
43
+ requests==2.31.0
44
+
45
+ # Data processing
46
+ pandas==2.1.1
47
+ numpy==1.24.3
48
+
49
+ # Utilities
50
+ python-dotenv==1.0.0
51
+ """
52
+
53
+ # --- SandboxManager Class ---
54
  class SandboxManager:
55
+ """Manages the lifecycle of a sandboxed environment."""
56
  def __init__(self):
57
  self.client = None
58
  self.current_engine = None
59
  self.is_connected = False
60
  self.sandbox_created = False
61
  self.creation_time = None
62
+ self.time_limit = timedelta(hours=SESSION_TIME_LIMIT_HOURS)
63
+ self.monitor_thread = None
64
+ self.stop_monitor = threading.Event()
65
+
66
  def select_random_engine(self):
67
+ """Selects a random backend engine from the available pool."""
68
  self.current_engine = random.choice(BACKEND_ENGINES)
69
  return self.current_engine
70
+
71
+ def get_remaining_time_str(self):
72
+ """Calculates and formats the remaining time for the sandbox."""
73
  if not self.creation_time:
74
  return "N/A"
75
 
76
  elapsed = datetime.now() - self.creation_time
77
+ remaining = self.time_limit - elapsed
78
 
79
  if remaining.total_seconds() <= 0:
80
  return "EXPIRED"
 
82
  hours, remainder = divmod(int(remaining.total_seconds()), 3600)
83
  minutes, seconds = divmod(remainder, 60)
84
  return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
85
+
86
+ def is_expired(self):
87
+ """Checks if the sandbox session has expired."""
88
  if not self.creation_time:
89
  return False
90
+ return (datetime.now() - self.creation_time) >= self.time_limit
91
+
 
 
92
  def connect(self):
93
+ """Connects to a random backend engine."""
94
+ if self.is_connected:
95
+ return " Already connected.", self.current_engine
96
+
97
  engine = self.select_random_engine()
98
  try:
99
  self.client = Client(engine)
100
  self.is_connected = True
101
+ return f"✅ Connection successful to: {engine}", engine
102
  except Exception as e:
103
  self.is_connected = False
104
  self.current_engine = None
105
+ return f"❌ Connection failed: {str(e)}", "Connection Failed"
106
+
107
  def launch_sandbox(self, main_py_code, requirements_txt):
108
+ """Launches the sandbox environment."""
109
  if self.sandbox_created:
110
+ if self.is_expired():
111
+ return "⏰ Sandbox expired. Please refresh the page to start a new session.", ""
112
+ return f"⚠️ Sandbox already running. Time left: {self.get_remaining_time_str()}", self.get_status()
113
+
 
114
  if not self.is_connected:
115
+ return "❌ Not connected to a backend. Please connect first.", ""
116
+
117
  try:
118
  result = self.client.predict(
119
  code=main_py_code,
120
  requirements=requirements_txt,
121
  api_name="/launch_sandbox"
122
  )
 
 
123
  self.sandbox_created = True
124
  self.creation_time = datetime.now()
125
+ self.start_monitor_thread()
126
 
127
+ remaining = self.get_remaining_time_str()
128
+ return (f"🚀 Sandbox deployed successfully on {self.current_engine}\n"
129
+ f"⏱️ Time Limit: {SESSION_TIME_LIMIT_HOURS} hours | Remaining: {remaining}\n"
130
+ f"📝 Response: {result}"), self.get_status()
 
 
 
 
131
  except Exception as e:
132
+ return f"❌ Deployment failed: {str(e)}", ""
133
+
134
  def fetch_logs(self):
135
+ """Fetches the latest logs from the sandbox."""
136
+ if not self.sandbox_created:
137
+ return "[SYSTEM] Sandbox not deployed."
138
+ if self.is_expired():
139
+ return "⏰ Sandbox expired. Logs are no longer available."
140
+
 
141
  try:
142
  result = self.client.predict(api_name="/fetch_logs")
143
  timestamp = datetime.now().strftime("%H:%M:%S")
144
+ remaining = self.get_remaining_time_str()
145
+ return f"[{timestamp}] Remaining: {remaining}\nLogs from {self.current_engine}:\n\n{result}"
146
  except Exception as e:
147
+ return f"[ERROR] Failed to fetch logs: {str(e)}"
148
+
149
  def kill_sandbox(self):
150
+ """Terminates the currently running sandbox."""
151
+ if not self.is_connected or not self.sandbox_created:
152
+ return "❌ No active sandbox to terminate.", ""
153
+
154
  try:
155
  result = self.client.predict(api_name="/kill_sandbox")
156
  self.sandbox_created = False
157
  self.creation_time = None
158
+ self.stop_monitor.set()
159
+ return f"🛑 Sandbox terminated on {self.current_engine}.\n\nResponse: {result}", "TERMINATED"
160
  except Exception as e:
161
+ return f"❌ Termination failed: {str(e)}", self.get_status()
162
+
163
  def get_status(self):
164
+ """Gets the current status of the sandbox."""
165
  if not self.is_connected:
166
  return "OFFLINE"
167
+ if not self.sandbox_created:
168
+ return "IDLE"
169
+ if self.is_expired():
170
  return "EXPIRED"
171
 
172
  try:
173
  result = self.client.predict(api_name="/status_sandbox")
174
+ remaining = self.get_remaining_time_str()
175
+ status_text = str(result).lower()
176
+ if "running" in status_text:
177
+ return f"RUNNING ({remaining}) - {result}"
178
  else:
179
+ return f"IDLE ({remaining}) - {result}"
180
  except Exception as e:
181
+ return f"ERROR - {str(e)}"
182
+
183
+ def start_monitor_thread(self):
184
+ """Starts a background thread to monitor session expiration."""
185
+ self.stop_monitor.clear()
186
  def monitor():
187
+ while not self.stop_monitor.is_set() and self.sandbox_created:
188
+ if self.is_expired():
189
+ print("Sandbox expired. Auto-terminating...")
190
+ self.kill_sandbox()
 
 
191
  break
192
+ time.sleep(30)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
+ self.monitor_thread = threading.Thread(target=monitor, daemon=True)
195
+ self.monitor_thread.start()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
+ # --- Gradio UI & Logic ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
 
199
+ # Initialize manager
200
+ sandbox = SandboxManager()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
 
202
+ # UI helper functions
203
  def load_sample_files():
204
  return SAMPLE_MAIN_PY, SAMPLE_REQUIREMENTS_TXT
205
 
206
  def get_session_info():
207
  if sandbox.sandbox_created:
208
+ remaining = sandbox.get_remaining_time_str()
209
+ created_at = sandbox.creation_time.strftime("%H:%M:%S")
210
+ return f"Created: {created_at} | Remaining: {remaining}"
211
+ return "No active sandbox"
212
+
213
+ # Modern CSS for a professional look
214
+ PROFESSIONAL_CSS = """
215
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
216
+ :root {
217
+ --primary-500: #3b82f6; --primary-600: #2563eb;
218
+ --gray-50: #f9fafb; --gray-100: #f3f4f6; --gray-200: #e5e7eb; --gray-700: #374151; --gray-800: #1f2937; --gray-900: #111827;
219
+ --red-500: #ef4444; --red-600: #dc2626;
220
+ --green-500: #22c55e;
221
+ --amber-500: #f59e0b;
222
+ --font-sans: 'Inter', sans-serif; --font-mono: 'JetBrains Mono', monospace;
223
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
224
+ }
225
+ body, .gradio-container { background-color: var(--gray-50); font-family: var(--font-sans) !important; }
226
+ .card { background-color: white; border-radius: 0.75rem; border: 1px solid var(--gray-200); box-shadow: var(--shadow-sm); margin-bottom: 1.5rem !important; }
227
+ .card-header { padding: 1rem 1.5rem; border-bottom: 1px solid var(--gray-200); font-weight: 600; font-size: 1.125rem; color: var(--gray-800); }
228
+ .card-content { padding: 1.5rem; }
229
+ .form-label { font-weight: 500; color: var(--gray-700); margin-bottom: 0.5rem; font-size: 0.875rem; text-transform: uppercase; letter-spacing: 0.05em; display: block; }
230
+ .btn { border-radius: 0.5rem; font-weight: 500; transition: all 0.2s ease; }
231
+ .btn-primary { background-color: var(--primary-500); color: white; border: none; }
232
+ .btn-primary:hover { background-color: var(--primary-600); transform: translateY(-1px); box-shadow: var(--shadow-md); }
233
+ .btn-secondary { background-color: white; color: var(--gray-700); border: 1px solid var(--gray-200); }
234
+ .btn-secondary:hover { background-color: var(--gray-50); border-color: var(--gray-300); }
235
+ .btn-danger { background-color: var(--red-500); color: white; border: none; }
236
+ .btn-danger:hover { background-color: var(--red-600); transform: translateY(-1px); box-shadow: var(--shadow-md); }
237
+ .status-badge { text-align: center; padding: 0.5rem 1rem; border-radius: 9999px; font-weight: 500; font-size: 0.875rem; border: 1px solid transparent; }
238
+ .status-offline { background-color: #fee2e2; color: #b91c1c; border-color: #fecaca; }
239
+ .status-online { background-color: #dcfce7; color: #166534; border-color: #bbf7d0; }
240
+ .status-idle { background-color: #feefc3; color: #b45309; border-color: #fde68a; }
241
+ .status-running { animation: pulse 2s infinite; background-color: #dbeafe; color: #1e40af; border-color: #bfdbfe; }
242
+ @keyframes pulse { 50% { opacity: 0.7; } }
243
+ .code-editor, .terminal { font-family: var(--font-mono) !important; background-color: var(--gray-900) !important; color: var(--gray-100) !important; border-radius: 0.5rem !important; border: 1px solid var(--gray-700) !important; }
244
+ .header { text-align: center; padding: 2rem 1rem; }
245
+ .header-title { font-size: 2.25rem; font-weight: 700; color: var(--gray-800); }
246
+ .header-subtitle { font-size: 1rem; color: var(--gray-700); max-width: 600px; margin: 0.5rem auto 0; }
247
+ """
248
 
249
+ with gr.Blocks(css=PROFESSIONAL_CSS, title="Sandbox Compute Platform") as demo:
250
+ # --- Header ---
 
 
251
  gr.HTML("""
252
+ <div class="header">
253
+ <h1 class="header-title">🚀 Sandbox Compute Platform</h1>
254
+ <p class="header-subtitle">An enterprise-grade cloud development environment with secure, time-limited sessions for rapid prototyping and execution.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  </div>
256
  """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
 
258
+ with gr.Row():
259
+ # --- Left Column: Configuration & Actions ---
260
+ with gr.Column(scale=3):
261
+ # Connection Card
262
+ with gr.Group(elem_classes="card"):
263
+ gr.HTML('<div class="card-header">1. Connection</div>')
264
+ with gr.Box(elem_classes="card-content"):
265
  with gr.Row():
266
+ connect_btn = gr.Button("🔗 Connect to Backend", elem_classes=["btn", "btn-primary"])
267
+ load_samples_btn = gr.Button("📄 Load Templates", elem_classes=["btn", "btn-secondary"])
268
+ connection_status = gr.Textbox(value="DISCONNECTED", label="Connection Status", interactive=False, elem_classes=["status-badge", "status-offline"])
269
+
270
+ # Environment Card
271
+ with gr.Group(elem_classes="card"):
272
+ gr.HTML('<div class="card-header">2. Environment Setup</div>')
273
+ with gr.Box(elem_classes="card-content"):
274
+ with gr.Tabs():
275
+ with gr.TabItem("main.py"):
276
+ main_py_editor = gr.Code(value=SAMPLE_MAIN_PY, language="python", label="Application Source Code", lines=15, elem_classes="code-editor")
277
+ with gr.TabItem("requirements.txt"):
278
+ requirements_editor = gr.Code(value=SAMPLE_REQUIREMENTS_TXT, language="text", label="Dependencies", lines=15, elem_classes="code-editor")
279
+
280
+ # Deployment Card
281
+ with gr.Group(elem_classes="card"):
282
+ gr.HTML('<div class="card-header">3. Deployment</div>')
283
+ with gr.Box(elem_classes="card-content"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  with gr.Row():
285
+ launch_btn = gr.Button("🚀 Deploy Sandbox", elem_classes=["btn", "btn-primary"], scale=2)
286
+ kill_btn = gr.Button("🛑 Terminate", elem_classes=["btn", "btn-danger"])
287
+ launch_output = gr.Textbox(label="Deployment Output", lines=4, interactive=False, placeholder="Deployment logs will appear here...", elem_classes="terminal")
288
+
289
+ # --- Right Column: Monitoring ---
290
+ with gr.Column(scale=2):
291
+ # Status Card
292
+ with gr.Group(elem_classes="card"):
293
+ gr.HTML('<div class="card-header">System Monitor</div>')
294
+ with gr.Box(elem_classes="card-content"):
295
+ gr.HTML('<p class="form-label">Instance Status</p>')
296
+ status_display = gr.Textbox(value="OFFLINE", interactive=False, show_label=False, elem_classes=["status-badge", "status-offline"])
 
 
 
 
 
 
 
 
 
 
 
 
297
 
298
+ gr.HTML('<p class="form-label" style="margin-top: 1.5rem;">Active Engine</p>')
299
+ current_engine_display = gr.Textbox(value="None", interactive=False, show_label=False)
 
 
300
 
301
+ gr.HTML('<p class="form-label" style="margin-top: 1.5rem;">Session Info</p>')
302
+ session_info = gr.Textbox(value="No active sandbox", interactive=False, show_label=False)
 
 
 
 
 
 
 
 
 
303
 
304
+ # Logs Card
305
+ with gr.Group(elem_classes="card"):
306
+ gr.HTML('<div class="card-header">Application Logs</div>')
307
+ with gr.Box(elem_classes="card-content"):
308
+ fetch_logs_btn = gr.Button("🔄 Refresh Logs", elem_classes=["btn", "btn-secondary"])
309
+ logs_display = gr.Textbox(lines=18, interactive=False, placeholder="[SYSTEM] Application logs will appear here...", show_label=False, elem_classes="terminal")
310
+
311
+ # --- Event Handlers & Logic ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
 
313
+ # Connection Logic
314
+ def handle_connect():
315
+ status_msg, engine_name = sandbox.connect()
316
+ if sandbox.is_connected:
317
+ return {
318
+ connection_status: gr.update(value=status_msg, elem_classes=["status-badge", "status-online"]),
319
+ current_engine_display: gr.update(value=engine_name)
320
+ }
321
+ else:
322
+ return {
323
+ connection_status: gr.update(value=status_msg, elem_classes=["status-badge", "status-offline"]),
324
+ current_engine_display: gr.update(value=engine_name)
325
+ }
326
+
327
+ connect_btn.click(fn=handle_connect, outputs=[connection_status, current_engine_display])
328
 
329
+ # Sample Loading
330
+ load_samples_btn.click(fn=load_sample_files, outputs=[main_py_editor, requirements_editor])
331
+
332
+ # Launch Logic
333
+ def handle_launch(main_py, reqs_txt):
334
+ output, status = sandbox.launch_sandbox(main_py, reqs_txt)
335
+ return output, status, get_session_info()
336
+
337
  launch_btn.click(
338
+ fn=handle_launch,
339
  inputs=[main_py_editor, requirements_editor],
340
+ outputs=[launch_output, status_display, session_info]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
  )
342
 
343
+ # Kill Logic
344
+ def handle_kill():
345
+ output, status = sandbox.kill_sandbox()
346
+ return output, status, get_session_info()
347
+
348
+ kill_btn.click(fn=handle_kill, outputs=[launch_output, status_display, session_info])
349
+
350
+ # Log Fetching
351
+ fetch_logs_btn.click(fn=sandbox.fetch_logs, outputs=logs_display)
352
 
353
+ # Automatic Refresh Logic
354
+ def update_status_and_logs():
355
+ status = sandbox.get_status()
356
+ logs = sandbox.fetch_logs() if sandbox.sandbox_created else "[SYSTEM] Auto-refresh paused. Deploy a sandbox to see logs."
357
+ s_info = get_session_info()
358
+
359
+ status_class = "status-offline"
360
+ if "RUNNING" in status: status_class = "status-running"
361
+ elif "IDLE" in status: status_class = "status-idle"
362
+ elif "EXPIRED" in status: status_class = "status-offline"
363
+
364
+ return {
365
+ status_display: gr.update(value=status, elem_classes=["status-badge", status_class]),
366
+ logs_display: logs,
367
+ session_info: s_info
368
+ }
369
+
370
  demo.load(
371
+ fn=update_status_and_logs,
372
+ outputs=[status_display, logs_display, session_info],
373
+ every=15 # Refresh every 15 seconds
374
  )
375
 
376
+ # --- Launch Application ---
377
  if __name__ == "__main__":
378
+ demo.launch(server_name="0.0.0.0", server_port=7860, debug=False)