Spaces:
Sleeping
Sleeping
Upload app.py with huggingface_hub
Browse files
app.py
CHANGED
|
@@ -1,5 +1,9 @@
|
|
| 1 |
import os
|
|
|
|
| 2 |
import asyncio
|
|
|
|
|
|
|
|
|
|
| 3 |
import json
|
| 4 |
import subprocess
|
| 5 |
|
|
@@ -10,8 +14,6 @@ from fastapi.staticfiles import StaticFiles
|
|
| 10 |
app = FastAPI()
|
| 11 |
app.mount("/static", StaticFiles(directory="static"), name="static")
|
| 12 |
|
| 13 |
-
SESSION_NAME = "main"
|
| 14 |
-
|
| 15 |
@app.get("/")
|
| 16 |
async def index():
|
| 17 |
return FileResponse("static/index.html")
|
|
@@ -20,55 +22,64 @@ async def index():
|
|
| 20 |
async def get_pass():
|
| 21 |
return {"pass": os.environ.get("TERMINAL_PASS", "")}
|
| 22 |
|
| 23 |
-
def ensure_session():
|
| 24 |
-
result = subprocess.run(
|
| 25 |
-
["tmux", "has-session", "-t", SESSION_NAME],
|
| 26 |
-
capture_output=True
|
| 27 |
-
)
|
| 28 |
-
if result.returncode != 0:
|
| 29 |
-
subprocess.run([
|
| 30 |
-
"tmux", "new-session", "-d", "-s", SESSION_NAME,
|
| 31 |
-
"-x", "200", "-y", "50"
|
| 32 |
-
])
|
| 33 |
-
|
| 34 |
@app.websocket("/ws")
|
| 35 |
async def terminal(websocket: WebSocket):
|
| 36 |
await websocket.accept()
|
| 37 |
-
|
|
|
|
| 38 |
|
| 39 |
proc = await asyncio.create_subprocess_exec(
|
| 40 |
-
"
|
| 41 |
-
stdin=
|
| 42 |
-
stdout=
|
| 43 |
-
stderr=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
)
|
| 45 |
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
while True:
|
| 48 |
try:
|
| 49 |
-
data = await
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
break
|
| 55 |
|
| 56 |
-
read_task = asyncio.create_task(
|
| 57 |
|
| 58 |
try:
|
| 59 |
while True:
|
| 60 |
message = await websocket.receive_text()
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
| 72 |
except (WebSocketDisconnect, Exception):
|
| 73 |
pass
|
| 74 |
finally:
|
|
@@ -76,5 +87,9 @@ async def terminal(websocket: WebSocket):
|
|
| 76 |
try:
|
| 77 |
proc.terminate()
|
| 78 |
await asyncio.wait_for(proc.wait(), timeout=2)
|
| 79 |
-
except:
|
| 80 |
proc.kill()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
+
import pty
|
| 3 |
import asyncio
|
| 4 |
+
import struct
|
| 5 |
+
import fcntl
|
| 6 |
+
import termios
|
| 7 |
import json
|
| 8 |
import subprocess
|
| 9 |
|
|
|
|
| 14 |
app = FastAPI()
|
| 15 |
app.mount("/static", StaticFiles(directory="static"), name="static")
|
| 16 |
|
|
|
|
|
|
|
| 17 |
@app.get("/")
|
| 18 |
async def index():
|
| 19 |
return FileResponse("static/index.html")
|
|
|
|
| 22 |
async def get_pass():
|
| 23 |
return {"pass": os.environ.get("TERMINAL_PASS", "")}
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
@app.websocket("/ws")
|
| 26 |
async def terminal(websocket: WebSocket):
|
| 27 |
await websocket.accept()
|
| 28 |
+
|
| 29 |
+
master_fd, slave_fd = pty.openpty()
|
| 30 |
|
| 31 |
proc = await asyncio.create_subprocess_exec(
|
| 32 |
+
"/bin/bash",
|
| 33 |
+
stdin=slave_fd,
|
| 34 |
+
stdout=slave_fd,
|
| 35 |
+
stderr=slave_fd,
|
| 36 |
+
env={
|
| 37 |
+
**os.environ,
|
| 38 |
+
"TERM": "xterm-256color",
|
| 39 |
+
"HOME": "/persistent",
|
| 40 |
+
"USER": "ubuntu"
|
| 41 |
+
}
|
| 42 |
)
|
| 43 |
|
| 44 |
+
os.close(slave_fd)
|
| 45 |
+
|
| 46 |
+
loop = asyncio.get_event_loop()
|
| 47 |
+
|
| 48 |
+
async def read_pty():
|
| 49 |
while True:
|
| 50 |
try:
|
| 51 |
+
data = await loop.run_in_executor(
|
| 52 |
+
None,
|
| 53 |
+
lambda: os.read(master_fd, 4096)
|
| 54 |
+
)
|
| 55 |
+
if data:
|
| 56 |
+
try:
|
| 57 |
+
text = data.decode("utf-8")
|
| 58 |
+
except UnicodeDecodeError:
|
| 59 |
+
text = data.decode("utf-8", errors="ignore")
|
| 60 |
+
await websocket.send_text(text)
|
| 61 |
+
except OSError:
|
| 62 |
break
|
| 63 |
|
| 64 |
+
read_task = asyncio.create_task(read_pty())
|
| 65 |
|
| 66 |
try:
|
| 67 |
while True:
|
| 68 |
message = await websocket.receive_text()
|
| 69 |
+
try:
|
| 70 |
+
msg = json.loads(message)
|
| 71 |
+
if msg.get("type") == "input":
|
| 72 |
+
os.write(master_fd, msg["data"].encode("utf-8"))
|
| 73 |
+
elif msg.get("type") == "resize":
|
| 74 |
+
cols = msg.get("cols", 80)
|
| 75 |
+
rows = msg.get("rows", 24)
|
| 76 |
+
fcntl.ioctl(
|
| 77 |
+
master_fd,
|
| 78 |
+
termios.TIOCSWINSZ,
|
| 79 |
+
struct.pack("HHHH", rows, cols, 0, 0)
|
| 80 |
+
)
|
| 81 |
+
except (json.JSONDecodeError, OSError):
|
| 82 |
+
break
|
| 83 |
except (WebSocketDisconnect, Exception):
|
| 84 |
pass
|
| 85 |
finally:
|
|
|
|
| 87 |
try:
|
| 88 |
proc.terminate()
|
| 89 |
await asyncio.wait_for(proc.wait(), timeout=2)
|
| 90 |
+
except Exception:
|
| 91 |
proc.kill()
|
| 92 |
+
try:
|
| 93 |
+
os.close(master_fd)
|
| 94 |
+
except:
|
| 95 |
+
pass
|