在隔离虚拟屏幕里跑 GUI 程序,面板实时查看操作
30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/12 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add jingyingruodi/dsh-headless-display该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:待复验
是什么
dsh 原生插件 · dev
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 20 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 60 处,展示前 24 条)
- 读取本地文件plugin.js:78
if (cmd.includes('/')) return existsSync(cmd) ? cmd : null - 读取本地文件plugin.js:82
if (existsSync(p)) return p - 执行外部命令来自配置/变量plugin.js:112
const p = spawn(bin.sway, ['--version'], { stdio: 'ignore' }) - 读取本地文件plugin.js:157
if (config.xauthority && existsSync(config.xauthority)) return config.xauthority - 读取本地文件plugin.js:158
if (process.env.XAUTHORITY && existsSync(process.env.XAUTHORITY)) return process.env.XAUTHORITY - 读取本地文件plugin.js:164
if (existsSync(dir)) { - 读取本地文件plugin.js:165
for (const f of readdirSync(dir)) if (f.startsWith('xauth_')) candidates.push(path.join(dir, f)) - 删除本地文件plugin.js:344
try { await rm(stateFile(display), { force: true }) } catch {} - 删除本地文件plugin.js:378
try { await rm(sock, { force: true }) } catch {} - 删除本地文件plugin.js:379
try { await rm(lock, { force: true }) } catch {} - 删除本地文件递归plugin.js:664
try { await rm(runDir, { recursive: true, force: true }) } catch {} - 写入本地文件plugin.js:665
await mkdir(runDir, { recursive: true, mode: 0o700 }) - 写入本地文件plugin.js:666
try { await chmod(runDir, 0o700) } catch {} - 写入本地文件plugin.js:671
await writeFile(confPath, [ - 执行外部命令来自配置/变量plugin.js:700
const child = spawn(bin.sway, ['-c', confPath], { detached: true, stdio: 'ignore', env }) - 删除本地文件递归plugin.js:742
try { await rm(runDir, { recursive: true, force: true }) } catch {} - 写入本地文件plugin.js:765
await writeFile(stateFile(target), JSON.stringify(st, null, 2)) - 执行外部命令来自配置/变量plugin.js:801
const child = spawn(binName, args, { detached: true, stdio: 'ignore', env }) - 写入本地文件plugin.js:824
await writeFile(stateFile(target), JSON.stringify(st, null, 2)) - 写入本地文件plugin.js:839
await mkdir(stateDir, { recursive: true }) - 删除本地文件递归plugin.js:921
if (st?.runDir) { try { await rm(st.runDir, { recursive: true, force: true }) } catch {} } - 执行外部命令来自配置/变量plugin.js:995
const vnc = spawn(vncExe, args, { detached: true, stdio: 'ignore', env: vncEnv }) - 执行外部命令来自配置/变量plugin.js:1029
const viewer = spawn(bin.viewer, viewerArgs, { detached: true, stdio: 'ignore', env: viewerEnv }) - 发起网络请求client.js:30
const res = await fetch(API + path, opts)
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 隔离虚拟屏幕运行GUI
- 面板实时查看操作
- 按需弹出真实窗口
隔离的 headless 虚拟 X display:默认桌面上零窗口、零干扰;面板里可实时查看与操作,按需弹出真实窗口。给 Agent 9 个工具 + HTTP API。仅 2D/X11。
README
由 DeepSeek 最新模型翻译生成dsh-headless-display
在隔离的虚拟 X display 里运行并操作 GUI 程序 —— 默认桌面上零窗口、零干扰。
给智能体提供 9 个工具,并在 DSH Web 界面里提供一个可看可点的实时画面面板。
- 默认后端 Xvfb(真 headless):桌面上不开任何窗口
- 想看/想亲手操作时,用面板上的「打开真实窗口」按需弹出(x11vnc + VNC 查看器)
- 可选后端 Xephyr:直接在用户桌面上开一个可见窗口(旧的可见行为)
本插件由 dsh-ego-browser 与自建的 tools/isolated_display.sh 演进而来;设计目标见下文「为什么需要隔离 display」。
名称沿革(为什么改过几次名)
@local/isolated-display → @local/gui-isolation → dsh-isolated-x11 → dsh-headless-display
改动原因不是反复无常,而是 dsh 的一个硬约束:宿主插件模块按「包名」做进程级缓存 (详见下文「维护者必读」第 2 个坑)—— 改了 JS 之后, set_plugin 开关、remove_bundle + install_bundle、改入口文件名、换目录路径 都无法加载新代码,只有换包名或重启 dsh 才行。
展开完整文档(README 剩余 1 节)
dsh-headless-display
在隔离的虚拟 X display 里运行并操作 GUI 程序 —— 默认桌面上零窗口、零干扰。
给智能体提供 9 个工具,并在 DSH Web 界面里提供一个可看可点的实时画面面板。
- 默认后端 Xvfb(真 headless):桌面上不开任何窗口
- 想看/想亲手操作时,用面板上的「打开真实窗口」按需弹出(x11vnc + VNC 查看器)
- 可选后端 Xephyr:直接在用户桌面上开一个可见窗口(旧的可见行为)
本插件由 dsh-ego-browser 与自建的 tools/isolated_display.sh 演进而来;设计目标见下文「为什么需要隔离 display」。
名称沿革(为什么改过几次名)
@local/isolated-display → @local/gui-isolation → dsh-isolated-x11 → dsh-headless-display
改动原因不是反复无常,而是 dsh 的一个硬约束:宿主插件模块按「包名」做进程级缓存 (详见下文「维护者必读」第 2 个坑)—— 改了 JS 之后, set_plugin 开关、remove_bundle + install_bundle、改入口文件名、换目录路径 都无法加载新代码,只有换包名或重启 dsh 才行。
所以开发期每改一次宿主代码就得换一次名;最后一次改名同时反映了行为变化(默认后端从 Xephyr 改为 headless 的 Xvfb)。GitHub 仓库也随之改名,旧链接会自动重定向;旧提交信息里的名字是当时的历史事实,保留不改写。
目录
- 为什么需要隔离 display
- 功能
- 安装
- 配置
- Agent 工具
- HTTP API
- Web UI 面板
- 架构
- ⚠️ 维护者必读:三个已经踩过的坑
- 已知限制
- 部署到其他设备
- 故障排查
- 许可证
三种后端:默认静默 / 按需弹窗
| 后端 | 桌面上有窗口 | DRI3 | Vulkan | 适用 |
| sway(auto 下的首选) | 否 | 有 | GPU(RADV 等) | Proton / DX12 / 3D 游戏,也兼容 2D |
| xvfb | 否 | 无 | 仅 lavapipe(CPU 软渲) | 2D / X11 程序,最轻量 |
| xephyr | 是(会打扰用户) | 无 | ✗ | 想一边跑一边直接看真实窗口 |
backend: auto(默认):探测到 sway 就用 sway,否则退回 xvfb。
显式写 sway / xvfb / xephyr 可强制指定。
为什么 3D 游戏必须用 sway:Xvfb 与 Xephyr 都不提供 DRI3, Vulkan 的 WSI 会打印 No DRI3 support detected - required for presentation 并退到 llvmpipe; UE/Unity 的 3D 游戏往往直接崩(Desynced 在 Xvfb 下抛 EXCEPTION_INT_DIVIDE_BY_ZERO)。
sway 后端起一个 私有 XDG_RUNTIME_DIR 的 headless sway,它自带 Xwayland —— Xwayland 提供 DRI3,Vulkan/GL 因而直通 GPU。
它的 wayland socket 在私有 runtime dir 里,与用户桌面会话完全不相干。
想看真实画面、想亲手操作时,不必切换后端:用面板上的 「打开真实窗口」(或工具 iso_display_popout)。它在该 display 上启动 只监听本机回环的 x11vnc,再用 VNC 查看器开一个窗口;关掉即回到静默, 里面的程序不用重启。这与 dsh-ego-browser「浏览器静默跑、需要时弹出真实页」是同一个模型。
⚠️ 已知限制:KDE Wayland 下,XWayland 窗口抬不到原生 Wayland 窗口之上,所以弹出的查看器窗口可能不会自动到最前 —— 可从任务栏点击它。
这是平台行为,不是插件能绕过的。
为什么需要隔离 display
目标机器的桌面往往是 KDE / GNOME 的 Wayland 会话。在 Wayland 主桌面上:
| 想做的事 | 现实 |
| 把输入注入到非焦点窗口 | ❌ XTEST 只打给当前焦点;XSendEvent 多数程序(含 Proton/UE)不理会 |
| 抓某个窗口的图 | ❌ XWayland 下 import -window root 直接 BadMatch |
| 安静地跑自动化 | ❌ 一定会抢焦点、占满屏,干扰用户 |
解法:另起一个独立的 X server,自动化全部在它内部进行,与用户会话完全隔离。
- auto/sway(推荐):headless sway + Xwayland,桌面零窗口且有 GPU。
- Xvfb:纯 headless、最轻,但无 GPU(3D 游戏跑不了)。
- Xephyr:在用户桌面上开一个普通窗口,内部同样是独立 X server。
在这个独立 display 里,XTEST / XSendEvent / 抓屏全部正常。
功能
- 9 个 Agent 工具:启停隔离 display、在其中跑命令、抓屏、列窗口、注入键鼠、以及按需弹出/收起真实窗口
- Web 侧边栏面板:实时画面 + 点击操作 + 文本/按键注入 + 窗口列表 + 真实窗口开关
- 宿主 HTTP API:8 条 /gui-isolation/ 路由,面板与外部脚本都可用
- 零 npm 依赖:纯 JS,无构建步骤
安装
前置(Linux / X11 或 Wayland)
sway 后端(默认 auto 下首选;跑 3D / Proton 游戏必需)
sudo apt install sway grim x11-utils xdotool imagemagick
说明:sway 自带 Xwayland;grim 用于抓屏(Xwayland 下 import 取不到根窗口)
xvfb 后端(无 GPU 时的退路 / 纯 2D 自动化)
sudo apt install xvfb
「打开真实窗口」所需
sudo apt install x11vnc gvncviewer
想用可见窗口后端(backend: xephyr)再装:
sudo apt install xserver-xephyr
验证 command -v sway grim Xvfb Xwayland xdpyinfo xdotool import identify x11vnc gvncviewer
sway 能跑但 GPU 不可用? 确认当前用户在 render 组里(id -nG | tr ' ' '\n' | grep render); wlroots 的 headless 后端要打开 /dev/dri/renderD。
- Xephyr —— 隔离 display 本体
- xdpyinfo —— 探活
- xdotool —— 注入键鼠
- import / identify(ImageMagick)—— 抓屏
安装插件
把本目录放到任意位置,然后在 DSH 里安装该目录 bundle:
plugin_manager(action: "install_bundle", target: "<本目录的绝对路径>")
安装后确认:
plugin_manager(action: "list_bundles") # 应出现 dsh-headless-display,rows: gui-isolation
验证
iso_display_start → {"display": ":9", "size": "1280x720", ...}
iso_display_run ":9" sh ["-c", "setsid xclock &"]
iso_display_capture ":9" → PNG 路径 iso_display_stop ":9"
跑 Windows / DX 游戏(Proton / Steam 兼容套件)
已实测:Desynced(Unreal 引擎、DX12、只有 Win64 二进制)在 sway 后端的隔离 display 里完整渲染、有声音、桌面上零窗口。
要点
1. 必须用 sway 后端 —— Xvfb/Xephyr 无 DRI3,Vulkan 退到 llvmpipe, UE 游戏直接抛 EXCEPTION_INT_DIVIDE_BY_ZERO。
2. 绕开 Steam 客户端直接调 Proton —— steam -applaunch 由 Steam 客户端进程带着它自己的环境启动游戏,调用方设的 DISPLAY 传不进去。
3. 用 iso_display_run + setsid ... &(该工具是同步的,GUI 程序必须后台化)。
调用模板
cat > /tmp/run-with-proton.sh <<'EOF'
#!/bin/bash
export STEAM_COMPAT_CLIENT_INSTALL_PATH="$HOME/.local/share/Steam"
该游戏在 Steam 里的 compatdata 前缀(Steam 通常已经建好)
export STEAM_COMPAT_DATA_PATH="/path/to/SteamLibrary/steamapps/compatdata/<APPID>"
export STEAM_COMPAT_INSTALL_PATH="/path/to/SteamLibrary/steamapps/common/<GAME>"
export SteamAppId=<APPID>
export SteamGameId=<APPID>
PROTON="/path/to/SteamLibrary/steamapps/common/Proton - Experimental/proton"
GAME="/path/to/.../Binaries/Win64/<GAME>.exe"
exec "$PROTON" run "$GAME" "$@"
EOF
chmod +x /tmp/run-with-proton.sh
然后:
iso_display_start → :2(backend: sway, gpu: true) iso_display_run ":2" sh ["-c", "setsid /tmp/run-with-proton.sh -log > /tmp/game.log 2>&1 &"] iso_display_capture ":2" → 截图,确认渲染 iso_display_stop ":2"
踩过的坑
| 现象 | 原因 / 解法 |
| 抓屏全黑 / 报 import: ... BadMatch | Xwayland 下 import -window root 取不到根窗口。插件已自动改用 grim 抓 Wayland 输出;手工抓屏时记得给 XDG_RUNTIME_DIR + WAYLAND_DISPLAY,别只给 DISPLAY |
| steam -applaunch 起在了桌面上 | 见上文第 2 点,改直接调 proton |
| Proton 版本与前缀不匹配 | compatdata/<APPID>/version 记着建前缀用的版本(本机是 11.0-100)。用别的版本会触发前缀更新,慢但可用 |
| 起不来但无报错 | 看 /tmp/game.log;pgrep -x <GAME>.exe 确认进程是否真的起来了 |
配置
在 cordis.patch.yml 的 config 里改(安装后也可在 DSH 的插件页配置):
| 键 | 默认 | 说明 |
| backend | auto | auto=有 sway 就用它、否则 xvfb;也可强制 sway / xvfb / xephyr |
| parentDisplay | $DISPLAY 或 :1 | 承载 Xephyr 窗口的桌面 display(仅 backend: xephyr 需要) |
| xauthority | 自动探测 | X 授权文件(sway 后端的 Xwayland 用它自己那份,插件自动发现) |
| defaultSize | 1280x720 | 默认分辨率(sway 后端同时作用于虚拟输出) |
| displayRange | [9, 20] | 可用的 display 编号区间(仅 xvfb/xephyr;sway 的 display 号由 Xwayland 自己挑) |
| stateDir | ~/.dsh-headless-display | 记录已启动 display 的目录 |
| swayRunDirBase | /run/user/<uid>/dsh-iso | sway 实例的私有 XDG_RUNTIME_DIR 前缀 |
| vncPortBase | 5900 | 弹出真实窗口时 x11vnc 的起始端口(只监听回环) |
| swayPath / grimPath / xephyrPath / xvfbPath / x11vncPath / viewerPath / xdotoolPath / importPath | 按名字在 PATH 查找 | 自定义可执行文件路径 |
⚠️ parentDisplay 与 xauthority 建议显式配置 —— 见下面第 3 个坑。
智能体工具
| 工具 | 参数 | 作用 |
| iso_display_start | size? display? | 启动隔离 display,返回 {display, size, pid} |
| iso_display_list | — | 列出已知 display 及是否存活 |
| iso_display_stop | display | 停止并清理状态记录 |
| iso_display_run | display command args? timeoutMs? | 在该 display 里执行命令(同步,GUI 程序请用 setsid ... &) |
| iso_display_capture | display outPath? | 整屏截图,返回 PNG 路径与尺寸 |
| iso_display_windows | display | 列顶层窗口 |
| iso_display_input | display kind key? text? x? y? window? | 注入输入;kind ∈ key type click focus |
window 参数很关键:给出窗口 id 时 key/type 走 XSendEvent 定向投递,不依赖焦点;不给则走 XTEST(依赖焦点,全屏程序自己会持有焦点时够用)。
HTTP API
全部挂在 /gui-isolation/ 下,由宿主 Web 服务提供(默认 http://127.0.0.1:3080)。
| 方法 | 路径 | 说明 |
| GET | /gui-isolation/state | {displays:[{display,alive,size,pid}], defaultSize, parentDisplay} |
| GET | /gui-isolation/snapshot?display=:9 | image/png 整屏截图 |
| GET | /gui-isolation/windows?display=:9 | {windows:[...]} |
| POST | /gui-isolation/start | body {size?, display?} |
| POST | /gui-isolation/stop | body 或 query display |
| POST | /gui-isolation/input | body {display, kind, key?, text?, x?, y?, window?} |
示例:
curl -X POST -H 'content-type: application/json' -d '{"size":"1024x768"}' \
http://127.0.0.1:3080/gui-isolation/start
curl -o shot.png "http://127.0.0.1:3080/gui-isolation/snapshot?display=%3A9"
curl -X POST -H 'content-type: application/json' \
-d '{"display":":9","kind":"click","x":100,"y":200}' \
http://127.0.0.1:3080/gui-isolation/input
这些路由不受 dsh 的浏览器鉴权 cookie 限制(实测 curl 直接可用),但默认只监听本机回环地址。不要把 3080 暴露到公网。
Web UI 面板
client.js 提供一个「隔离显示」面板:实时画面(约 0.9s 轮询)、点画面即在该坐标点击、文本输入、常用按键、窗口列表、启停按钮,以及「打开真实窗口 / 关闭真实窗口」开关。
落点策略:
- 装了 dsh-better-sidebar → 注册成右侧栏标签页(ctx.betterSidebar.registerTab)。
标签页渲染在侧栏那一列内部,结构上不可能覆盖其它区域。
- 没装 → 不注册任何 UI(智能体工具与 HTTP API 仍然可用)。
⚠️ 曾经有一个「回退到 shell.overlay 浮动面板」的分支,已彻底删除: shell.overlay 是贯穿整个窗口的浮层,配上 height:100% + 不透明背景的面板会把整个 Web UI 盖死,用户点不到任何东西,只能从外部远程窗口通知智能体才回滚掉。
教训:绝不要把大尺寸组件挂到 frame-wide 浮层上;宁可没有 UI,也不能挡住用户。
样式只用宿主主题 token(--dsw-alias-),不引入任何 Harness Client 包。
架构
┌─ 浏览器 ────────────────────────────────┐ │ client.js → 右栏标签页(better-sidebar) │ │ fetch /gui-isolation/snapshot (轮询) │ │ fetch /gui-isolation/input (点击) │ └───────────────┬─────────────────────────┘ │ HTTP ┌───────────────▼─────────────────────────┐ │ plugin.js(宿主) │ │ ctx.tools.register(...) × 9 个工具 │ │ ctx.webServer.register(...) × 8 路由 │ │ ctx.betterSidebar 由 client 侧使用 │ └───────────────┬─────────────────────────┘ │ execFile ┌───────────────▼─────────────────────────┐ │ Xvfb(默认,headless) / Xephyr(可选) │ │ 独立 X server(如 :9) │ │ 默认桌面上无窗口;按需 popout 出真实窗口 │ │ 你的被测程序跑在这里 │ └─────────────────────────────────────────┘
后端可插拔:所有 display 操作集中在 start/stop/snapshotBuffer/windows/input 几个函数里。
要接第二个后端(例如 MaaFramework 修好后的 WlrRootsController),在这几个函数上分派即可。
⚠️ 维护者必读:三个已经踩过的坑
1. 插件的 Config 不能是裸 JSON Schema
Cordis 的实现是:
function resolveConfig(runtime, config) {
if (!runtime.Config) return config;
const result = runtime.Config["~standard"].validate(config); // ← 需要 Standard Schema ... }
声明成裸 JSON Schema 会报 Cannot read properties of undefined (reading 'validate'), 插件直接激活失败。零依赖的纯 JS bundle 拿不到 schema 库,所以干脆不要声明 Config,把默认值写在代码里。
(/home/user/deepseek-harness/vendor/cordis/lib/index.js:956)
2. ★ 模块被进程级缓存:改了 JS 代码,重装也不会加载新代码
实测结论(2026-10-11):
| 手段 | 能否加载新代码 |
| set_plugin 关→开 | ❌ |
| remove_bundle + install_bundle | ❌ |
| 改入口文件名(index.js → plugin.js) | ❌ |
| 换目录路径(同样的包名) | ❌ |
| 换包名 | ✅ |
缓存按包名键控。所以:
开发期每改一次宿主代码,就要换一次包名,或者重启 dsh。
正式维护时,改完代码重启 dsh 是最省事的路径(规范也写明「替换已安装的包需要重启」)。
3. dsh 宿主进程不继承桌面的 DISPLAY / XAUTHORITY
实测宿主进程的 environ 里这两个变量都没有。因此:
- 不配置就会连不上 X server(Xephyr 起不来 / xdotool 报错)
- 代码里有自动探测兜底:$XAUTHORITY → /run/user/<uid>/xauth_ → ~/.Xauthority
- 但显式配置最稳(见 cordis.patch.yml 的 parentDisplay / xauthority)
4. 判断 betterSidebar 可用性不能用 ctx.get()
better-sidebar 是通过 ctx.provide("betterSidebar", service) 发布服务的,它晚于本插件 apply。所以:
// ❌ 错:此刻拿到 undefined,标签页永远不注册(表现为「右栏根本没有这个选项」)
if (!ctx.get('betterSidebar')) return
ctx.inject(['betterSidebar'], cb)
// ✅ 对:Cordis 会等待依赖就绪;真的没有时回调不执行,自然没有任何 UI
ctx.inject(['betterSidebar'], cb)
另外:betterSidebar 不在 Cordis 的 Service 目录里,
cordis_inspect_query(platform:"client", provider:"Service") 查不到它 ——
别因为查不到就以为它不存在。
已知限制
| 限制 | 说明 |
| xvfb / xephyr 跑不了 Vulkan / 3D | 两者都不提供 DRI3。vulkaninfo 会报 No DRI3 support detected - required for presentation,Vulkan 退到 llvmpipe;UE 游戏启动即 EXCEPTION_INT_DIVIDE_BY_ZERO。3D / Proton 游戏请用 sway 后端(有 DRI3,Vulkan 直通 GPU)。 |
| iso_display_run 是同步的 | 跑不会退出的 GUI 程序会占满超时。用 sh -c "setsid <程序> &" 后台起。 |
| 画面靠轮询 | 约 0.9s 一帧。要更流畅可改成 WebSocket + MJPEG/VP8 流。 |
| 无输入法/多指手势 | 只有 xdotool 能力范围内的键鼠。 |
| 默认只监听回环 | 不要把 3080 暴露到公网。 |
| 弹出窗口不一定到最前 | KDE Wayland 下 XWayland 窗口抬不过原生 Wayland 窗口(平台限制) |
| backend: xephyr 会开窗口 | 会打扰用户;除非确实需要,否则保持默认 auto |
| sway 启动慢于 xvfb | 要连起 wlroots + Xwayland 两个进程,首次约 3–6 秒(xvfb 约 1 秒)。换来的是 GPU。 |
| sway 需要 render 组 | wlroots headless 要打开 /dev/dri/renderD。不在组里会退到软件渲染(仍能跑,只是慢)。 |
部署到其他设备
1. 装前置(见上)
2. 取到本目录(git clone 或拷贝)
3. 改 cordis.patch.yml:
- parentDisplay 改成目标机器的桌面 display(echo $DISPLAY)
- xauthority 改成目标机器的 X 授权文件(ls /run/user/$(id -u)/xauth_)
4. 安装:plugin_manager(action:"install_bundle", target:"<目标机上的绝对路径>")
5. 验证:iso_display_list → iso_display_start → iso_display_capture
6. 装了 dsh-better-sidebar 时,刷新页面应能在右栏看到「隔离显示」
目标机没有 Xephyr 时,也可以退而使用自建的 tools/isolated_display.sh(功能等价、命令行驱动,不依赖 dsh 插件系统)。
故障排查
| 现象 | 原因 / 处理 |
| 插件行 fiberPhase: failed,报 ...reading 'validate' | 声明了 Config —— 删掉它(见坑 1) |
| 改了代码但行为没变 | 模块缓存(见坑 2)—— 换包名或重启 dsh |
| iso_display_start 报「Xephyr 未能在 :N 上就绪」 | parentDisplay 或 xauthority 不对(见坑 3);手动跑一次 DISPLAY=:1 Xephyr :9 -screen 1280x720 -ac 看报错 |
| 抓屏是纯黑/纯色 | 该 display 里确实没有窗口;先用 iso_display_run 起个程序 |
| 面板里没有画面 | 确认 display 存活(iso_display_list),以及 /gui-isolation/state 能返回 |
| 3D 程序秒退 / 报 VKD3D 或 D3D 错误 | 该后端无 DRI3。改用 sway(见「已知限制」) |
| sway 后端启动超时 | 手动验证:WLR_BACKENDS=headless WLR_LIBINPUT_NO_DEVICES=1 sway -c /dev/null,看能否起来;再 command -v Xwayland grim |
| sway 起得来但游戏报 Vulkan 找不到设备 | 检查 id -nG 是否含 render;ls -l /dev/dri/renderD* |
| grim 报找不到 wayland socket | 抓屏必须带该实例的 XDG_RUNTIME_DIR + WAYLAND_DISPLAY(插件已自动处理;手工抓屏常忘) |
| 页面看不到「隔离显示」标签 | 需要刷新页面;并确认 dsh-better-sidebar 已启用 |
文件
| 文件 | 说明 |
| plugin.js | 宿主:9 个工具 + 8 条 HTTP 路由 |
| client.js | 浏览器:右栏标签页(better-sidebar 缺失时不注册任何 UI) |
| cordis.patch.yml | bundle patch(插入 gui-isolation 行 + 配置) |
| locale/{en,zh}.json | 插件卡片标题与描述 |
| icon.svg | 插件图标 |
许可证
MIT © 2026 jingyingruodi预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
jingyingruodi/dsh-headless-display
在隔离虚拟屏幕里跑 GUI 程序,面板实时查看操作
相关插件 · 开发 / 工程
隐私简历生成器reactive-resume/reactive-resume
Reactive Resume 已迁至 GitHub 上的…
★ 45,125已汉化MCP
71.7
AI 架构文档生成器sopaco/deepwiki-rs
🚀 Litho 已进化为 Terrain —— 给你的 AI 智能体一张会呼吸的代码库地图。
★ 3,157已汉化DSH
70.8
长时任务执行闭环AMAP-ML/LongHorizon-Harness
面向 Computer-Use Agent 的 Loop Engineering
★ 1,764已汉化DSH
70.5
AI 编程纪律方法包GanyuanRan/Aegis
让 AI 编程 agent 变得可信:少返工、更安全、说“完成”前先给证据。
★ 1,384已汉化DSH
69.6
多代理编排运行时Enderfga/claw-orchestrator
面向编程智能体的运行时。把 Claude Code、Codex、Antigravity、Grok…
★ 586已汉化MCP
69.4
记忆进化插件csyangwen/dsh-memory-evolve
dsh-memory-evolve 使用场景指南
★ 461已汉化DSH
67.9