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) # 处理 cd 命令 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} # 处理 clear 命令 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}$ " # 只保留最近20条命令以避免过长 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() # 自定义CSS(现在放在launch()方法中) 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; } """ # 创建界面(移除了css参数) with gr.Blocks(title="Ubuntu Terminal") as demo: gr.Markdown("# 🐧 Ubuntu 22.04 Terminal") gr.Markdown(f"**Current directory:** `{terminal.current_dir}`") # 使用 Textbox 显示终端输出 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) # 启动应用(css参数现在在这里) demo.launch( server_name="0.0.0.0", server_port=7860, share=False, css=custom_css # CSS参数移到这里 )