Initial import: WebAisMap
Closes TG-4 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+92
@@ -0,0 +1,92 @@
|
||||
import os
|
||||
import json
|
||||
import struct
|
||||
import signal
|
||||
import select
|
||||
import threading
|
||||
|
||||
|
||||
def _pty_set_winsize(fd, rows, cols):
|
||||
import fcntl
|
||||
import termios
|
||||
|
||||
winsz = struct.pack("HHHH", int(rows), int(cols), 0, 0)
|
||||
fcntl.ioctl(fd, termios.TIOCSWINSZ, winsz)
|
||||
|
||||
|
||||
def terminal_session(ws):
|
||||
"""PTY + bash, обмен через WebSocket (как ttyd, но на том же порту что и Flask)."""
|
||||
import pty
|
||||
|
||||
pid, master_fd = pty.fork()
|
||||
if pid == 0:
|
||||
os.environ.setdefault("TERM", "xterm-256color")
|
||||
os.environ.setdefault("SHELL", "/bin/bash")
|
||||
try:
|
||||
os.execvp("/bin/bash", ["/bin/bash"])
|
||||
except OSError:
|
||||
os.execvp("/bin/sh", ["/bin/sh"])
|
||||
os._exit(127)
|
||||
|
||||
relay_done = threading.Event()
|
||||
ws_lock = threading.Lock()
|
||||
|
||||
def relay_out():
|
||||
try:
|
||||
while not relay_done.is_set():
|
||||
r, _, _ = select.select([master_fd], [], [], 0.25)
|
||||
if master_fd not in r:
|
||||
continue
|
||||
try:
|
||||
chunk = os.read(master_fd, 65536)
|
||||
except OSError:
|
||||
break
|
||||
if not chunk:
|
||||
break
|
||||
try:
|
||||
with ws_lock:
|
||||
ws.send(chunk)
|
||||
except Exception:
|
||||
break
|
||||
finally:
|
||||
relay_done.set()
|
||||
|
||||
t = threading.Thread(target=relay_out, daemon=True)
|
||||
t.start()
|
||||
|
||||
try:
|
||||
while True:
|
||||
msg = ws.receive()
|
||||
if msg is None:
|
||||
break
|
||||
if isinstance(msg, str) and msg.startswith("{"):
|
||||
try:
|
||||
j = json.loads(msg)
|
||||
if j.get("type") == "resize":
|
||||
r, c = int(j.get("rows") or 24), int(j.get("cols") or 80)
|
||||
if r > 0 and c > 0:
|
||||
_pty_set_winsize(master_fd, r, c)
|
||||
continue
|
||||
except (json.JSONDecodeError, TypeError, ValueError, KeyError):
|
||||
pass
|
||||
if isinstance(msg, str):
|
||||
msg = msg.encode("utf-8", errors="replace")
|
||||
try:
|
||||
os.write(master_fd, msg)
|
||||
except OSError:
|
||||
break
|
||||
finally:
|
||||
relay_done.set()
|
||||
try:
|
||||
os.close(master_fd)
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
try:
|
||||
os.waitpid(pid, 0)
|
||||
except ChildProcessError:
|
||||
pass
|
||||
t.join(timeout=1.5)
|
||||
Reference in New Issue
Block a user