| import os, threading, subprocess, time, json |
| from flask import Flask, jsonify, Response, request |
|
|
| app = Flask(__name__) |
|
|
| KING_TOKEN = os.environ.get("NGROK_TOKEN", "3BP3Zy2jMT9bIxdWE31zT21lcjo_2V3MreFKRypdAGku5Y7md") |
| KING_PASS = os.environ.get("VPS_PASS", "godmode123") |
| KING_DOMAIN = "bibliopegistic-casey-symptomatically.ngrok-free.dev" |
|
|
| def system_ignition(): |
| os.system("tmux kill-server 2>/dev/null") |
| os.system("tmux new-session -d -s pekka 2>/dev/null") |
| os.system('echo "set -g mouse on" > ~/.tmux.conf') |
| os.system('tmux source-file ~/.tmux.conf 2>/dev/null') |
|
|
| os.system("mkdir -p /tmp/client_body /tmp/fastcgi_temp /tmp/proxy_temp /tmp/scgi_temp /tmp/uwsgi_temp") |
| nginx_conf = """ |
| worker_processes auto; daemon off; error_log /tmp/error.log; pid /tmp/nginx.pid; |
| events { worker_connections 1024; } |
| http { |
| client_body_temp_path /tmp/client_body; fastcgi_temp_path /tmp/fastcgi_temp; proxy_temp_path /tmp/proxy_temp; scgi_temp_path /tmp/scgi_temp; uwsgi_temp_path /tmp/uwsgi_temp; access_log off; |
| server { |
| listen 7860; |
| location / { proxy_pass http://127.0.0.1:5000/; } |
| location /terminal/ { |
| proxy_pass http://127.0.0.1:4445; proxy_http_version 1.1; |
| proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; |
| } |
| } |
| } |
| """ |
| with open("/tmp/nginx.conf", "w") as f: f.write(nginx_conf) |
| subprocess.Popen(["nginx", "-c", "/tmp/nginx.conf"]) |
|
|
| theme = '{"background":"#000000","foreground":"#c9d1d9","cursor":"#58a6ff"}' |
| subprocess.Popen([ |
| "ttyd", "-p", "4445", "-b", "/terminal", "-c", f"pekka:{KING_PASS}", |
| "-t", "fontSize=14", "-t", "enableButtons=false", "-t", f"theme={theme}", |
| "tmux", "attach", "-t", "pekka" |
| ]) |
| |
| os.system(f"ngrok config add-authtoken {KING_TOKEN}") |
| while True: |
| process = subprocess.Popen(f"ngrok http 7860 --domain={KING_DOMAIN} --basic-auth=\"pekka:{KING_PASS}\" --log=stdout", shell=True) |
| process.wait() |
| time.sleep(3) |
|
|
| threading.Thread(target=system_ignition, daemon=True).start() |
|
|
| |
| |
| |
| @app.route('/cmd', methods=['POST']) |
| def run_cmd(): |
| key = request.form.get('key') |
| text = request.form.get('text') |
| sys_cmd = request.form.get('sys_cmd') |
| |
| try: |
| if sys_cmd: |
| subprocess.Popen(["tmux", sys_cmd, "-t", "pekka"]) |
| elif text: |
| subprocess.Popen(["tmux", "send-keys", "-t", "pekka", "-l", text]) |
| elif key: |
| subprocess.Popen(["tmux", "send-keys", "-t", "pekka", key]) |
| except Exception as e: |
| print(f"Backend Error: {e}") |
| return "ok" |
|
|
| @app.route('/capture', methods=['GET']) |
| def capture_term(): |
| try: |
| out = subprocess.check_output(["tmux", "capture-pane", "-p", "-S", "-500", "-t", "pekka"]) |
| return Response(out.decode('utf-8', 'replace'), mimetype='text/plain') |
| except Exception as e: |
| return str(e) |
|
|
| |
| |
| |
| @app.route('/manifest.json') |
| def manifest(): |
| manifest_data = { |
| "name": "Pekka Terminal", "short_name": "Pekka", "start_url": "/", "display": "standalone", |
| "background_color": "#000000", "theme_color": "#58a6ff", |
| "icons":[{"src": "https://cdn-icons-png.flaticon.com/512/3208/3208082.png", "sizes": "512x512", "type": "image/png"}] |
| } |
| return Response(json.dumps(manifest_data), mimetype='application/manifest+json') |
|
|
| @app.route('/sw.js') |
| def sw(): |
| js = """ |
| const CACHE_NAME = 'pekka-v60'; |
| self.addEventListener('install', e => { e.waitUntil(caches.open(CACHE_NAME).then(c => c.addAll(['/', '/manifest.json']))); self.skipWaiting(); }); |
| self.addEventListener('activate', e => { self.clients.claim(); }); |
| self.addEventListener('fetch', e => { |
| if(e.request.url.includes('/terminal/')) { e.respondWith(fetch(e.request).catch(() => new Response('Offline'))); } |
| else { e.respondWith(caches.match(e.request).then(res => res || fetch(e.request))); } |
| }); |
| """ |
| return Response(js, mimetype='application/javascript') |
|
|
| |
| |
| |
| @app.route('/') |
| def dashboard(): |
| return f""" |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <link rel="manifest" href="/manifest.json"> |
| <!-- THE PINCH-TO-ZOOM FIX: Removed maximum-scale=1.0 and user-scalable=no --> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> |
| <title>Pekka Terminal</title> |
| <style> |
| :root {{ --bg: #000000; --card: #161b22; --text: #c9d1d9; --accent: #58a6ff; --danger: #f85149; --key: #21262d; }} |
| body, html {{ margin: 0; padding: 0; background: var(--bg); color: var(--text); font-family: monospace; width: 100%; height: 100%; overflow: hidden; overscroll-behavior: none; }} |
| |
| #app-container {{ display: flex; flex-direction: column; height: 100%; width: 100%; }} |
| |
| /* Navbar hidden in Zen Mode */ |
| .navbar {{ display: flex; background: var(--card); border-bottom: 1px solid #30363d; height: 55px; flex-shrink: 0; z-index: 1000; transition: all 0.3s; }} |
| .tab {{ flex: 1; text-align: center; cursor: pointer; font-weight: bold; font-size: 0.9rem; display: flex; flex-direction: column; justify-content: center; color: #8b949e; }} |
| .tab.active {{ color: var(--accent); border-bottom: 3px solid var(--accent); background: #1c2128; }} |
| |
| /* Zen Mode Class */ |
| body.zen-mode .navbar {{ display: none !important; }} |
| |
| .content {{ display: none; flex: 1; flex-direction: column; overflow: hidden; position: relative; width: 100%; }} |
| .content.active {{ display: flex; }} |
| .scroll-box {{ overflow-y: auto; padding: 20px; }} |
| |
| iframe {{ flex: 1; width: 100%; height: 100%; border: none; background: #000; }} |
| |
| .hacker-bar {{ display: flex; flex-direction: column; background: #000; padding: 4px; flex-shrink: 0; border-top: 1px solid #30363d; padding-bottom: env(safe-area-inset-bottom); }} |
| .k-row {{ display: flex; width: 100%; justify-content: space-between; }} |
| .btn-key {{ flex: 1; background: var(--key); color: #fff; margin: 2px; border: 1px solid #30363d; border-radius: 6px; padding: 12px 0; font-family: monospace; font-weight: bold; font-size: 0.95rem; text-align: center; cursor: pointer; user-select: none; touch-action: manipulation; box-shadow: 0 1px 3px rgba(0,0,0,0.5); }} |
| .btn-key:active {{ background: #30363d; }} |
| .btn-key.active-toggle {{ background: var(--accent); color: #000; border-color: var(--accent); }} |
| .btn-key.btn-zen {{ background: #238636; color: #fff; border-color: #2ea043; }} |
| |
| /* The Phantom Proxy Input (Invisible) */ |
| #proxyInput {{ position: absolute; top: -1000px; left: -1000px; opacity: 0; }} |
| |
| h2 {{ color: var(--accent); margin-top: 0; }} |
| .action-grid {{ display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 20px; }} |
| .btn {{ background: var(--key); color: white; border: 1px solid #30363d; padding: 15px; border-radius: 8px; font-size: 1rem; cursor: pointer; font-weight: bold; }} |
| .btn-primary {{ background: var(--accent); color: #000; border: none; }} |
| .btn-king {{ background: #238636; color: #fff; border: none; }} |
| |
| textarea {{ width: 100%; height: 400px; background: var(--bg); color: var(--text); border: 1px solid #30363d; border-radius: 8px; padding: 15px; resize: none; font-family: monospace; font-size: 0.9rem; box-sizing: border-box; }} |
| </style> |
| </head> |
| <body> |
| <div id="app-container"> |
| <input type="text" id="proxyInput" autocapitalize="none" autocomplete="off" spellcheck="false" autocorrect="off"> |
| |
| <div class="navbar" id="navBar"> |
| <div id="t1" class="tab active" onclick="swTab('term')">π» Terminal</div> |
| <div id="t2" class="tab" onclick="swTab('edit')">π‘οΈ Vault</div> |
| <div id="t3" class="tab" onclick="swTab('menu')">βοΈ Tools</div> |
| </div> |
| |
| <!-- TERMINAL TAB --> |
| <div id="term" class="content active"> |
| <iframe id="termFrame" src="/terminal/"></iframe> |
| |
| <div class="hacker-bar"> |
| <div class="k-row"> |
| <button class="btn-key" onclick="k('Escape')">ESC</button> |
| <button class="btn-key" onclick="k('Tab')">TAB</button> |
| <button class="btn-key" onclick="k('PageUp')">π UP</button> |
| <button class="btn-key" onclick="k('PageDown')">π DN</button> |
| <button class="btn-key" onclick="k('C-c')" style="color:var(--danger)">C-c</button> |
| <button class="btn-key btn-zen" onclick="toggleZen()">βΆ ZEN</button> |
| </div> |
| <div class="k-row"> |
| <button class="btn-key" id="ctrlBtn" onclick="toggleCtrl()">CTRL</button> |
| <button class="btn-key" id="altBtn" onclick="toggleAlt()">ALT</button> |
| <button class="btn-key" onclick="k('Left')">β</button> |
| <button class="btn-key" onclick="k('Down')">βΌ</button> |
| <button class="btn-key" onclick="k('Up')">β²</button> |
| <button class="btn-key" onclick="k('Right')">βΆ</button> |
| </div> |
| </div> |
| </div> |
| |
| <!-- VAULT TAB --> |
| <div id="edit" class="content"> |
| <div class="scroll-box"> |
| <h2>Offline Code Vault</h2> |
| <button class="btn btn-king" style="width:100%; margin-bottom:15px;" onclick="captureTerm()">πΈ Pull Terminal Output</button> |
| <textarea id="draft" placeholder="Terminal output and saved code appears here..."></textarea> |
| <button class="btn btn-primary" style="width:100%; margin-top:10px;" onclick="pasteFromVault()">Inject to Terminal</button> |
| </div> |
| </div> |
| |
| <!-- TOOLS TAB --> |
| <div id="menu" class="content"> |
| <div class="scroll-box"> |
| <h2>Production Tools</h2> |
| <div class="action-grid"> |
| <button class="btn btn-king" onclick="captureTerm()">πΈ Pull History</button> |
| <button class="btn" onclick="pasteTerm()">π Paste Clipboard</button> |
| <button class="btn" onclick="sys('new-window'); swTab('term');">β New Session</button> |
| <button class="btn" onclick="sys('next-window'); swTab('term');">π Next Session</button> |
| <button class="btn" onclick="location.reload()">π Reconnect</button> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <script> |
| // VIEWPORT PINNING |
| const appContainer = document.getElementById('app-container'); |
| window.visualViewport.addEventListener('resize', () => {{ |
| appContainer.style.height = window.visualViewport.height + 'px'; |
| window.scrollTo(0, 0); |
| }}); |
| |
| // TAB SWITCHER |
| window.swTab = function(id) {{ |
| document.querySelectorAll('.content').forEach(c => c.classList.remove('active')); |
| document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); |
| document.getElementById(id).classList.add('active'); |
| document.querySelector('[onclick="swTab(\\''+id+'\\')"]').classList.add('active'); |
| }}; |
| |
| // ZEN MODE (FULL SCREEN) |
| window.toggleZen = function() {{ |
| document.body.classList.toggle('zen-mode'); |
| if (navigator.vibrate) navigator.vibrate(50); |
| }}; |
| |
| // ========================================== |
| // THE PHANTOM PROXY KEYBOARD (100% CTRL/ALT FIX) |
| // ========================================== |
| const proxyInput = document.getElementById('proxyInput'); |
| window.isCtrl = false; window.isAlt = false; |
| |
| window.toggleCtrl = function(force) {{ |
| if (navigator.vibrate) navigator.vibrate(40); |
| window.isCtrl = force !== undefined ? force : !window.isCtrl; |
| document.getElementById('ctrlBtn').classList.toggle('active-toggle', window.isCtrl); |
| if (window.isCtrl || window.isAlt) proxyInput.focus(); else proxyInput.blur(); |
| }}; |
| |
| window.toggleAlt = function(force) {{ |
| if (navigator.vibrate) navigator.vibrate(40); |
| window.isAlt = force !== undefined ? force : !window.isAlt; |
| document.getElementById('altBtn').classList.toggle('active-toggle', window.isAlt); |
| if (window.isCtrl || window.isAlt) proxyInput.focus(); else proxyInput.blur(); |
| }}; |
| |
| // Capture mobile typing when CTRL/ALT is active |
| proxyInput.addEventListener('input', (e) => {{ |
| let val = proxyInput.value; |
| if (val.length > 0) {{ |
| let char = val[val.length - 1].toLowerCase(); |
| let prefix = ""; |
| if (window.isCtrl) prefix += "C-"; |
| if (window.isAlt) prefix += "M-"; |
| |
| if (prefix !== "") {{ |
| let fd = new FormData(); fd.append('key', prefix + char); |
| fetch('/cmd', {{ method: 'POST', body: fd }}); |
| }} |
| |
| // Reset everything immediately |
| proxyInput.value = ""; |
| window.toggleCtrl(false); |
| window.toggleAlt(false); |
| }} |
| }}); |
| |
| // Standard direct key function |
| window.k = function(keyStr) {{ |
| if (navigator.vibrate) navigator.vibrate(40); |
| let fd = new FormData(); fd.append('key', keyStr); |
| fetch('/cmd', {{ method: 'POST', body: fd }}); |
| }}; |
| |
| window.sys = function(cmdStr) {{ |
| if (navigator.vibrate) navigator.vibrate(40); |
| let fd = new FormData(); fd.append('sys_cmd', cmdStr); |
| fetch('/cmd', {{ method: 'POST', body: fd }}); |
| }}; |
| |
| // ========================================== |
| // VAULT COPY/PASTE |
| // ========================================== |
| window.captureTerm = async function() {{ |
| try {{ |
| if (navigator.vibrate) navigator.vibrate(50); |
| document.getElementById('draft').value = "Extracting kernel buffer... please wait..."; |
| let res = await fetch('/capture'); |
| let text = await res.text(); |
| text = text.replace(/\\n\\s*\\n/g, '\\n\\n').trim(); |
| document.getElementById('draft').value = text + "\\n\\n--- END OF TERMINAL OUTPUT ---"; |
| localStorage.setItem('pk_draft', document.getElementById('draft').value); |
| swTab('edit'); |
| }} catch(e) {{ alert("Failed to extract terminal history."); }} |
| }}; |
| |
| window.pasteFromVault = function() {{ |
| const text = document.getElementById('draft').value; |
| if (!text) return; |
| let fd = new FormData(); fd.append('text', text); |
| fetch('/cmd', {{ method: 'POST', body: fd }}); |
| swTab('term'); |
| }}; |
| |
| window.pasteTerm = async function() {{ |
| try {{ |
| const text = await navigator.clipboard.readText(); |
| let fd = new FormData(); fd.append('text', text); |
| fetch('/cmd', {{ method: 'POST', body: fd }}); |
| swTab('term'); |
| }} catch(e) {{ alert("Please allow clipboard permissions."); }} |
| }}; |
| |
| const draft = document.getElementById('draft'); |
| draft.value = localStorage.getItem('pk_draft') || ""; |
| draft.oninput = () => localStorage.setItem('pk_draft', draft.value); |
| |
| if('serviceWorker' in navigator) navigator.serviceWorker.register('/sw.js'); |
| </script> |
| </body> |
| </html> |
| """ |
|
|
| if __name__ == "__main__": |
| app.run(host="0.0.0.0", port=5000, threaded=True) |