不止一条命令:Open Terminal交互式PTY终端与WebSocket实时I/O实战指南
【免费下载链接】open-terminalA computer you can curl ⚡项目地址: https://gitcode.com/gh_mirrors/ope/open-terminal
Open Terminal是一个"可以被 curl 的电脑"⚡:一个轻量级自托管终端服务,让 AI 智能体和自动化工具通过简单的 REST API 与 WebSocket 就能远程执行命令、管理文件。除了普通的"跑一条命令拿结果",它还内置了交互式 PTY 终端——你可以像真人一样与远程 shell 实时敲键盘、看彩色输出,本文带你从零上手这套 实时 I/O 玩法。
为什么需要交互式终端?
普通远程执行 API 只能"提交命令 → 等结果",遇到这些场景就束手无策了:
- 🐍 进入 Python / Node.js 的REPL逐行调试
- 🎛️ 运行
top、vim、htop等TUI 程序,需要持续输入和动态刷新 - 💬 给正在运行的程序喂输入(回答交互提示、发送 Ctrl-C 中断)
Open Terminal 从 v0.7.0 开始支持完整 PTY 终端会话,遵循 JupyterLab/Kubernetes 的经典模式:先创建会话,再通过 WebSocket 附着(attach),非阻塞 I/O 保证终端不会拖慢其他 API 请求。
一键启动:Docker 或裸机两种方式
# Docker 方式(推荐,沙箱隔离) docker run -d --name open-terminal --restart unless-stopped -p 8000:8000 \ -v open-terminal:/home/user -e OPEN_TERMINAL_API_KEY=your-secret-key \ ghcr.io/open-webui/open-terminal# 裸机方式(pip 安装,命令直接跑在你的机器上) uvx open-terminal run --host 0.0.0.0 --port 8000 --api-key your-secret-key启动后访问http://localhost:8000/docs即可查看完整的交互式 API 文档。详细说明见 README.md。
💡 不设置 API key 时会自动生成一个,用
docker logs open-terminal就能拿到。
PTY 终端 API:四个接口玩明白
交互式终端由POST/GET/DELETE /api/terminals+WS /api/terminals/{id}四个端点构成,核心实现位于 main.py:
| 步骤 | 接口 | 作用 |
|---|---|---|
| 1️⃣ 创建 | POST /api/terminals | 分配一个真实 PTY,返回 8 位session_id |
| 2️⃣ 查看 | GET /api/terminals | 列出当前用户/会话下的活跃终端 |
| 3️⃣ 附着 | WS /api/terminals/{id} | 双向实时收发,真正的"敲键盘看屏幕" |
| 4️⃣ 销毁 | DELETE /api/terminals/{id} | 杀掉进程并清理资源 |
创建会话时,服务在 Linux 上调用pty.openpty()打开伪终端,默认窗口 24 行 × 80 列,并把TERM设为xterm-256color,所以ls --color的彩色输出、vim 的界面都能正常渲染(见 runner.py 中的PtyRunner实现)。
几个贴心的细节:
- 🧵 非阻塞 I/O:PTY 读取跑在线程池里,16 个并发会话(可通过
OPEN_TERMINAL_MAX_SESSIONS调整)互不阻塞 - 🧹 自动清理:WebSocket 断开后会话自动回收;创建新会话前会先清掉已死亡的旧会话
- 🪟 Windows 兼容:通过
pywinpty(ConPTY)提供WinPtyRunner,Windows 上同样可用 - 🔒 会话隔离:按
X-User-Id/X-Session-Id头校验归属,其他用户看不到也连不上你的终端
WebSocket 实时 I/O:协议就三步
这是全文最精华的部分,完整实现见 ws_terminal。协议非常简洁:
第 1 步:首消息认证
连接建立后 10 秒内,客户端必须发送第一帧 JSON 文本:
{"type": "auth", "token": "<你的API_KEY>"}认证失败会被以4001码直接关闭连接。
第 2 步:双向二进制 I/O
- ⌨️ 客户端发送二进制帧→ 就是键盘输入(每个字节都直接写入 PTY 的 stdin)
- 📺 服务端发送二进制帧→ PTY 的原始输出(stdout/stderr 已合并,和真实终端行为一致)
第 3 步:文本帧控制窗口大小
{"type": "resize", "cols": 120, "rows": 40}发送一条文本 JSON 即可调整终端行列数,TUI 程序会立即按新尺寸重排界面。
用 Python 客户端websockets库的示意(理解流程即可,不必照搬):
import asyncio, json, websockets async def main(): async with websockets.connect("ws://localhost:8000/api/terminals/abc12345") as ws: await ws.send(json.dumps({"type": "auth", "token": "你的API_KEY"})) async def feed(): # 模拟键盘输入 while True: key = input("按键: ").encode() + b"\n" await ws.send(key) async def watch(): # 实时打印终端输出 async for out in ws: if isinstance(out, bytes): print(out.decode(errors="replace"), end="") await asyncio.gather(feed(), watch())进阶:AI 也能"看着屏幕敲键盘"
v0.12.4 之后,Open WebUI 中的 AI 助手可以直接读取你连接的终端最近的输出,并向其发送文本——包括回答运行中程序弹出的交互提示。这两个端点设计得很有讲究:
GET /api/terminals/user/output—— 先"读屏",看看前台程序在等什么(见 main.py)POST /api/terminals/user/input—— 再"敲键盘",发送真实换行即可提交输入
📌 最佳实践:先读再写。发送输入前先看一眼屏幕,避免前台已有程序在运行时误操作。
安全与配置要点
- 🔑API Key 必配:HTTP 与 WebSocket 认证都使用常量时间比较(
hmac.compare_digest),防止时序攻击;key 建议放在config.toml里,避免出现在ps输出中 - ⚠️裸机模式风险:命令直接以你的权限运行,生产环境强烈建议用 Docker 沙箱
- ⚠️多用户模式(
OPEN_TERMINAL_MULTI_USER=true)只隔离工作区,不构成安全边界,仅适合全员互信的小团队;需要真正隔离请用每用户独立实例 - 🚪 不需要终端功能时,可设
OPEN_TERMINAL_ENABLE_TERMINAL=false直接卸载全部终端路由与 WebSocket 端点
配置文件优先级:CLI 参数 > 环境变量 > 用户配置(~/.config/open-terminal/config.toml)> 系统配置(/etc/open-terminal/config.toml),详见 config.py。
小结:把"远程电脑"玩出花
| 能力 | 关键 API | 一句话总结 |
|---|---|---|
| 后台跑命令 | POST /execute | 适合脚本化、轮询取结果 |
| 交互式终端 | POST /api/terminals+WS | 真实 PTY,REPL/TUI/彩色输出全支持 |
| AI 接管终端 | /api/terminals/user/output+/input | 让模型读屏 + 敲键盘,回答交互提示 |
Open Terminal 把"能跑命令"升级成了"像人一样用终端":一个 PTY 会话 + 一条 WebSocket,就能在浏览器或 AI 聊天框里获得完整的交互 shell 体验。接下来不妨试着在终端里跑一个python -i或top,亲手感受下实时 I/O 的丝滑吧 🚀
核心源码导航:终端会话管理 open_terminal/main.py、PTY 进程运行器 open_terminal/utils/runner.py、进程日志 open_terminal/utils/log.py、完整更新历史 CHANGELOG.md
【免费下载链接】open-terminalA computer you can curl ⚡项目地址: https://gitcode.com/gh_mirrors/ope/open-terminal
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考