DevEnv / app.py
ayoub5550's picture
Upload app.py with huggingface_hub
70db9bb verified
Raw
History Blame Contribute Delete
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>
"""
@app.route("/")
def dashboard():
return render_template_string(DASHBOARD_HTML)
@app.route("/api/health")
def health():
return jsonify({"status": "ok", "service": "devenv", "tools": ["bash", "python", "node", "git", "chromium"]})
@app.route("/api/exec", methods=["POST"])
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)})
@app.route("/api/read", methods=["POST"])
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)})
@app.route("/api/write", methods=["POST"])
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)})
@app.route("/api/browse", methods=["POST"])
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)