ayoub5550 commited on
Commit
70db9bb
Β·
verified Β·
1 Parent(s): 46d71bf

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +198 -0
app.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ DevEnv - Development Environment API for AI Agent
3
+ Provides: command execution, file operations, web scraping, health checks
4
+ """
5
+ import subprocess
6
+ import base64
7
+ import os
8
+ import json
9
+ import tempfile
10
+ from flask import Flask, request, jsonify, render_template_string
11
+
12
+ app = Flask(__name__)
13
+ API_TOKEN = os.environ.get("API_TOKEN", "2004")
14
+ WORKSPACE = "/home/user/workspace"
15
+
16
+ def check_auth():
17
+ auth = request.headers.get("Authorization", "")
18
+ if auth == f"Bearer {API_TOKEN}":
19
+ return True
20
+ token = request.args.get("token", "")
21
+ if token == API_TOKEN:
22
+ return True
23
+ return False
24
+
25
+ # ── Dashboard ──
26
+ DASHBOARD_HTML = """
27
+ <!DOCTYPE html>
28
+ <html>
29
+ <head>
30
+ <title>πŸ–₯️ DevEnv - Agent Development Environment</title>
31
+ <meta charset="utf-8">
32
+ <style>
33
+ * { margin: 0; padding: 0; box-sizing: border-box; }
34
+ body { font-family: 'Segoe UI', sans-serif; background: #0d1117; color: #c9d1d9; }
35
+ .header { background: #161b22; padding: 20px 30px; border-bottom: 1px solid #30363d; }
36
+ .header h1 { color: #58a6ff; font-size: 24px; }
37
+ .header p { color: #8b949e; margin-top: 5px; }
38
+ .container { max-width: 900px; margin: 30px auto; padding: 0 20px; }
39
+ .card { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 20px; margin-bottom: 20px; }
40
+ .card h2 { color: #58a6ff; margin-bottom: 10px; font-size: 18px; }
41
+ .status { display: inline-block; padding: 4px 12px; border-radius: 20px; font-size: 13px; font-weight: 600; }
42
+ .status.ok { background: #238636; color: white; }
43
+ .terminal { background: #0d1117; border: 1px solid #30363d; border-radius: 6px; padding: 15px; margin-top: 10px; }
44
+ .terminal input { width: 80%; background: transparent; border: none; color: #c9d1d9; font-family: monospace; font-size: 14px; outline: none; }
45
+ .terminal button { background: #238636; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; }
46
+ .terminal pre { margin-top: 10px; color: #8b949e; font-size: 13px; white-space: pre-wrap; max-height: 300px; overflow-y: auto; }
47
+ .api-list code { background: #0d1117; padding: 3px 8px; border-radius: 4px; font-size: 13px; color: #79c0ff; }
48
+ .api-list li { margin: 8px 0; line-height: 1.6; }
49
+ .badge { display: inline-block; background: #1f6feb; color: white; padding: 2px 8px; border-radius: 4px; font-size: 11px; margin-right: 5px; }
50
+ </style>
51
+ </head>
52
+ <body>
53
+ <div class="header">
54
+ <h1>πŸ–₯️ DevEnv β€” Agent Development Environment</h1>
55
+ <p>Linux development environment with API control for AI agents</p>
56
+ </div>
57
+ <div class="container">
58
+ <div class="card">
59
+ <h2>πŸ“Š Status</h2>
60
+ <span class="status ok">● Online</span>
61
+ <p style="margin-top:10px; color:#8b949e">Ubuntu 22.04 β€’ Python 3 β€’ Node.js β€’ Git β€’ Chromium</p>
62
+ </div>
63
+ <div class="card">
64
+ <h2>πŸ’» Terminal</h2>
65
+ <div class="terminal">
66
+ <span style="color:#238636">$</span>
67
+ <input type="text" id="cmd" placeholder="Type a command..." onkeypress="if(event.key==='Enter')runCmd()">
68
+ <button onclick="runCmd()">Run</button>
69
+ <pre id="output">Ready.</pre>
70
+ </div>
71
+ </div>
72
+ <div class="card">
73
+ <h2>πŸ”Œ API Endpoints</h2>
74
+ <ul class="api-list">
75
+ <li><span class="badge">POST</span> <code>/api/exec</code> β€” Execute shell commands</li>
76
+ <li><span class="badge">POST</span> <code>/api/read</code> β€” Read file contents</li>
77
+ <li><span class="badge">POST</span> <code>/api/write</code> β€” Write/create files</li>
78
+ <li><span class="badge">POST</span> <code>/api/browse</code> β€” Fetch web page content</li>
79
+ <li><span class="badge">GET</span> <code>/api/health</code> β€” Health check</li>
80
+ </ul>
81
+ </div>
82
+ </div>
83
+ <script>
84
+ async function runCmd() {
85
+ const cmd = document.getElementById('cmd').value;
86
+ const out = document.getElementById('output');
87
+ out.textContent = 'Running...';
88
+ try {
89
+ const r = await fetch('/api/exec', {
90
+ method: 'POST',
91
+ headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer """ + API_TOKEN + """'},
92
+ body: JSON.stringify({command: cmd})
93
+ });
94
+ const d = await r.json();
95
+ out.textContent = (d.stdout || '') + (d.stderr ? '\\nSTDERR: ' + d.stderr : '');
96
+ if (!out.textContent.trim()) out.textContent = '(no output)';
97
+ } catch(e) { out.textContent = 'Error: ' + e; }
98
+ }
99
+ </script>
100
+ </body>
101
+ </html>
102
+ """
103
+
104
+ @app.route("/")
105
+ def dashboard():
106
+ return render_template_string(DASHBOARD_HTML)
107
+
108
+ @app.route("/api/health")
109
+ def health():
110
+ return jsonify({"status": "ok", "service": "devenv", "tools": ["bash", "python", "node", "git", "chromium"]})
111
+
112
+ @app.route("/api/exec", methods=["POST"])
113
+ def exec_cmd():
114
+ if not check_auth():
115
+ return jsonify({"error": "unauthorized"}), 401
116
+ data = request.get_json() or {}
117
+ cmd = data.get("command", "echo hello")
118
+ timeout = min(data.get("timeout", 30), 120)
119
+ cwd = data.get("cwd", WORKSPACE)
120
+ try:
121
+ result = subprocess.run(
122
+ cmd, shell=True, capture_output=True, text=True,
123
+ timeout=timeout, cwd=cwd,
124
+ env={**os.environ, "HOME": "/home/user"}
125
+ )
126
+ return jsonify({
127
+ "stdout": result.stdout[-10000:],
128
+ "stderr": result.stderr[-3000:],
129
+ "returncode": result.returncode
130
+ })
131
+ except subprocess.TimeoutExpired:
132
+ return jsonify({"error": "timeout", "stdout": "", "stderr": f"Command timed out after {timeout}s"})
133
+ except Exception as e:
134
+ return jsonify({"error": str(e)})
135
+
136
+ @app.route("/api/read", methods=["POST"])
137
+ def read_file():
138
+ if not check_auth():
139
+ return jsonify({"error": "unauthorized"}), 401
140
+ data = request.get_json() or {}
141
+ path = data.get("path", "")
142
+ if not path:
143
+ return jsonify({"error": "path required"})
144
+ try:
145
+ with open(path, "r") as f:
146
+ content = f.read(100000) # 100KB max
147
+ return jsonify({"content": content, "path": path})
148
+ except Exception as e:
149
+ return jsonify({"error": str(e)})
150
+
151
+ @app.route("/api/write", methods=["POST"])
152
+ def write_file():
153
+ if not check_auth():
154
+ return jsonify({"error": "unauthorized"}), 401
155
+ data = request.get_json() or {}
156
+ path = data.get("path", "")
157
+ content = data.get("content", "")
158
+ if not path:
159
+ return jsonify({"error": "path required"})
160
+ try:
161
+ os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
162
+ with open(path, "w") as f:
163
+ f.write(content)
164
+ return jsonify({"status": "ok", "path": path, "bytes": len(content)})
165
+ except Exception as e:
166
+ return jsonify({"error": str(e)})
167
+
168
+ @app.route("/api/browse", methods=["POST"])
169
+ def browse():
170
+ if not check_auth():
171
+ return jsonify({"error": "unauthorized"}), 401
172
+ data = request.get_json() or {}
173
+ url = data.get("url", "")
174
+ if not url:
175
+ return jsonify({"error": "url required"})
176
+ try:
177
+ import requests as req
178
+ r = req.get(url, timeout=15, headers={"User-Agent": "Mozilla/5.0"})
179
+ from bs4 import BeautifulSoup
180
+ soup = BeautifulSoup(r.text, "html.parser")
181
+ # Remove scripts and styles
182
+ for s in soup(["script", "style"]):
183
+ s.decompose()
184
+ text = soup.get_text(separator="\n", strip=True)
185
+ return jsonify({
186
+ "url": url,
187
+ "status_code": r.status_code,
188
+ "title": soup.title.string if soup.title else "",
189
+ "text": text[:15000],
190
+ "html_length": len(r.text)
191
+ })
192
+ except Exception as e:
193
+ return jsonify({"error": str(e)})
194
+
195
+ if __name__ == "__main__":
196
+ os.makedirs(WORKSPACE, exist_ok=True)
197
+ print("πŸ–₯️ DevEnv API starting on port 7860...")
198
+ app.run(host="0.0.0.0", port=7860)