dsh 局域网访问部署教程
August 16, 2026 · View on GitHub
适用对象:已在 Android 手机 Termux 上部署好 dsh(
dsh web能跑、能在本机用)的用户。 目标:同一 WiFi 下的平板 / 电脑 / 其他手机,用浏览器直接打开手机上的 dsh——看到手机上的对话、能改设置、能建工作区,客户端零安装。
一、总体架构
客户端浏览器 ──> http://<手机IP>:3088
│
▼
┌──────────────────────────┐
│ dsh-lan-proxy.js (0.0.0.0:3088) │ 手机端
│ ① 开关检查 ~/.dsh-lan-on │
│ ② Host/Origin 改写成回环 │
│ ③ 注入 crypto.randomUUID 补丁 │
└───────────┬──────────────┘
▼
dsh web (127.0.0.1:3080) ← dsh 本体,只监听回环
| 端口 | 进程 | 监听 | 作用 |
|---|---|---|---|
| 3080 | dsh web | 127.0.0.1 | dsh 本体(CLI 故意禁 --host 0.0.0.0) |
| 3088 | dsh-lan-proxy.js | 0.0.0.0 | 局域网唯一入口:转发 + 回环改写 + 注入补丁 |
| 3090 | dsh-llm-proxy.js | 127.0.0.1 | LLM 代理(可选,用于上游网关适配) |
| 3098 | dsh-ctl-server.js | 127.0.0.1 | 控制 API(开关 / 重启 / 状态,供控制台 App 调用) |
二、工作原理(为什么必须这么做)
dsh 是单设备设计,局域网访问被三层障碍挡住,必须逐层绕开:
| # | 障碍 | 现象 | 根因 | 解法 |
|---|---|---|---|---|
| 1 | 客户端 loopback 门 | LAN 打开页面后只调 inventory 同步(value:[]),workspace.list / session.list / settings.describe 一个都不发,UI 永远卡"正在加载工作区" | 客户端 JS 计算 isLoopback(非回环 hostname → false),整个数据流不初始化 | 源码补丁:强制 isLoopback: true |
| 2 | 服务端特权围栏 | 设置 / 凭据 / 建工作区返回 403 | PRIVILEGED_METHODS 特权表只对回环连接开放(判断写死 []) | 源码补丁:3 处放开为 this.trustedHosts |
| 3 | 🔑 crypto.randomUUID 缺失(真正的元凶) | 页面 console 反复报 WebSocket is closed before the connection is established,WS 23ms 内被 abort,连接永不建立 | dsh 客户端用 crypto.randomUUID() 生成 RPC ID,但它只在安全上下文(HTTPS / localhost)存在;LAN 的 http://<IP>:3088 是非安全上下文 → 抛错 → RPC 全崩 → 连接中止 | 代理层:给 HTML <head> 注入 polyfill |
双层加固设计:
- 服务端(rebrain.sh 幂等打补丁):解决障碍 1、2——改的是 dsh 包内 JS,升级后重跑即重打
- 代理层(dsh-lan-proxy.js):解决障碍 3 + Host/Origin 回环改写——不碰 dsh 本体,任何 LAN 设备打开页面即生效
⚠️ 只测 curl = 假阳性:API 层 200 不代表 UI 能用(障碍 1 在客户端 JS,curl 不触发)。必须用真浏览器验证(见第五节)。
三、部署步骤
1. 准备 bundle(5 个文件)
| 文件 | 作用 |
|---|---|
dsh-lan-proxy.js | LAN 代理(核心:开关 + 回环改写 + polyfill 注入) |
dsh-ctl-server.js | 控制 API 服务 |
start-dsh.sh | 启动脚本(自动检测 wlan0 IP → 起 4 个进程) |
rebrain.sh | 幂等重装脚本:打全部源码补丁 + 部署配置 + 拉起链 |
dsh-supervise.sh | 保活监督循环(进程死 5s 自动重启) |
2. 推送到手机
cd <bundle目录>
for f in dsh-lan-proxy.js dsh-ctl-server.js start-dsh.sh rebrain.sh dsh-supervise.sh; do
adb push $f /sdcard/Download/$f
done
adb shell "run-as com.termux sh -c '
for f in dsh-lan-proxy.js dsh-ctl-server.js start-dsh.sh rebrain.sh dsh-supervise.sh; do
cat /sdcard/Download/\$f > /data/data/com.termux/files/home/dsh-brain-swap/\$f
done
rm -f /sdcard/Download/dsh-lan-proxy.js /sdcard/Download/dsh-ctl-server.js /sdcard/Download/start-dsh.sh /sdcard/Download/rebrain.sh /sdcard/Download/dsh-supervise.sh
'"
⚠️ 必须用
cat >注入:run-as com.termux的 shell 下cp会静默写出 0 字节文件。
3. 打补丁并启动
adb shell "run-as com.termux sh -c '
pkill -f \"[d]sh-supervise[.]sh\" 2>/dev/null
sleep 1
REBRAIN_NO_LAUNCH=1 sh /data/data/com.termux/files/home/dsh-brain-swap/rebrain.sh
'"
# 上面输出 "already patched" / "patched" 即补丁幂等生效
adb shell "run-as com.termux sh -c '
setsid sh /data/data/com.termux/files/home/dsh-brain-swap/dsh-supervise.sh \
> /data/data/com.termux/files/home/dsh-supervise.log 2>&1 < /dev/null &
'"
sleep 40 # 冷启动等待
4. 验证部署
# 补丁是否打上
adb shell "run-as com.termux sh -c '
F=/data/data/com.termux/files/usr/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-client-connection
grep -c \"isTrustedApiRequest(request, this.trustedHosts)\" \$F/lib/index.js # 期望 ≥2
grep -c \"LAN-patch\" \$F/lib/client.js # 期望 ≥1
grep -c LOOPBACK_HOST \$HOME/dsh-lan-proxy.js # 期望 ≥1
'"
# 四个端口 + 当前 IP
adb shell "run-as com.termux sh -c '
export PATH=/data/data/com.termux/files/usr/bin:\$PATH
for p in 3080 3088 3090 3098; do curl -s -m 2 -o /dev/null -w \"\$p=%{http_code} \" http://127.0.0.1:\$p/; done
echo; ip route get 1 | grep -oE \"src [0-9.]+\" | cut -d\" \" -f2
'"
4 端口全部 200 = 部署成功,记下输出的 IP。
四、使用
- 客户端浏览器打开
http://<手机IP>:3088(IP 变了重新执行上条命令查询;或装配套控制台 App,实时显示大绿色地址) - 即可看到手机上的工作区、对话、设置、模型选择——与手机上操作一致
- 首次打开有"内测声明"弹窗,点继续
五、真浏览器验证(判定"能用"的标准)
用电脑 Edge/Chrome 打开 http://<手机IP>:3088,全部满足才算通过:
- ✅ 页面出现手机上已有的工作区和对话内容
- ✅ 开发者工具 Network 里调用了
workspace.list/session.list/settings.describe等数据 API(≥20 个) - ✅ localStorage 出现
dsh.workspace.view.v5等键 - ✅ 设置页可打开、可新建工作区、可发消息
只看到页面但"一直加载"= 障碍 1 未解决;设置 403 = 障碍 2 未解决;WS 疯狂重连 = 障碍 3 未解决。对照第二节定位。
六、排查速查
| 现象 | 原因 | 处理 |
|---|---|---|
打不开 http://<IP>:3088 | IP 不对 / 链死了(系统杀后台)/ 开关关了 | 重查 IP;重启链;开启关 |
| 一直"正在加载工作区" | 代理旧版(无 polyfill)或补丁丢失 | 重跑 rebrain;确认两个 grep -c 通过 |
| 设置 / 凭据 403 | 特权围栏补丁没打上 | 重跑 rebrain |
| 手机本地 3080 正常、LAN 全挂 | 多个代理进程抢 3088 | pkill -f "[d]sh-lan-proxy[.]js" 后重启链,确保只剩 1 个代理进程 |
| 发消息 429 | 上游 LLM 限流窗口 | 等几分钟重试,与 LAN 无关 |
七、安全与回滚
- 安全:所有改动都在手机 Termux 内、可逆,不碰路由器。注意开关打开期间 WiFi 内网任何设备都能访问手机 dsh——家用 WiFi 无碍,公共网络请关闭开关(
~/.dsh-lan-on删除即关)。 - 回滚:
pkill -f "[d]sh-supervise[.]sh"停链 → 删~/.termux/boot/99-dsh.sh撤自启 → 重装 dsh 包还原补丁 → 删~/dsh-*.js、~/dsh-brain-swap/即完全撤掉。 - 升级 dsh 后:重跑
rebrain.sh即自动重打全部补丁(幂等)。
八、致谢
- 感谢 DeepSeek Harness (dsh) 团队——出色的本地 AI 智能体框架;本方案所有补丁均为对运行文件的独立适配,不修改其任何版权代码,官方升级后重跑
rebrain.sh即可重打。 - 感谢 Termux 与 Termux:Boot——本方案依赖的 Android 终端环境与开机自启机制。
- 感谢商汤 SenseNova 提供的 OpenAI 兼容网关(可选上游)。
- 特别感谢本项目的测试与反馈者。
九、友情链接
| 项目 | 链接 | 说明 |
|---|---|---|
| DeepSeek Harness (dsh) | https://github.com/deepseek-ai/dsh | 本方案的服务端本体 |
| Termux | https://github.com/termux/termux-app | Android 终端模拟器 |
| Termux:Boot | https://github.com/termux/termux-boot | 开机自启支持 |
| puppeteer | https://github.com/puppeteer/puppeteer | 教程验证章节使用的浏览器自动化工具 |
如果你有同类项目(dsh 增强 / Termux 工具 / 局域网访问方案),欢迎在 Issue 中提交,我会添加到本表。