| from fastapi import FastAPI, HTTPException, Query, File, UploadFile |
| from fastapi.responses import PlainTextResponse, HTMLResponse |
| import subprocess |
| import os |
| import shutil |
|
|
| app = FastAPI() |
| print("--- TERM-X PRO WEBUI STARTING ---") |
|
|
| |
| SECURITY_TOKEN = os.getenv("SECURITY_TOKEN", "a") |
|
|
| @app.get("/") |
| def read_root(): |
| return {"status": "online", "message": "TermX PRO is running"} |
|
|
| @app.get("/termx", response_class=PlainTextResponse) |
| def run_command(cmd: str = Query(...), token: str = Query(...)): |
| if token != SECURITY_TOKEN: |
| raise HTTPException(status_code=403, detail="Invalid Token!") |
| |
| |
| nvm_prefix = 'export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && ' |
| |
| claude_env = 'export ANTHROPIC_API_KEY="a" && export ANTHROPIC_BASE_URL="https://azils-anti-supa.hf.space/v1" && ' |
| |
| full_cmd = nvm_prefix + claude_env + cmd |
|
|
| try: |
| |
| result = subprocess.run(full_cmd, shell=True, capture_output=True, text=True, timeout=30) |
| output = result.stdout if result.stdout else result.stderr |
| return output if output else "Command executed (No output)." |
| except subprocess.TimeoutExpired: |
| return "Error: Command timed out after 30 seconds." |
| except Exception as e: |
| return f"Error: {str(e)}" |
|
|
| @app.post("/upload") |
| async def upload_file(token: str = Query(...), file: UploadFile = File(...)): |
| if token != SECURITY_TOKEN: |
| raise HTTPException(status_code=403, detail="Invalid Token!") |
| try: |
| file_path = os.path.join(os.getcwd(), file.filename) |
| with open(file_path, "wb") as buffer: |
| shutil.copyfileobj(file.file, buffer) |
| return {"message": f"Successfully uploaded {file.filename}"} |
| except Exception as e: |
| return {"error": str(e)} |
|
|
| @app.get("/openweb", response_class=HTMLResponse) |
| def open_web(token: str = Query(...)): |
| if token != SECURITY_TOKEN: |
| raise HTTPException(status_code=403, detail="Invalid Token!") |
|
|
| return """<!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>TermX Pro - Terminal & Chat</title> |
| <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&family=Inter:wght@400;600&display=swap" rel="stylesheet"> |
| <style> |
| :root { --bg: #0d1117; --card: #161b22; --text: #c9d1d9; --primary: #58a6ff; --accent: #238636; } |
| body { background: var(--bg); color: var(--text); font-family: 'Inter', sans-serif; margin: 0; display: flex; flex-direction: column; height: 100vh; } |
| |
| /* Header */ |
| header { background: var(--card); padding: 15px 20px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #30363d; } |
| .logo { font-weight: 600; color: var(--primary); font-size: 1.2rem; } |
| |
| /* Main Layout */ |
| .main-container { display: flex; flex: 1; overflow: hidden; padding: 10px; gap: 10px; } |
| |
| /* Terminal Section */ |
| .panel { flex: 1; background: var(--card); border-radius: 12px; display: flex; flex-direction: column; border: 1px solid #30363d; } |
| .panel-header { padding: 10px 15px; background: #21262d; border-radius: 12px 12px 0 0; font-size: 0.9rem; font-weight: 600; } |
| |
| #terminal-output { flex: 1; padding: 15px; overflow-y: auto; font-family: 'Fira Code', monospace; font-size: 13px; white-space: pre-wrap; color: #7ee787; } |
| |
| /* Chat Section */ |
| #chat-container { flex: 1; display: flex; flex-direction: column; } |
| #chat-messages { flex: 1; padding: 15px; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; } |
| .msg { max-width: 85%; padding: 10px 15px; border-radius: 15px; font-size: 0.95rem; line-height: 1.4; } |
| .user-msg { align-self: flex-end; background: var(--primary); color: white; } |
| .bot-msg { align-self: flex-start; background: #30363d; color: var(--text); } |
| |
| /* Inputs */ |
| .input-area { padding: 15px; background: #21262d; display: flex; gap: 10px; border-top: 1px solid #30363d; } |
| input { flex: 1; background: #0d1117; border: 1px solid #30363d; color: white; padding: 10px; border-radius: 8px; outline: none; } |
| button { background: var(--accent); color: white; border: none; padding: 10px 20px; border-radius: 8px; cursor: pointer; font-weight: 600; } |
| button:hover { opacity: 0.9; } |
| |
| .loading { color: orange; font-style: italic; font-size: 0.8rem; } |
| </style> |
| </head> |
| <body> |
| |
| <header> |
| <div class="logo">TERM-X PRO UI</div> |
| <div style="font-size: 0.8rem; opacity: 0.7;">Status: Connected</div> |
| </header> |
| |
| <div class="main-container"> |
| <div class="panel"> |
| <div class="panel-header">LINUX TERMINAL</div> |
| <div id="terminal-output">Welcome to TermX. Type a command below...</div> |
| <div class="input-area"> |
| <input type="text" id="cmd-input" placeholder="Enter linux command (ls, cd, apt...)" onkeypress="if(event.key==='Enter') runCmd()"> |
| <button onclick="runCmd()">Execute</button> |
| </div> |
| </div> |
| |
| <div class="panel" id="chat-container"> |
| <div class="panel-header">CLAUDE AI CHAT</div> |
| <div id="chat-messages"> |
| <div class="msg bot-msg">Hello! I am Claude. How can I help you with your files or code today?</div> |
| </div> |
| <div class="input-area"> |
| <input type="text" id="chat-input" placeholder="Ask Claude anything..." onkeypress="if(event.key==='Enter') sendChat()"> |
| <button onclick="sendChat()" id="chat-btn">Send</button> |
| </div> |
| </div> |
| </div> |
| |
| <script> |
| const TOKEN = new URLSearchParams(window.location.search).get('token'); |
| |
| async def apiCall(command) { |
| try { |
| const response = await fetch(`/termx?token=${TOKEN}&cmd=${encodeURIComponent(command)}`); |
| return await response.text(); |
| } catch (e) { |
| return "Error: " + e.message; |
| } |
| } |
| |
| async function runCmd() { |
| const input = document.getElementById('cmd-input'); |
| const output = document.getElementById('terminal-output'); |
| const cmd = input.value; |
| if(!cmd) return; |
| |
| output.innerHTML += `\\n\\n$ ${cmd}\\n<span class="loading">Processing...</span>`; |
| input.value = ''; |
| output.scrollTop = output.scrollHeight; |
| |
| const res = await apiCall(cmd); |
| output.innerHTML = output.innerHTML.replace('<span class="loading">Processing...</span>', res); |
| output.scrollTop = output.scrollHeight; |
| } |
| |
| async function sendChat() { |
| const input = document.getElementById('chat-input'); |
| const box = document.getElementById('chat-messages'); |
| const text = input.value; |
| if(!text) return; |
| |
| // Add User Message |
| box.innerHTML += `<div class="msg user-msg">${text}</div>`; |
| input.value = ''; |
| |
| // Add Loading Placeholder |
| const botId = 'bot-' + Date.now(); |
| box.innerHTML += `<div class="msg bot-msg" id="${botId}"><span class="loading">Claude is thinking...</span></div>`; |
| box.scrollTop = box.scrollHeight; |
| |
| // Claude Command: claude 'text' |
| const fullCmd = `claude "${text.replace(/"/g, '\\"')}"`; |
| const res = await apiCall(fullCmd); |
| |
| document.getElementById(botId).innerText = res; |
| box.scrollTop = box.scrollHeight; |
| } |
| </script> |
| |
| </body> |
| </html>""" |