| import gradio as gr |
| import subprocess |
| import os |
| import sys |
|
|
| class Terminal: |
| def __init__(self): |
| self.current_dir = "/app" |
| self.history = [] |
| self.output_history = [] |
| self.max_history = 100 |
| |
| def execute(self, command): |
| """执行命令并返回结果""" |
| if not command.strip(): |
| return {"output": "", "success": True, "dir": self.current_dir} |
| |
| cmd = command.strip() |
| self.history.append(cmd) |
| if len(self.history) > self.max_history: |
| self.history.pop(0) |
| |
| |
| if cmd.startswith("cd "): |
| try: |
| target = cmd[3:].strip() |
| if not target: |
| target = os.path.expanduser("~") |
| |
| if not os.path.isabs(target): |
| target = os.path.join(self.current_dir, target) |
| |
| target = os.path.normpath(target) |
| |
| if os.path.isdir(target): |
| os.chdir(target) |
| self.current_dir = os.getcwd() |
| output = f"Changed directory to: {self.current_dir}" |
| self.output_history.append(f"$ {cmd}\n{output}") |
| return {"output": output, "success": True, "dir": self.current_dir} |
| else: |
| output = f"cd: {target}: No such directory" |
| self.output_history.append(f"$ {cmd}\n{output}") |
| return {"output": output, "success": False, "dir": self.current_dir} |
| except Exception as e: |
| output = f"cd error: {str(e)}" |
| self.output_history.append(f"$ {cmd}\n{output}") |
| return {"output": output, "success": False, "dir": self.current_dir} |
| |
| |
| if cmd == "clear" or cmd == "cls": |
| self.output_history = [] |
| return {"output": "", "success": True, "dir": self.current_dir, "clear": True} |
| |
| |
| try: |
| original_dir = os.getcwd() |
| os.chdir(self.current_dir) |
| |
| process = subprocess.run( |
| ["bash", "-c", cmd], |
| capture_output=True, |
| text=True, |
| timeout=30, |
| env=os.environ |
| ) |
| |
| os.chdir(original_dir) |
| |
| |
| output_lines = [] |
| if process.stdout: |
| output_lines.append(process.stdout.rstrip()) |
| if process.stderr: |
| output_lines.append(process.stderr.rstrip()) |
| |
| if process.returncode != 0 and not process.stderr: |
| output_lines.append(f"[Exit code: {process.returncode}]") |
| |
| output = "\n".join(output_lines) |
| |
| |
| self.output_history.append(f"$ {cmd}\n{output}" if output else f"$ {cmd}") |
| |
| return { |
| "output": output, |
| "success": process.returncode == 0, |
| "dir": self.current_dir |
| } |
| |
| except subprocess.TimeoutExpired: |
| output = "Error: Command timed out after 30 seconds" |
| self.output_history.append(f"$ {cmd}\n{output}") |
| return {"output": output, "success": False, "dir": self.current_dir} |
| except Exception as e: |
| output = f"Error: {str(e)}" |
| self.output_history.append(f"$ {cmd}\n{output}") |
| return {"output": output, "success": False, "dir": self.current_dir} |
| |
| def get_full_output(self): |
| """获取完整的终端输出""" |
| if not self.output_history: |
| return f"{self.current_dir}$ " |
| |
| |
| recent_history = self.output_history[-20:] |
| full_output = "\n\n".join(recent_history) |
| full_output += f"\n\n{self.current_dir}$ " |
| return full_output |
|
|
| |
| terminal = Terminal() |
|
|
| |
| custom_css = """ |
| #terminal-output textarea { |
| font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace !important; |
| font-size: 14px !important; |
| background-color: #1e1e1e !important; |
| color: #e0e0e0 !important; |
| border: 1px solid #444 !important; |
| } |
| #terminal-output { |
| max-height: 500px; |
| overflow-y: auto; |
| } |
| .prompt-text { |
| color: #4CAF50; |
| font-weight: bold; |
| } |
| .command-text { |
| color: #64B5F6; |
| } |
| .error-text { |
| color: #f44336; |
| } |
| #cmd-input input { |
| font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; |
| font-size: 14px; |
| } |
| .quick-btn { |
| font-family: monospace; |
| font-size: 12px; |
| margin: 2px; |
| padding: 4px 8px; |
| } |
| """ |
|
|
| |
| with gr.Blocks(title="Ubuntu Terminal") as demo: |
| |
| gr.Markdown("# 🐧 Ubuntu 22.04 Terminal") |
| gr.Markdown(f"**Current directory:** `{terminal.current_dir}`") |
| |
| |
| terminal_output = gr.Textbox( |
| label="", |
| value=terminal.get_full_output(), |
| lines=25, |
| elem_id="terminal-output", |
| interactive=False, |
| show_label=False |
| ) |
| |
| |
| with gr.Row(): |
| cmd_input = gr.Textbox( |
| label="", |
| placeholder="Enter command (press Enter to execute)...", |
| scale=8, |
| elem_id="cmd-input", |
| show_label=False |
| ) |
| execute_btn = gr.Button("Run", variant="primary", scale=1) |
| clear_btn = gr.Button("Clear", variant="secondary", scale=1) |
| |
| |
| gr.Markdown("### 🚀 Quick Commands") |
| with gr.Row(): |
| quick_commands = [ |
| ("pwd", "Show path"), |
| ("ls -la", "List files"), |
| ("python3 --version", "Python"), |
| ("wget --version", "Wget"), |
| ("curl --version", "Curl"), |
| ("git --version", "Git"), |
| ("sudo whoami", "Sudo test"), |
| ("uname -a", "System info"), |
| ] |
| |
| for cmd, desc in quick_commands: |
| btn = gr.Button(cmd, size="sm", min_width=100) |
| btn.click( |
| fn=lambda c=cmd: c, |
| outputs=[cmd_input] |
| ) |
| |
| |
| with gr.Accordion("📋 System Information", open=False): |
| |
| try: |
| user = os.environ.get('USER') or os.environ.get('USERNAME') or 'ubuntu' |
| uid = os.getuid() |
| except: |
| user = 'ubuntu' |
| uid = 1000 |
| |
| gr.Markdown(f""" |
| **Available Tools:** |
| - ✅ Python 3, pip |
| - ✅ sudo (passwordless for 'ubuntu' user) |
| - ✅ wget, curl, git |
| - ✅ vim, nano, htop |
| - ✅ build-essential |
| |
| **Current User:** `{user}` (uid: {uid}) |
| **Home Directory:** `{os.path.expanduser('~')}` |
| |
| **Try these commands:** |
| ```bash |
| # File operations |
| echo "Hello" > test.txt |
| cat test.txt |
| rm test.txt |
| |
| # System info |
| df -h |
| free -h |
| ps aux | head -20 |
| |
| # Network test |
| curl -s https://httpbin.org/ip |
| ping -c 1 8.8.8.8 2>/dev/null || echo "ping not available" |
| ``` |
| """) |
| |
| |
| def execute_command(cmd, current_output): |
| """执行命令并更新输出""" |
| result = terminal.execute(cmd) |
| new_output = terminal.get_full_output() |
| return new_output, "" |
| |
| def clear_terminal(): |
| """清空终端""" |
| terminal.output_history = [] |
| return terminal.get_full_output() |
| |
| |
| execute_btn.click( |
| execute_command, |
| inputs=[cmd_input, terminal_output], |
| outputs=[terminal_output, cmd_input] |
| ) |
| |
| cmd_input.submit( |
| execute_command, |
| inputs=[cmd_input, terminal_output], |
| outputs=[terminal_output, cmd_input] |
| ) |
| |
| clear_btn.click( |
| clear_terminal, |
| outputs=[terminal_output] |
| ) |
|
|
| if __name__ == "__main__": |
| print("=" * 60) |
| print("🚀 Starting Terminal Application") |
| print("📁 Current directory:", terminal.current_dir) |
| |
| |
| try: |
| user = os.environ.get('USER') or os.environ.get('USERNAME') or 'ubuntu' |
| except: |
| user = 'ubuntu' |
| |
| print("👤 User:", user) |
| print("=" * 60) |
| |
| |
| demo.launch( |
| server_name="0.0.0.0", |
| server_port=7860, |
| share=False, |
| css=custom_css |
| ) |