📱 phone-bridge
v1.1.2
用手机跟电脑上的 DSH 对话:发文字、拍照、传图,回复实时回显。不同 Wi-Fi 也能用。
English | 中文
> 官方 Web 版和官方桌面端都支持。 dsh web 用 profile web;官方桌面 EXE(DeepSeek Harness 0.2.0-rc.2)用 profile desktop,两边分别安装、分别生效。
>
> 没有公网服务器,没有第三方中转。 手机和电脑在你的 Tailscale 账号所建的 WireGuard 私有网络里会合,不向公网暴露任何东西。
⬇️ 下载
Releases → v1.1.2
| 文件 | 装在哪 | 大小 |
|---|---|---|
phone-bridge-1.1.2.apk |
手机 | 249 KB |
phone-bridge-plugin-1.1.2.zip |
电脑(DSH 插件) | 5 MB |
三步上手:
# 1. 电脑装插件(然后重启 DSH)
dsh plugin --profile desktop add github:chs-1108/dsh-phone-bridge
# 2. 两端装 Tailscale 并登录(同一账号,或互相 share 设备)
# 电脑 https://tailscale.com/download 手机 Play Store
# 3. 手机装 APK,打开 → 扫 DSH「设置 → 手机同步」里的二维码
adb install -r phone-bridge-1.1.2.apk
APK 是通用包,不含任何写死的地址 —— 每个用户扫自己电脑的码,连自己的电脑。
>
> Node.js ≥18。原生 App 需要 Android 7.0(API 24) 以上;任意现代浏览器也能用。
目录
- 这是什么
- 亮点
- 环境要求
- 最快的方式:双击安装
- 下载与安装的完整流程
- 安装
- 手动安装
- 手机端
- 实现原理
- 隐私
- 排错
- 开发
- 许可
这是什么
phone-bridge 是官方 DeepSeek Harness 的插件,把 DSH 会话装进手机。手机打开一个小网页(或原生 Android 外壳),发文字和照片到真实的 DSH 会话,实时读到 Agent 的回复。
手机(WebView / 浏览器)
│ HTTP over Tailscale —— WireGuard 加密,点对点
▼
DSH webServer ──▶ /phone-bridge 前缀路由 ──▶ sessionController ──▶ 模型
只消费 DSH 公开的 webServer 和 sessionController 两个服务,不改核心、不 fork 宿主。
亮点
- 跨网络可用 —— 手机不需要和电脑同一个 Wi-Fi。两端各加入同一个 Tailscale tailnet,各拿一个固定的
100.x.y.z地址,在 Wi-Fi 和移动网络之间切换时 URL 都不变。 - 真实会话,不是旁路 —— 消息落到正常 DSH 会话里。可以用
/api/prefs钉到任意会话,也可以让它惰性创建并复用。 - 流式回复 —— 插件订阅
sessionController.follow({assistantStream: true}),手机轮询增量结果,长回答边写边出。 - 拍照与相册 —— 原生外壳把
onShowFileChooser接进系统选择器并带相机入口,照片经MediaStore写入,再交给 WebView 一个可读 URI。每条消息最多 8 张图。 - 不用 App 也能用 —— 纯浏览器方案:打开 URL、发文字、选图片即可。
- 一键部署 ——
setup-phone-bridge.ps1装 Tailscale、改监听、注册插件、构建 APK,并通过 USB 把两个 App 推进手机。只需要你自己的 Tailscale 账号。 - 不需要 Gradle —— APK 直接由
aapt2+d8+zipalign+apksigner构建,约十秒出包。
环境要求
| DSH | 0.2.0 以上,桌面 EXE 或 dsh web |
| Node.js | ≥ 18 |
| 宿主系统 | Windows 10/11(部署脚本是 PowerShell) |
| 手机 | Android 7.0+ 装 APK,或任意现代浏览器 |
| 账号 | 你自己的免费 Tailscale 账号 |
可选,仅构建 APK 时需要:JDK 17 和 Android SDK build-tools 34.0.0。可选,仅 USB 推送到手机时需要:adb。
最快的方式:双击安装
下载仓库后,双击 安装.cmd,会弹出一个窗口,然后跟着走。
整个过程只有两次需要你操作:
- 登录 Tailscale —— 浏览器会自动打开,用你自己的账号登录
- 插上手机 USB 线 —— 弹「允许调试」时点允许
其余全部自动:装 Tailscale、改监听地址、注册插件、生成配对密钥、探测端口、构建 APK、推送安装、授相机权限、健康检查。
> 详细说明见 安装说明.md。不想用图形界面就 install-gui.ps1 -Console,或走下面的命令行流程。
下载与安装
全部东西在同一个页面:Releases → v1.1.2
| 文件 | 装在哪 | 大小 |
|---|---|---|
phone-bridge-1.1.2.apk |
手机 | 249 KB |
phone-bridge-plugin-1.1.2.zip |
电脑(DSH 插件) | 0.9 MB |
装到电脑
# 方式一:从 GitHub 直接装
dsh plugin --profile desktop add github:chs-1108/dsh-phone-bridge
# 方式二:下 zip 解开后本地装
dsh plugin --profile desktop add "D:\path\to\phone-bridge-plugin-1.1.2.zip"
装完重启 DSH,然后在 DSH 里打开「设置 → 手机同步」。能看到二维码就是成了。
⚠️ 必须改监听地址,否则手机连不上
DSH 的 web 服务默认只监听 127.0.0.1 —— 它本来只服务本机浏览器,手机从 Tailscale 过来的请求会被直接拒掉。
自动修(推荐)
打开「设置 → 手机同步」。如果顶部出现红框提示「DSH 只监听本机」,点「一键修复」就好:
```dsh-ui 示例
🔴 DSH 只监听本机
当前 DSH 的 web 服务只接受本机连接,手机发来的请求根本到不了这里。
[ 一键修复 ] 自动改配置,改完需要重启 DSH
插件会自己找到当前 profile 的 `cordis.patch.yml`,改好 `host`,并在旁边留一份带时间戳的备份。然后**完全退出 DSH 再打开**。
#### 手动修
编辑:
Windows: %USERPROFILE%.dsh\profiles\desktop\cordis.patch.yml
macOS/Linux: ~/.dsh/profiles/desktop/cordis.patch.yml
加上(已存在 `webserver` 条目的话,只改 `host`):
```yaml
- id: webserver
name: '@deepseek-ai/dsh-host-webserver'
inject: [webStartup]
config:
host: '0.0.0.0'
port: !!js ctx.webStartup.port ?? 3080
compression: gzip
compressionLevel: 1
compressionThresholdBytes: 1024
> 这里的 name 在不同 DSH 版本里可能是 @deepseek-ai/dsh-host-webserver 或 @deepseek-ai/dsh-web-server。自动修会沿用文件里已有的写法。
验证
完全退出 DSH 再打开(关窗口不算),然后:
netstat -ano | findstr LISTENING | findstr 19387
| 看到 | 含义 |
|---|---|
0.0.0.0:19387 或 [::]:19387 |
✅ 手机能连进来 |
127.0.0.1:19387 |
❌ 只有本机能连,手机一定失败 |
v1.1.2 起插件会自己检查:设置面板顶部会出现红框直接告诉你是这个问题,不用再猜。
验证命令行也行:
curl http://127.0.0.1:19387/phone-bridge/health
# {"ok":true,"name":"phone-bridge","version":1}
装到手机
两端都装 Tailscale 并登录(电脑装 官网版,手机装 Play Store 版),登录同一个账号或互相 share 设备。
然后:
adb install -r phone-bridge-1.1.2.apk
没有数据线就传到手机点安装。打开 App 会看到配对页。
配对
{"title":"配对只做一次","gap":12,"items":[{"type":"steps","steps":[{"title":"电脑打开 DSH → 设置 → 手机同步","desc":"那里有一个二维码"},{"title":"手机 App 点「扫描电脑上的二维码」","desc":"扫描后地址和密钥自动填入"},{"title":"点「连接」","desc":"之后打开 App 直接就是聊天界面"}]},{"type":"callout","tone":"info","title":"二维码是自动生成的","content":"每次打开设置页都按当前状态实时算:本机 Tailscale 地址 + 随机 token。换电脑、换网络、token 变了,二维码自动跟着变,不需要你配置任何东西。"}]}
手填也行,配对页有两个输入框:
| 字段 | 从哪抄 |
|---|---|
| 配对地址 | 设置页上的 http://100.x.y.z:19387/phone-bridge |
| 配对密钥 | 设置页上的 token(一长串十六进制) |
换电脑或重新配对
App 内 → 设置 → 清除配对(重新扫码),会回到配对页,重新扫新电脑的码即可。
用起来
配对完成后,在 App 底部输入框打字(或点「拍照 / 相册」发图),点发送。回复会流式返回。点顶部「会话」可以切换目标会话,「设置」里可以调是否返回思考过程。
如果不想用 APK
手机浏览器直接打开配对地址也行,功能完全一样,只是发照片走标准的文件选择器。
安装(其余方式)
标准方式 —— dsh plugin
DSH 自带插件子命令,会把参数原样转发给 profile 目录里的 pnpm。这是所有第三方插件的官方安装路径,本插件同样适用:
# 桌面端(DeepSeek Harness 0.2.0-rc.2)
dsh plugin --profile desktop add github:chs-1108/dsh-phone-bridge
# 官方 Web 版
dsh plugin --profile web add github:chs-1108/dsh-phone-bridge
已经有本地克隆的话,直接指目录:
dsh plugin --profile desktop add /path/to/dsh-phone-bridge
然后把包名追加到 profile package.json 的 dsh.profile.bundles,重启 DSH:
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-phone-bridge"
]
}
}
}
确认加载成功:
curl http://127.0.0.1:19387/phone-bridge/health
# {"ok":true,"name":"phone-bridge","version":1}
一键部署
插件装好后,剩下的环境由 setup-phone-bridge.ps1 全包 —— Tailscale、监听地址、APK、以及通过 USB 推到手机。电脑上,管理员 PowerShell:
Set-ExecutionPolicy -Scope Process Bypass -Force
.\setup-phone-bridge.ps1
脚本走九步,只会在两处停下来问你:
- 登录你的 Tailscale 账号(自动开浏览器)。
- 确认重启 DSH 让插件生效。
如果想让它顺便把 Tailscale 和 phone-bridge 的 APK 也装进手机,请先用 USB 连上手机并提前打开 USB 调试。它还会预授相机权限。
结束后会给出这样的地址:
http://100.x.y.z:19387/phone-bridge/?token=<48 位十六进制>
手机浏览器打开它,或直接启动装好的 DSH 手机同步 App —— 已经配对好了。
> ?token= 必须是查询参数。#fragment 不会发到服务器,所以 #t=... 会鉴权失败。
脚本参数
.\setup-phone-bridge.ps1 -SkipPhone # 只部署电脑端
.\setup-phone-bridge.ps1 -SkipApk # 复用上次的 APK
.\setup-phone-bridge.ps1 -SkipTailscale # 已装好并登录过
.\setup-phone-bridge.ps1 -SkipRestart # 稍后自己重启 DSH
.\setup-phone-bridge.ps1 -Port 19387 # 指定端口
手动安装
你的 DSH 构建里没有 dsh plugin 的话,可以自己接线:
1. 把插件放到 DSH 能找到的位置
git clone https://github.com/chs-1108/dsh-phone-bridge.git
mkdir -p ~/.dsh/phone-bridge
cp -r dsh-phone-bridge/{index.js,client.js,package.json,cordis.patch.yml,web,vendor} ~/.dsh/phone-bridge/
2. 在 profile 里注册
把包链进 profile 的 node_modules,并在 cordis.patch.yml 加一行:
- id: phone-bridge
name: dsh-phone-bridge
3. 让 webServer 可被访问
服务默认只监听 127.0.0.1,手机连不上。在同一个 cordis.patch.yml 覆盖 host(profile patch 是最高优先级层):
- id: webserver
name: '@deepseek-ai/dsh-web-server'
config:
host: '0.0.0.0'
port: !!js ctx.webStartup.port ?? 3080
compression: gzip
compressionLevel: 1
compressionThresholdBytes: 1024
inject: [webStartup]
4. 重启 DSH,然后读 token
cat ~/.dsh/phone-bridge/state.json
手机端
装 Release 里的 phone-bridge-1.1.2.apk 就行,不用自己构建。
打开 App 是配对页,扫码或手填(见上面的「配对」)。配对信息存在手机本地,之后直接进聊天界面。
App 自带前台服务(持有 wake lock + 常驻通知),息屏和切后台都不会断连。Tailscale 在两端也是后台服务,关掉窗口不断线,重启自动连回来。
自己构建 APK
需要 JDK 17 和 Android SDK(build-tools 34 + platform 34):
$env:PHONE_BRIDGE_JAVA_HOME = "C:\path\to\jdk-17"
$env:PHONE_BRIDGE_SDK = "C:\path\to\Android\Sdk"
$env:PHONE_BRIDGE_BUILD_TOOLS = "34.0.0"
pwsh -File build-apk.ps1
# -> apk-out/phone-bridge.apk
不带参数构建出来的是通用包,每个用户扫码连自己的电脑。要发布就发这种。
预配对构建(仅自用)
调试时懒得每次扫码,可以把地址烘进去:
.\build-apk.ps1 -BaseUrl "http://100.x.y.z:19387/phone-bridge" -Token "<你的 token>"
打开就是主界面。但烘进去的包只能连这一台电脑,换机器必须重编 —— 不要发布这种包。
配对信息仍可通过 App 内「设置 → 清除配对」解除,解除后不会再被烘入值粘回去。
实现原理
| 组件 | 作用 |
|---|---|
index.js |
Host 半。在 webServer 上注册 prefix 路由,驱动 sessionController.prompt() 与 .follow(),生成 token。 |
client.js |
Client 半。把设置面板注入 DSH 网页端。 |
web/app.html |
手机加载的单页应用,无框架,约 16 KB。 |
apk/ |
原生 Android 外壳:SetupActivity、MainActivity(WebView + 文件选择 + 相机)、ScanActivity(zxing)、CaptureProvider。 |
setup-phone-bridge.ps1 |
两端九步部署。 |
build-apk.ps1 |
免 Gradle 的 APK 构建。 |
鉴权只有两条路:回环地址(电脑访问自己),或 48 位十六进制 token 的恒时比较。会话惰性解析——存的 sessionId 还在就复用,否则新建并持久化。
拍照链路刻意写进共享的 MediaStore 而不是私有 ContentProvider。所有相机 App 本来就能写那里,这样就绕开了在多个 OEM ROM 上静默失败的跨进程 URI 授权。
完整细节,包括九个拍照 bug 及每个修复的来由,见 IMPLEMENTATION.md。部署步骤与排错见 SETUP.md。
隐私
本插件不含任何服务器地址、token 或账号。这些值由 build-apk.ps1 在构建时通过 -BaseUrl / -Token 注入,仓库里的 BuildDefaults.java 和 network_security_config.xml 除回环地址外一律留空。
流量只在你自己的 tailnet 内,点对点加密,不经任何第三方中转。插件不会发起任何额外的出站连接。
把 token 当密码看待:拿到它的人就能跟你的 DSH 对话。要轮换,删掉 ~/.dsh/phone-bridge/state.json 里的 token 后重启。
排错
| 现象 | 原因 | 处理 |
|---|---|---|
{"ok":false,"error":"unauthorized"} |
token 没传到 | 用 ?token=,不要 #t= |
| 手机连不上 | 服务只监听回环 | 在 cordis.patch.yml 设 host: '0.0.0.0' 后重启 |
web boot: 1 entry did not activate |
client 注册 id ≠ 包名 | 必须是 dsh-phone-bridge |
| 拍照存下了但照片不出现 | 相机 App 访问不到 Provider | 改用 MainActivity 里的 MediaStore 路径 |
adb devices 显示 unauthorized |
调试授权没点 | 解锁手机,点允许 |
| APK 安装报签名冲突 | 签名密钥不同 | 先 adb uninstall com.dsh.phonebridge |
确认插件活着:
curl http://127.0.0.1:19387/phone-bridge/health
# {"ok":true,"name":"phone-bridge","version":1}
开发
node test/loader-import.mjs # client 半以包名注册
node test/composition.mjs # host 半可组合
node test/host.smoke.mjs # 路由与鉴权冒烟
重新打包:
.\build-zip.ps1
许可
MIT —— 见 LICENSE。