Download app.py from ayoub5550/DevEnv: direct link, hf CLI and curl.
- Browser
- Download file 8.14 kB
-
https://huggingface.co/spaces/ayoub5550/DevEnv/resolve/main/app.py
- Command line
-
hf download hf://spaces/ayoub5550/DevEnv/app.py
-
curl -L -o app.py https://huggingface.co/spaces/ayoub5550/DevEnv/resolve/main/app.py
8.14 kB
| """ | |
| DevEnv - Development Environment API for AI Agent | |
| Provides: command execution, file operations, web scraping, health checks | |
| """ | |
| import subprocess | |
| import base64 | |
| import os | |
| import json | |
| import tempfile | |
| from flask import Flask, request, jsonify, render_template_string | |
| app = Flask(__name__) | |
| API_TOKEN = os.environ.get("API_TOKEN", "2004") | |
| WORKSPACE = "/home/user/workspace" | |
| def check_auth(): | |
| auth = request.headers.get("Authorization", "") | |
| if auth == f"Bearer {API_TOKEN}": | |
| return True | |
| token = request.args.get("token", "") | |
| if token == API_TOKEN: | |
| return True | |
| return False | |
| # ββ Dashboard ββ | |
| DASHBOARD_HTML = """ | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>π₯οΈ DevEnv - Agent Development Environment</title> | |
| <meta charset="utf-8"> | |
| <style> | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { font-family: 'Segoe UI', sans-serif; background: #0d1117; color: #c9d1d9; } | |
| .header { background: #161b22; padding: 20px 30px; border-bottom: 1px solid #30363d; } | |
| .header h1 { color: #58a6ff; font-size: 24px; } | |
| .header p { color: #8b949e; margin-top: 5px; } | |
| .container { max-width: 900px; margin: 30px auto; padding: 0 20px; } | |
| .card { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 20px; margin-bottom: 20px; } | |
| .card h2 { color: #58a6ff; margin-bottom: 10px; font-size: 18px; } | |
| .status { display: inline-block; padding: 4px 12px; border-radius: 20px; font-size: 13px; font-weight: 600; } | |
| .status.ok { background: #238636; color: white; } | |
| .terminal { background: #0d1117; border: 1px solid #30363d; border-radius: 6px; padding: 15px; margin-top: 10px; } | |
| .terminal input { width: 80%; background: transparent; border: none; color: #c9d1d9; font-family: monospace; font-size: 14px; outline: none; } | |
| .terminal button { background: #238636; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; } | |
| .terminal pre { margin-top: 10px; color: #8b949e; font-size: 13px; white-space: pre-wrap; max-height: 300px; overflow-y: auto; } | |
| .api-list code { background: #0d1117; padding: 3px 8px; border-radius: 4px; font-size: 13px; color: #79c0ff; } | |
| .api-list li { margin: 8px 0; line-height: 1.6; } | |
| .badge { display: inline-block; background: #1f6feb; color: white; padding: 2px 8px; border-radius: 4px; font-size: 11px; margin-right: 5px; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="header"> | |
| <h1>π₯οΈ DevEnv β Agent Development Environment</h1> | |
| <p>Linux development environment with API control for AI agents</p> | |
| </div> | |
| <div class="container"> | |
| <div class="card"> | |
| <h2>π Status</h2> | |
| <span class="status ok">β Online</span> | |
| <p style="margin-top:10px; color:#8b949e">Ubuntu 22.04 β’ Python 3 β’ Node.js β’ Git β’ Chromium</p> | |
| </div> | |
| <div class="card"> | |
| <h2>π» Terminal</h2> | |
| <div class="terminal"> | |
| <span style="color:#238636">$</span> | |
| <input type="text" id="cmd" placeholder="Type a command..." onkeypress="if(event.key==='Enter')runCmd()"> | |
| <button onclick="runCmd()">Run</button> | |
| <pre id="output">Ready.</pre> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <h2>π API Endpoints</h2> | |
| <ul class="api-list"> | |
| <li><span class="badge">POST</span> <code>/api/exec</code> β Execute shell commands</li> | |
| <li><span class="badge">POST</span> <code>/api/read</code> β Read file contents</li> | |
| <li><span class="badge">POST</span> <code>/api/write</code> β Write/create files</li> | |
| <li><span class="badge">POST</span> <code>/api/browse</code> β Fetch web page content</li> | |
| <li><span class="badge">GET</span> <code>/api/health</code> β Health check</li> | |
| </ul> | |
| </div> | |
| </div> | |
| <script> | |
| async function runCmd() { | |
| const cmd = document.getElementById('cmd').value; | |
| const out = document.getElementById('output'); | |
| out.textContent = 'Running...'; | |
| try { | |
| const r = await fetch('/api/exec', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer """ + API_TOKEN + """'}, | |
| body: JSON.stringify({command: cmd}) | |
| }); | |
| const d = await r.json(); | |
| out.textContent = (d.stdout || '') + (d.stderr ? '\\nSTDERR: ' + d.stderr : ''); | |
| if (!out.textContent.trim()) out.textContent = '(no output)'; | |
| } catch(e) { out.textContent = 'Error: ' + e; } | |
| } | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| def dashboard(): | |
| return render_template_string(DASHBOARD_HTML) | |
| def health(): | |
| return jsonify({"status": "ok", "service": "devenv", "tools": ["bash", "python", "node", "git", "chromium"]}) | |
| def exec_cmd(): | |
| if not check_auth(): | |
| return jsonify({"error": "unauthorized"}), 401 | |
| data = request.get_json() or {} | |
| cmd = data.get("command", "echo hello") | |
| timeout = min(data.get("timeout", 30), 120) | |
| cwd = data.get("cwd", WORKSPACE) | |
| try: | |
| result = subprocess.run( | |
| cmd, shell=True, capture_output=True, text=True, | |
| timeout=timeout, cwd=cwd, | |
| env={**os.environ, "HOME": "/home/user"} | |
| ) | |
| return jsonify({ | |
| "stdout": result.stdout[-10000:], | |
| "stderr": result.stderr[-3000:], | |
| "returncode": result.returncode | |
| }) | |
| except subprocess.TimeoutExpired: | |
| return jsonify({"error": "timeout", "stdout": "", "stderr": f"Command timed out after {timeout}s"}) | |
| except Exception as e: | |
| return jsonify({"error": str(e)}) | |
| def read_file(): | |
| if not check_auth(): | |
| return jsonify({"error": "unauthorized"}), 401 | |
| data = request.get_json() or {} | |
| path = data.get("path", "") | |
| if not path: | |
| return jsonify({"error": "path required"}) | |
| try: | |
| with open(path, "r") as f: | |
| content = f.read(100000) # 100KB max | |
| return jsonify({"content": content, "path": path}) | |
| except Exception as e: | |
| return jsonify({"error": str(e)}) | |
| def write_file(): | |
| if not check_auth(): | |
| return jsonify({"error": "unauthorized"}), 401 | |
| data = request.get_json() or {} | |
| path = data.get("path", "") | |
| content = data.get("content", "") | |
| if not path: | |
| return jsonify({"error": "path required"}) | |
| try: | |
| os.makedirs(os.path.dirname(path) or ".", exist_ok=True) | |
| with open(path, "w") as f: | |
| f.write(content) | |
| return jsonify({"status": "ok", "path": path, "bytes": len(content)}) | |
| except Exception as e: | |
| return jsonify({"error": str(e)}) | |
| def browse(): | |
| if not check_auth(): | |
| return jsonify({"error": "unauthorized"}), 401 | |
| data = request.get_json() or {} | |
| url = data.get("url", "") | |
| if not url: | |
| return jsonify({"error": "url required"}) | |
| try: | |
| import requests as req | |
| r = req.get(url, timeout=15, headers={"User-Agent": "Mozilla/5.0"}) | |
| from bs4 import BeautifulSoup | |
| soup = BeautifulSoup(r.text, "html.parser") | |
| # Remove scripts and styles | |
| for s in soup(["script", "style"]): | |
| s.decompose() | |
| text = soup.get_text(separator="\n", strip=True) | |
| return jsonify({ | |
| "url": url, | |
| "status_code": r.status_code, | |
| "title": soup.title.string if soup.title else "", | |
| "text": text[:15000], | |
| "html_length": len(r.text) | |
| }) | |
| except Exception as e: | |
| return jsonify({"error": str(e)}) | |
| if __name__ == "__main__": | |
| os.makedirs(WORKSPACE, exist_ok=True) | |
| print("π₯οΈ DevEnv API starting on port 7860...") | |
| app.run(host="0.0.0.0", port=7860) | |