dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
基于 GTK4 + WebKitGTK 6.0 的 dsh web 启动器。
可以作为 deepseek-harness 的 gtk4 desktop 桌面版使用。
注:这只是一个启动器,不包含 dsh,也不预装任何额外插件,
启动时会执行 npx --loglevel verbose --yes @deepseek-ai/dsh web --no-open --port 0
目的是保证使用的是最新版官方 dsh。
Debian/Ubuntu:
sudo apt install python3-gi gir1.2-gtk-4.0 gir1.2-webkit-6.0还需要 npx 命令在 PATH 中。
git clone https://github.com/shellexy/dsh-webviewgtk.git
cd dsh-webviewgtkpython3 dsh-webviewgtk.py启动后会:
Loading...;npx --loglevel verbose --yes @deepseek-ai/dsh web --no-open --port 0,由 dsh 自动选择空闲端口;dsh web: http://127.0.0.1:54321/?token=...,端口由 dsh 自动选择),解析成功后自动加载该地址;按 F12 或 Ctrl+Shift+I 打开/收起 WebKit 的开发者工具(停靠在窗口内,含 Console、 Elements、Network),加载页阶段也可以打开,用于排查页面或通知桥的问题。
dsh 官方 web UI 目前自己不产生任何通知,因此外壳在文档开始注入一段桥脚本,由它把 dsh 页面自己的会话状态翻译成桌面通知:
sessionId, running)驱动它自己的会话状态;桥脚本包住 MessageEvent.data 观察这条流,只看参数形状、不绑定事件名, running 变为 false 即本轮结束。
[data-approval-key](权限审批)、[data-question-key](ask_user 提问)、 [data-plan-review-key](计划确认)。桥用 MutationObserver 加 1 秒轮询发现,完全不依赖 任何 dsh 事件名;同一轮 ask_user 的多道题按"面板 key 去掉序号"归并为一次请求,只通知一次, 新的一次请求再通知。会话 id 取侧栏当前选中会话,点击通知即可切回去。 实测某些 dsh 版本并不把这类等待以"转发瀑布"送到浏览器,所以 DOM 是主路径;如果哪天真 收到 approval/request / user-questions/request 瀑布帧,它只用来把标题说得更准 (在等你批准 / 在等你回答),并覆盖"后台会话在等你、面板不在当前视图"这一种情况。
回到应用即视为已读,撤回本次运行弹出的通知。
关闭方式:在页面控制台执行 globalThis.__DSH_SHELL_NOTIFY_DISABLE__ = true; 也可在 dsh_webviewgtk.py 里改 BRIDGE_JS_TEMPLATE 中的 PENDING_MARKERS(DOM 选择器)、 PENDING_TRIGGERS(瀑布事件名,可选)与 PENDING_POLL_INTERVAL_MS 以适配后续 dsh 版本。
通知桥依赖 dsh 内部的两类事实:转发事件里 (sessionId, running) 的参数约定(不绑定名字), 以及上述 data-* 面板属性。dsh 升级后若某类通知不再出现,先在页面控制台确认这两者。
UserScriptInjectionTime.START 注入,那一刻 `` 还不存在。扫描器因此不能以 #root 为前提,只能挂在 documentElement 上并等 DOMContentLoaded (installScanner)。早期版本用 getElementById('root') === null 直接 return,导致扫描器 从未安装、除"完成"以外的通知全部无声失效。
load_uri 刚发出时 get_uri() 为空,此时桥已经在报到。校验必须先解析载荷、只对会驱动外壳动作的 completed / pending 校验来源,并在空 URI 时以"是否加载过 dsh 地址"(_trusted_uri)为准,否则真实通知会被自己的校验丢掉。
console.debug,桥的日志用 console.log。kind: 'diagnose' 回传终端,否则一个异常就会让整类通知消失且毫无线索。DSH_WEBVIEWGTK_DEBUG=1 时这些诊断会逐句打印。
script-message-received 回调拿到的既可能是 JSON 文本、也可能是已反序列化的对象,_parse_script_message 两种都接受。
# 外壳侧:打印实际注入的桥脚本、收到的每条载荷,以及被丢弃的原因;
# 同时把页面侧调试强制打开(每次页面加载都自带),无需手动在控制台设置。
DSH_WEBVIEWGTK_DEBUG=1 python3 dsh_webviewgtk.py成功时终端应出现(顺序即执行顺序):
[dsh-webviewgtk] 桥诊断:bridge-start href=http://127.0.0.1:3081/…
[dsh-webviewgtk] 桥诊断:scanner-deferred readyState=loading
[dsh-webviewgtk] 桥诊断:markers 0 … none
[dsh-webviewgtk] 桥诊断:scanner-ready root=DIV interval=1000ms
[dsh-webviewgtk] 桥诊断:markers 1 <会话id> <面板key> ← 卡片出现
[dsh-webviewgtk] 收到页面通知载荷:{'kind': 'pending', …}
桌面通知:DeepSeek Harness 在等你 — 有新的待处理交互scanner-ready root=none 或缺少该行,说明扫描器没装上(见上面第 1 条)。
页面侧(打开 dsh 页面的开发者控制台):
typeof globalThis.__DSH_SHELL_NOTIFY__ // 'function' = 桥在跑;'undefined' = 桥没注入
globalThis.__DSH_SHELL_NOTIFY_DEBUG__ = true // 每条观察到的帧 + 每 5 秒一次心跳自证
globalThis.__DSH_SHELL_NOTIFY_DISABLE__ = true // 关闭桌面通知[dsh-webviewgtk] 已注入的通知桥脚本:跑的是旧安装副本(pip show -f dsh-webviewgtk 能看到已安装的位置),重新安装或用 python3 dsh-webviewgtk.py 跑仓库版本。
收到页面通知载荷:桥脚本没跑起来或消息通道没注册(启动时应打印警告:无法注册通知桥消息通道),把控制台里 [dsh-shell-notify] 的输出一起看。
heartbeat … markers=0、面板出现时 没有 pending-panel:说明那个面板不带已知的 data-* 标识(或不在 #root 里)。把面板元素的 outerHTML 开头几百字符贴出来, 我按它真实用的标识改 PENDING_MARKERS。
heartbeat 都没有:调试标志没设上(要设 true,且设置后 5 秒内应出现第一条),或桥没注入(先看上一行 typeof 的结果)。
在对话输入框(contenteditable)里按鼠标中键会粘贴 PRIMARY selection(当前选中的 文字),与普通 textarea/单行输入框保持一致。
原因:WebKitGTK 对 contenteditable 的中键粘贴会派发普通 paste 事件,但事件里的 clipboardData 是 CLIPBOARD;页面据此插入就会粘进复制缓冲区。因此注入脚本在捕获阶段拦下 中键引发的 paste,请外壳用 Gdk.Display.get_primary_clipboard() 读出 PRIMARY,再以合成 paste 事件回填,页面的粘贴处理(清洗、撤销边界)保持不变。普通 textarea/input 走 WebKit 原生插入,不需要干预。
排查:DSH_WEBVIEWGTK_DEBUG=1 启动后,中键粘贴会在终端打印 回填 PRIMARY:'…';若为空串,说明 GTK 没读到 PRIMARY(Wayland 下主选区可能没设置)。
在仓库目录下执行:
pip install . --break-system-packages或:
python3 setup.py install --user安装后会提供 dsh-webviewgtk 命令,并安装:
dsh-webviewgtk 启动命令dsh-webviewgtk.svg 图标到 hicolor 图标主题com.github.shellexy.DshWebviewGtk.desktop 到 applications 目录(与通知使用的应用 id 一致)python3 tests/bridge_smoke_test.py # 通知桥脚本的解析逻辑(只需要 node)
python3 tests/notification_probe.py # 真实弹出一条通知,验证点击回调(需要桌面会话)
python3 tests/primary_paste_test.py # 中键粘贴走 PRIMARY 的桥逻辑(只需要 node)dsh-webviewgtk.svg。dsh-webviewgtk。com.github.shellexy.DshWebviewGtk.desktop 中声明了 StartupWMClass=dsh-webviewgtk。发生在主循环返回、解释器收尾之后,不影响使用。已确认与外壳逻辑无关:只建窗口的路径干净, 打开检查器也不复现;WEBKIT_DISABLE_DMABUF_RENDERER=1 python3 dsh_webviewgtk.py 可消除,因此判定为 WebKitGTK 2.52.6 退出期的 DMA-BUF 渲染缺陷。 不要在主循环返回后显式 webview.destroy() / window.destroy() 试图"清理"它: 实测会把无害提示变成段错误(核心已转储)。
各版本变更见 CHANGELOG.md。