Skip to main content
W

dsh-phone-remote

wlxyxykj/dsh-phone-remote

Install

dsh plugin --profile web add github:wlxyxykj/dsh-phone-remote

README

dsh-phone-remote · 手机遥控

用手机控制 DSH 桌面版里跑着的 Agent:手机上说一句话,电脑上的 Agent 就干活, 输出实时推到手机,随时能叫停。

Phone control for DeepSeek Harness (DSH Desktop): serve a token-protected mobile page from this computer so you can drive the agent from your phone.


目录


它能干什么

DSH 桌面版自带的界面只监听 127.0.0.1,手机打不开。这个插件在电脑上再起一个只服务手机的小网页:

在手机上你能做说明
看会话列表电脑上所有会话(含正在运行的那个)都在列表里
选一个会话继续聊和电脑上是同一个会话、同一份上下文
新建会话可以指定工作目录,Agent 就在那个目录里干活
发指令Agent 真的会在电脑上执行:跑命令、改文件、查资料
传文件给 Agent用 📎 把手机里的文件(照片、PDF、表格…)传到电脑,Agent 直接读
把产出下回手机Agent 说一句「发你手机」,手机页就出现下载卡片,点一下存到手机
实时看输出一个字一个字推过来;工具调用显示成可展开的小卡片
随时叫停运行中按钮变成 ■,点一下停止当前回合

电脑上还有一个控制台(只有本机能打开),用来出示二维码、查看/重置口令、管理访问白名单。


前提条件

项目要求
电脑装有 DSH 桌面版(本插件在 DSH 0.2.0-rc.2 上实测通过)
手机任意浏览器即可(Safari / Chrome / 国产浏览器都行;微信内置浏览器可能限制流式推送,建议用系统浏览器)
网络手机要能直接访问到电脑的某个 IP:同一个 Wi-Fi 最省事;不同网络见 手机连不上?
端口默认用 8790;被占用时会自动往后找(8791、8792…),以控制台/日志显示的为准

安装(DSH 桌面版)

方式一:图形界面(推荐)

  1. 打开 DSH 桌面版 → 设置 → 插件

  2. 点 添加插件

  3. 在输入框里粘贴这一行(GitHub 地址,公开仓库,不需要任何账号):

    github:wlxyxykj/dsh-phone-remote
    

    也可以填本地路径:插件目录,或打好的 .tgz 文件路径。

  4. 点 安装,等它把依赖装完(几秒)

  5. 重启一次 DSH 桌面版 ← 必须,见下方说明

  6. 重启后回到 设置 → 插件,应能看到 dsh-phone-remote 处于已启用状态

为什么必须重启? DSH 宿主进程按「模块 URL」缓存插件代码,进程活着的时候不会重新读取磁盘上的插件模块。 所以安装或升级插件代码之后必须重启一次;改配置项则可以热生效,不用重启。

方式二:命令行

DSH 自带命令行,桌面版用户也可以用:

dsh plugin --profile desktop add github:wlxyxykj/dsh-phone-remote

装完同样重启一次 DSH 桌面版;重启后到「设置 → 插件」确认它是启用状态(有些情况下需要在那里手动打开开关)。

怎么确认装好了

重启后,设置 → 插件里能看到 dsh-phone-remote;或者直接打开控制台:

http://127.0.0.1:8790/

能打开就说明跑起来了。启动日志里也会打印一段横幅:

[dsh-phone-remote]
  ┌─ DSH 手机遥控已就绪 ─────────────────────────────────
  │ 监听      0.0.0.0:8790
  │ 电脑面板  http://127.0.0.1:8790/
  │ 访问控制  开放(任何能连到本机的设备都可以,凭口令访问)
  │ 手机打开:
  │   http://192.168.x.x:8790/m?t=xxxxxxxx   WLAN
  └──────────────────────────────────────────────────────

使用:三步上手

第 1 步:在电脑上打开控制台

浏览器打开 http://127.0.0.1:8790/

这个页面只在本机能开(手机打不开,故意的),里面是:手机链接 + 二维码 + 口令 + 访问控制 + 会话概览。

第 2 步:手机扫码进来

手机连和电脑同一个 Wi-Fi,然后用相机扫面板上的二维码:

  • 二维码里已经带了口令,扫完直接进入,不用手输;
  • 手机浏览器会记住口令(存成 Cookie,重启电脑也还在);
  • 也可以在手机浏览器里手动输入面板上那条链接(形如 http://192.168.1.23:8790/m?t=xxxxxxxx)。

第 3 步:指挥 Agent

想做的事在手机上怎么操作
切换会话左上角 ☰ → 点会话
新建会话☰ → 底部「+ 新建会话并接管」,可填工作目录
发指令底部输入框输入,点 ↑ 发送
看输出自动流式显示;工具调用是一条可展开的卡片,点开看完整输出
叫停运行中发送按钮变成 ■,点一下停止当前回合

不需要在手机上确认任何授权弹窗——远程会话默认已经设成 approvalPolicy: never,发指令就直接执行。

第 4 步(可选):传文件 / 收文件

手机 → 电脑:给 Agent 喂文件

  1. 手机页面底部点 📎,选文件(可多选,单个最大 25 MB);
  2. 上传完成后输入框上方出现文件小标签(⏳ 上传中 → 📎 已就绪,点 ✕ 可移除);
  3. 正常发指令即可——文件会写进这个会话的工作目录下的 .phone-uploads/, 指令里自动附上路径,Agent 用它自带的文件工具直接读。

落地路径形如 .phone-uploads/20260930-220145-合同.pdf。 你只要说「读一下我刚传的合同,把关键条款列出来」就行。

电脑 → 手机:把产出拿回来

Agent 需要把文件给你时,它会调用工具 phone_send_file;你也可以直接说:

把这个报告发我手机上

手机页面上会出现一张下载卡片(文件名 + 大小 + 备注),点一下就存到手机。 卡片刷新后仍在(有效期 2 小时,单文件最大 25 MB)。

如果点下载没反应,长按卡片选「在新标签页打开」——部分手机浏览器对内联下载有限制。


界面导览

电脑控制台 http://127.0.0.1:8790/

区域内容
手机访问链接所有可用地址(Wi-Fi / 有线 / Tailscale 等),带二维码和「复制」按钮;点链接即复制含口令的完整地址
状态监听地址、访问口令(显示/复制)、会话数、运行中的会话数、新会话默认目录
访问控制白名单开关、白名单列表、最近访问过的客户端(被拦的会标「已拦截」,旁边就是「允许」按钮)
会话最近有活动的会话一览
安全须知口令等于操作权等提醒

控制台每 5 秒自动刷新,换了网络后新地址会自动出现在列表里。

手机页 /m

区域内容
顶栏当前会话名、连接/运行状态、☰ 会话列表、⟳ 手动重连
消息流你说的(右侧气泡)、Agent 回答(Markdown 渲染)、工具调用卡片、折叠的「💭 思考过程」、文件下载卡片、错误提示
底栏📎 上传文件、输入框、发送/停止按钮;上传中的文件显示为小标签
口令页如果这个浏览器没有口令,会先显示「需要访问口令」的输入框

配置

两种改法,任选一种(改配置不用重启,保存即生效):

A. 在 DSH 里改:设置 → 插件 → dsh-phone-remote → 配置

B. 改 profile 的 cordis.patch.yml($DSH_HOME/profiles/<profile>/cordis.patch.yml),加一行:

- id: phone-remote
  name: 'dsh-phone-remote'
  config:
    port: 8790
    cwd: 'D:\work\my-project'
    allowCancel: false

配置项

键默认说明
enabledtruefalse 时插件完全不启动(不监听、不注册工具)
host0.0.0.0监听地址。0.0.0.0 = 所有网卡都能访问;改成 127.0.0.1 就只有本机能用
port8790起始端口;被占用时自动向后找,最多试到 8799
token空访问口令。留空 = 复用已保存的,没有就随机生成 24 位并写入 $DSH_HOME/dsh-phone-remote.json
cwd空手机上新建会话时的默认工作目录
agentPreset空手机上新建会话使用的 Agent preset(如 standard)
approvalPolicynever远程会话的授权策略。never = 不再逐条询问(推荐);ask = 跟随桌面端弹窗
allowCreatetrue是否允许手机新建会话
allowCanceltrue是否允许手机叫停回合
exposePaneltrue是否提供本机控制台 /
allowFrom[]IP 白名单(写死在配置里)。非空 = 只有名单里的客户端能访问,且面板不能改;留空则在面板上管理

访问控制与安全

口令是怎么工作的

  • 口令是 24 位随机字符(约 120 bit),暴力猜解不可行;
  • 二维码/链接里带着它(?t=...),所以扫码等于自动带钥匙;
  • 服务端校验通过后会种一个 HttpOnly + SameSite=Strict 的 Cookie,之后这个浏览器就不用再带;
  • 校验用 SHA-256 常数时间比较;每个 IP 每分钟失败超过 20 次会被限流(429)。

白名单(可选,建议在有公网 IP 时打开)

默认是开放的:任何能连到这台电脑的设备都能打开登录页(仍需要口令)。

如果电脑有公网 IP(校园网直连、宽带拨号很常见),建议打开「仅白名单」:

  1. 控制台 → 访问控制 → 「启用『仅白名单』」;
  2. 让手机先访问一次(被拦没关系),它的 IP 会出现在最近访问的客户端里,标着「已拦截」;
  3. 点那一行的 允许 —— 立即放行,不用重启。

白名单每行支持三种写法:203.0.113.42(单个 IP)、203.0.113.(整个前缀)、10.0.0.0/8(CIDR 网段)。

代价:手机用移动数据时 IP 会变(开关飞行模式就会变)。变了之后手机会显示「此设备未授权」, 你需要回电脑面板点一下「允许」。如果你经常不在电脑边,就不要开白名单——纯口令模式换任何网络都能连。

两条必须知道的安全提醒

  1. 链接/二维码就是钥匙,别截图外发、别丢群里;
  2. 目前是明文 HTTP,链路上的中间人理论上能嗅探到口令。要彻底解决就用 Tailscale 之类的加密隧道,或者用完就把配置里的 enabled 设为 false。

用 Tailscale:固定地址 + 加密(推荐)

手机用移动数据时 IP 会变、白名单难维护、明文传输有风险——Tailscale 一次解决这三件事: 它把你的设备组成一个私有网络,每台设备拿到固定的 100.x.y.z 地址,设备之间端到端加密。

  1. 电脑和手机都装 Tailscale,登录同一个账号;
  2. 电脑上执行 tailscale ip -4 拿到形如 100.101.102.103 的地址(控制台链接列表里也会多出一条,排在最前面);
  3. 手机浏览器打开 http://100.101.102.103:8790/m?t=<口令>,之后无论在 Wi-Fi 还是流量下都能用。

想收得更紧(二选一):

方案做法效果
A. 只换地址保持 host: '0.0.0.0',白名单写 100.64.0.0/10公网仍能连到端口,但一律 403,只有你的 tailnet 能进
B. 只监听 Tailscale 网卡配置 host: '100.101.102.103'公网连 TCP 都连不上,最彻底

方案 B 的注意点:Tailscale 必须在 DSH 启动前已经连上,否则那个地址不存在。 插件遇到这种情况会自动退回监听 0.0.0.0 并打一条告警,不会整个起不来(此时请确保白名单是开的)。

国内提示:login.tailscale.com 偶尔不好访问,连不上可以换 ZeroTier,原理一样(插件同样会把它的网卡排在前面)。


手机连不上?逐个排查

按顺序试,基本三步内能定位:

1. 手机和电脑网络通不通

  • 同一个 Wi-Fi 是最省事的(注意有些公共/校园 Wi-Fi 开了「客户端隔离」,同网段也互相不通);
  • 电脑有公网 IP 时,手机用移动数据也可能直接连上(取决于网络是否放行);
  • 都通不了就用手机开热点,让电脑连手机热点——控制台几秒内会自动列出新地址,扫新的码即可;
  • 想随时随地用,见上面的 Tailscale。

2. 电脑上的控制台打得开吗

  • 打不开 → 插件没跑起来:确认 设置 → 插件 里是启用状态,并重启一次 DSH 桌面版;
  • 端口不是 8790 → 被占用了,看控制台标题/启动日志里的实际端口。

3. 手机显示什么

手机上的现象原因怎么办
「此设备未授权访问」白名单开着,手机 IP 不在名单里回电脑控制台 → 访问控制 → 最近访问 → 点「允许」;或关掉白名单
「需要访问口令」输入框链接里没带口令,或这个浏览器没存过粘贴面板上的口令,或重新扫码
黑屏 / 「页面没能启动」卡片页面脚本没加载成功下拉刷新;不行就重新扫码
页面能开但没反应局域网能开页面、流式被中间设备掐断换系统浏览器(别用微信内置)
一直转圈网络到了但服务没回确认电脑没休眠、插件还在运行

4. 还是不行?放行防火墙

Windows 防火墙可能拦了入站端口。管理员 PowerShell 执行一次(端口按实际的改):

New-NetFirewallRule -DisplayName "DSH Phone Remote" -Direction Inbound -Action Allow `
  -Protocol TCP -LocalPort 8790 -Profile Private

Wi-Fi 被 Windows 标成「公用网络」时,-Profile Private 不生效;要么把该网络改成专用网络,要么用 -Profile Any(暴露面更大,慎用)。

5. 忘记口令了怎么办

打开电脑控制台 → 状态 → 显示 就能看到口令;点 重置口令 会作废旧链接和所有已登录的手机,然后重新扫码即可。


升级 / 卸载

DSH 目前不支持插件自动更新,升级要手动来:

升级:设置 → 插件 → 卸载 dsh-phone-remote → 重新「添加插件」新版本 → 重启 DSH
卸载:设置 → 插件 → 卸载 dsh-phone-remote → 重启 DSH

命令行方式:

dsh plugin --profile desktop remove dsh-phone-remote   # 卸载
dsh plugin --profile desktop add github:wlxyxykj/dsh-phone-remote#v1.1.0   # 装指定版本

用 GitHub 安装时可以用 tag 锁定版本,例如 github:wlxyxykj/dsh-phone-remote#v1.1.0。


工作原理

┌──────────────┐   扫码/链接    ┌────────────────────┐   局域网/公网   ┌──────────────────────┐
│  手机浏览器   │ ─────────────▶ │  电脑控制台 (仅本机) │ ◀───────────── │  DSH 桌面版宿主进程    │
│  /m?t=<口令>  │ ◀───────────── │  127.0.0.1:8790    │                │  ctx.sessionController│
│  发指令/看输出 │   SSE 流式推送  │  二维码/白名单       │                │  (真正在跑 Agent)    │
└──────────────┘                └────────────────────┘                └──────────────────────┘
  • 插件作为 DSH 的一个 bundle 被加载,在宿主进程里监听一个端口;
  • 手机面 /m 与 /api/m/* 需要口令;控制台 /、/api/panel/*、/api/qr 只接受 127.0.0.1;
  • 发指令走 ctx.sessionController(list / create / resolveAgent / prompt / cancel / follow), 输出通过 follow 的 SSE 流推送;远程会话在第一条指令前就被设成 approvalPolicy: never;
  • 口令、白名单存在 $DSH_HOME/dsh-phone-remote.json(旧版本名 mobile-remote.json 会被自动沿用)。

文件结构

文件作用
lib/index.js插件入口:配置、口令、启动监听、注册 phone_remote 工具、卸载清理
lib/server.jsHTTP 路由、口令校验、IP 白名单、SSE、端口顺延、优雅停止
lib/session.js到 DSH 宿主服务的桥接
lib/app.html手机页(原生 JS,无前端依赖)
lib/panel.html电脑控制台
lib/markdown.js零依赖 Markdown 渲染器(转 DOM 只用 textContent,模型输出的 HTML 不会被当标签执行)
lib/qr.js零依赖 QR 编码器(字节模式,版本 1–10,EC L/M/Q/H)
lib/net.js / lib/store.js局域网地址探测 / 状态文件读写
locale/*.json插件在 DSH 里的显示名与描述(中文/英文)

另外注册了两个给 Agent 用的工具:

工具作用
phone_remote你说「给我手机遥控的链接」,它返回链接和控制台地址
phone_send_file把电脑上的文件推到手机(你在手机页看到下载卡片),说「这个文件发我手机」即可

文件传输的两个方向用的是不同机制:上传是把文件写进会话工作目录后,把路径写进指令(Agent 用自己的文件工具读,不依赖模型的附件能力); 下发是把文件登记到插件的下载表,再通过手机正在监听的 SSE 流推一张卡片过去,下载走 /api/m/download/<id>(同样要口令)。


开发与测试

node --test "test/*.test.mjs"

93 项测试,覆盖:HTTP 路由 / 口令栅栏 / 限流、IP 白名单(含真实非回环地址的拦截测试)、 SSE 流、会话桥接、文件上传与下载(含越权、超限、过期文件、SSE 推送)、插件装配、Markdown 渲染、QR 编码。

其中有 4 项是与两套独立 QR 实现(node-qrcode、qrcode-generator)的逐模块交叉比对, 需要 scratch/ 下的参考实现;克隆下来没有它时会自动跳过,其余 89 项照常通过。


License

MIT © tofar

Related plugins