内容
<img width="2172" height="724" alt="image" src="https://github.com/user-attachments/assets/ec5413d6-24fd-4d4d-968f-7c13db156c8d" />
# 浏览器天地
**一个基于Web的终端桌面,可以启动、流式传输和恢复一批无头Shell——以及运行在其中的AI编码代理。**
将浏览器指向代理服务器,您将获得一个完整的窗口桌面,包含实时的终端:您可以平铺、选项卡、分割和拖动它们跨虚拟桌面。每个终端都是一个真正的PTY,在某台机器上运行,通过WebSocket流式传输到浏览器。
即使没有浏览器连接,Shell也会继续运行。关闭选项卡,隔天再来,屏幕会从快照中恢复——正好是您离开的地方。
名字一目了然:一个小桌面——窗口、终端和您的AI编码代理舰队——完全生活在浏览器选项卡中。(`webterm`是Python包和模块名称。)
Browserland还公开了一个**MCP服务器**,允许LLM利用终端直接驱动——包括全屏TUI。
## 这是什么?
Browserland是两个小程序和一个浏览器:
- **代理**(生产者)是无头的进程,拥有一个真正的终端——Linux上的`pty.openpty`,Windows上的ConPTY/WinPTY。代理运行一个命令(`bash`、`cmd.exe`、AI编码代理、任何程序),保持最近输出的环形缓冲区,并通过单个WebSocket流式传输终端。
- **代理服务器**是一个小型Web服务器。代理向其注册;浏览器与之连接。它在两者之间中继字节,提供桌面UI,并可以根据预先批准的**配置文件**列表按需启动新的代理。
- **浏览器**渲染桌面——一个平铺窗口管理器,位于[xterm.js](https://xtermjs.org/)之上——并将您的按键发送回PTY。
线路格式是故意的小型JSON帧集,通过一个WebSocket,因此任何能理解它们的生产者都可以向代理服务器注册——Browserland自己的代理只是参考实现。
```
┌─────────┐ 二进制ANSI + JSON ┌────────┐ /ws?session=<id> ┌─────────┐
│ 代理 │ ──────────────────▶ │ 代理服务器 │ ──────────────────▶ │ 浏览器 │
│ (PTY + │ ◀────────────────── │ │ ◀────────────────── │ xterm.js│
│ ConPTY/ │ 输入/调整大小/ └────────┘ 输入/粘贴/调整大小 └─────────┘
│ openpty)│ 快照请求
└─────────┘
```
由于PTY存在于代理中,而不是浏览器中,因此终端在浏览器重新加载甚至代理服务器重启时仍然存活——代理会带回退重试,浏览器的附件会触发环形缓冲区的快照重绘。
## 截图
带有平铺和浮动终端的桌面
<img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/135f7d46-617e-40f4-a195-f1ce43a625eb" />
<img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/db268062-9932-498c-9e55-1fab4ce50422" />
## 功能
- **浏览器内平铺窗口管理器**(类似niri):浮动和*以及*平铺窗口,虚拟桌面分页器和任务栏——所有都在页面中,无需原生应用。
- **嵌套选项卡和分割**:任何平铺都可以容纳选项卡组,选项卡可以容纳分割,通过递归单元格模型。按需构建布局。
- **不仅仅是终端**:便利贴,[CodeMirror 6](https://codemirror.net/)文本编辑器,文件管理器和任务管理器——由沙盒化的`/file/*` API支持。
- **跨平台PTY**:Linux `pty.openpty`;Windows自动选择ConPTY或WinPTY(存在控制台窗口时使用ConPTY以正确处理Ctrl-C,适用于无头进程的WinPTY)。
- **AI代理舰队**:检测每个窗口的前台编码代理(`claude` / `codex` / `grok` / `opencode`),跟踪实时OSC标题+工作目录,并显示每个窗口的git状态。
- **多主机**:将相同的UI附加到其他代理服务器(例如,通过[Tailscale](https://tailscale.com/)的另一台机器),任务栏中带有每个主机的状态芯片。
- **单个活动浏览器租约**:一次只有一个浏览器驱动输入,因此两个打开的选项卡永远不会争夺键盘。
- **按需MCP / AI代理访问**:MCP客户端或AI利用工具可以列出、观察、驱动和启动终端——包括实时的交互式TUI和控制台——在您控制的每个窗口访问模式下。参见下面的**MCP和AI代理访问**。
## MCP和AI代理访问
代理服务器公开了一个令牌保护的`/mcp/*` HTTP API,并附带了一个stdio [MCP](https://modelcontextprotocol.io/)服务器(`webterm.mcptool`),因此任何MCP客户端或AI利用工具都可以**列出、观察、驱动和启动**终端。代理只是生产者;代理服务器仍然是唯一的授权者——每个MCP调用都由相同的每个窗口访问模式和主启用开关保护。
- **交互式TUI,作为纯文本** — `read_screen`通过[pyte](https://github.com/selectel/pyte)重放PTY环形缓冲区,渲染一个实时终端的**当前**屏幕,以便利用工具可以读取全屏应用程序(btop,htop,vim,less)——不仅仅是面向行的滚动缓冲区——并且`send_input`输入到它们中。没有pyte,读取会回落到一个依赖项免费的内部渲染器,该渲染器仍然返回一个**有界**的渲染网格(`degraded: true`现在保留用于罕见的最后一刻原始解码)。
- **您正在使用的实时会话** — `list_terminals`枚举正在运行的会话(id,标题,cwd,代理,类型,列/行,模式),因此利用工具可以附加到人当前正在使用的确切控制台,读取其状态,并(在`readwrite`中)驱动它。会话在浏览器重新加载和代理服务器重启时持续存在,因此句柄保持有效。
### 工具
每个工具都映射到一个代理服务器端点并返回其JSON。窗口`id`被命名为`<host>:<int>`字符串,因此一个服务器可以支持多个代理服务器(参见下面的**多主机**);使用单个代理服务器时,主机是`default`(`"default:12345"`)。
| 工具 | 端点 | 备注 |
|---|---|---|
| `mcp_info(host?)` | `GET /mcp/info` | 功能标志(`allow_launch`,`default_mode`)。省略`host` → 以主机名作为键的字典 |
| `list_terminals` | `GET /mcp/terminals` | `{"terminals":[…],"errors":{host:msg}}`:所有主机合并,每个终端的`host`设置为配置名称(代理服务器的机器主机名保留为`machine_host`)+命名空间`id`;主机关闭时,错误不会影响其他部分 |
| `list_profiles(host?)` | `GET /mcp/profiles` | 可启动的配置文件名称+默认值。省略`host` → 以主机名作为键的字典 |
| `read_screen(id)` | `POST /mcp/read` | 屏幕渲染为有界纯文本网格(pyte,或依赖项免费的备用) |
| `send_input(id, data)` | `POST /mcp/input` | 目标窗口必须处于**`readwrite`**模式;换行符被发送为**Enter**(CR),因此命令可以运行(包括在PowerShell上) |
| `send_keys(id, keys)` | `POST /mcp/input` | 发送控制/转义**按键** — `["C-c"]`,`["Esc"]`,`["Up","Enter"]` — 普通文本无法表达的内容 |
| `launch_terminal(profile?, cols=80, rows=24, title?, cwd?, host?)` | `POST /mcp/launch` | 代理服务器必须启用 **`allow_launch`**;当配置了多个主机时,需要`host` |
**多主机。** 将 `--hosts` (或 `$BROWSERLAND_MCP_HOSTS`)传递给一个 JSON 数组,包含 `{name, url, token}` 描述,以从一个服务器进程中提供多个代理服务器;每个接受 `id` 的工具都路由到 `"<host>:…"` 前缀。单个 `--broker-url`/`--token` 形式是一个主机速记(`default`)。有关详细信息,请参见 [`webterm/mcptool/README.md`](webterm/mcptool/README.md)。
> `send_input` **工具** 将 `data` 中的换行符映射到回车键 — 实际的 Enter 键发送的字节 — 因此在 PowerShell/PSReadLine(将裸换行符视为软继续)和 Unix shell 上,行会被提交。
### 安全/启用
访问是分层且按需启用的 — 启用前无法访问任何内容:
- **主启用** 默认情况下为 **关闭**;关闭时,每个 `/mcp/*` 调用都返回 `403 mcp_disabled`。
- **每个窗口模式** 为 `off` / `read` / `readwrite`,具有新的窗口的全局 `default_mode`。`off` 完全隐藏窗口;`read` 允许观察;`readwrite` 还允许 `send_input`。
- **`allow_launch`** 是 `launch_terminal` 的单独门控。
- **MCP令牌** 是一个与浏览器 `auth_token` (UI密码)不同的承载秘密;**代理服务器**用 `WEB_TERMINAL_MCP_TOKEN` (或 `webterm_mcp.json` 边车)固定它,**MCP客户端**通过 `BROWSERLAND_MCP_TOKEN` (如下所示的利用示例)传递相同的秘密。
### 注册到利用工具
```bash
claude mcp add browserland \
--env BROWSERLAND_MCP_TOKEN=… \
--env BROWSERLAND_MCP_URL=http://127.0.0.1:4445 \
-- python -m webterm.mcptool
```
任何其他 **stdio** MCP客户端(Hermes,您自己的,…)都以相同的方式注册 — 将其指向启动命令并传递两个环境变量:
```json
{
"mcpServers": {
"browserland": {
"command": "python",
"args": ["-m", "webterm.mcptool"],
"env": {
"BROWSERLAND_MCP_TOKEN": "…",
"BROWSERLAND_MCP_URL": "http://127.0.0.1:4445"
}
}
}
}
```
或直接运行服务器,与本地代理服务器通信:
```bash
BROWSERLAND_MCP_TOKEN=… python -m webterm.mcptool
BROWSERLAND_MCP_TOKEN=… browserland-mcp --broker-url http://127.0.0.1:4445
```
有关完整的HTTP合同、错误表和配置边车,请参见 ** [`docs/TECHNICAL.md`](docs/TECHNICAL.md) ** 和 ** [`webterm/mcptool/README.md`](webterm/mcptool/README.md) **。
## 快速入门
您需要 **Python ≥ 3.9**。从检查安装:
```bash
pip install -e .
```
### Windows
```powershell
# 代理服务器(默认 127.0.0.1:4445)
python -m webterm.broker
# 一个运行 cmd.exe 的代理,注册到本地代理服务器
python -m webterm.agent -- cmd.exe
# 然后打开 http://127.0.0.1:4445/ 并点击会话(或“新建终端”)
```
Windows代理还需要一个PTY后端:`pip install -e ".[windows]"`(引入`pywinpty`)。
### Linux
```bash
./launchers/run-broker.sh # 代理服务器
./launchers/run-agent.sh -- bash -l # 代理
```
启动脚本在首次运行时会引导一个虚拟环境,包含运行时依赖项。然后打开 `http://127.0.0.1:4445/`。
## 安装和附加功能
```bash
pip install -e . # 核心:代理服务器 + 代理
pip install -e ".[windows]" # + pywinpty(Windows PTY后端)
pip install -e ".[pyte]" # + pyte(二级快照渲染)
pip install -e ".[procs]" # + psutil(任务管理器,代理徽章,实时工作目录)
pip install -e ".[mcp]" # + 标准输入MCP服务器(Python ≥ 3.10)
pip install -e ".[dev]" # + pytest用于测试套件
```
`psutil`(`procs` 额外功能)是尽力而为的:它支持 **任务管理器进程列表**、**前台代理徽章** 和 **实时工作目录跟踪**。如果没有它,代理仍然运行,仍然销毁窗口 — 这三个视图只是降级(空列表 / 无徽章 / 无工作目录)。
按需混合和匹配,例如 `pip install -e ".[pyte,mcp,dev]"`。`mcp` 额外功能需要 **Python ≥ 3.10** (MCP SDK),而其他所有功能都运行在 **Python ≥ 3.9** 上。有关运行服务器,请参见 **MCP和AI代理访问**。
## 项目布局
| 路径 | 内容 |
|---|---|
| `webterm/agent/` | 无头生产者:PTY后端、输出环形缓冲区、OSC标题嗅探器、重新连接的WebSocket客户端 |
| `webterm/broker/` | Web服务器:桌面UI(`index.html`)、`/ws`中继、生产者WS、会话列表、仅限配置文件的启动 |
| `webterm/mcptool/` | 随附的标准输入MCP服务器,封装代理服务器的`/mcp/*` API |
| `webterm/protocol.py` | JSON帧形状的单一真相来源 |
| `launchers/` | 两个操作系统的虚拟环境启动脚本(和systemd单元) |
| `tests/` | pytest套件:协议、快照、代理↔代理服务器集成、真实PTY往返 |
## 文档
完整的工程参考资料位于 ** [`docs/TECHNICAL.md`](docs/TECHNICAL.md) **:
- 完整的线路协议和帧语义,
- 完整的身份验证模型(每个表面,令牌优先级,CORS),
- 每个HTTP端点,包括MCP HTTP合同及其错误表,
- 访问模式,MCP配置边车和随附的MCP服务器,以及
- 部署说明(systemd单元,多主机通过Tailscale,测试)。
## 许可证
在MIT许可证下发布 — 请参见 [LICENSE](LICENSE)。