# dsh-mobile-gateway 使用说明
> 配套手机 App(DeepSeek Harness Mobile)完整使用指南。
> 服务器端只需要官方版 dsh + 本插件,**不需要下载源码、不需要改代码、不需要编译**。
[English](USAGE.en.md) | 中文
---
## 目录
1. [整体架构](#整体架构)
2. [准备条件](#准备条件)
3. [服务器端:三步安装](#服务器端三步安装)
4. [管理页:token、二维码与设备管理](#管理页token二维码与设备管理)
5. [手机端:安装与连接](#手机端安装与连接)
6. [App 功能一览](#app-功能一览)
7. [配置参考](#配置参考)
8. [安全模型](#安全模型)
9. [故障排查](#故障排查)
10. [常见问题](#常见问题)
---
## 整体架构
```
┌────────────────────────── 你的电脑(同一 Wi-Fi / 局域网)──────────────────────────┐
│ │
│ 官方 dsh web(npm 安装,无任何改动) │
│ ├─ /api 官方接口 ── 仅本机(127.0.0.1)可访问,本机浏览器照常使用 │
│ └─ /m/api 本插件挂载 ── 局域网可访问,必须携带 Bearer token │
│ ├─ 全部会话/工作区/模型 RPC(白名单制) │
│ ├─ /m/api/respond 审批、提问应答 │
│ └─ /m/api/events.mux|.host 两条 WebSocket 实时事件流 │
│ ▲ │
└──────────────────────────────┼─────────────────────────────────────────────────────┘
│ Wi-Fi(局域网明文 HTTP + token 认证)
┌──────┴──────┐
│ 手机 App │ 地址: http://<电脑IP>:<端口>
│ (Flutter) │ token: 与插件配置一致
└─────────────┘
```
**一句话**:插件把 dsh web 的网卡打开并对官方 `/api` 关门,只留一条带 token 验票的 `/m/api` 通道给手机 App。
## 准备条件
**电脑(服务器端)**
- 已安装官方 [DeepSeek Harness](https://npmjs.com/package/@deepseek-ai/dsh)(`npm install -g @deepseek-ai/dsh`,当前验证版本 `0.1.2-rc.1`)
- Node.js `^22.19` 或 `>=24`
- 已配置好可用的模型(App 内也可查看/切换,但 dsh 本身需有一个可用 provider)
**手机**
- Android 8.0+(APK 直接安装);或自行用源码构建(支持 Android + Windows)
- 与电脑在**同一局域网**(连同一个 Wi-Fi / 路由器)
## 服务器端:三步安装
### 第 1 步:安装插件
```powershell
dsh plugin --profile web add github:elskly-cmyk/dsh-mobile-gateway
# 首次安装 pnpm 会询问是否允许构建本包(allowBuilds),按 dsh 的提示放行即可
dsh plugin --profile web add link:<本插件仓库路径> # 开发调试用本地路径
```
### 第 2 步:配置 token
> **可以跳过这一步**——装好插件启动后,打开本机浏览器
> `http://127.0.0.1:3080/m/`,点「生成新 token」一键完成(见
> [管理页](#管理页token二维码与设备管理))。以下为手动方式。
生成一个长随机串(任选其一):
```powershell
# PowerShell
-join ((1..64) | ForEach-Object { '{0:x}' -f (Get-Random -Max 16) })
# Node
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
```
写入配置。**两种途径任选**:
途径 A:编辑配置文件(推荐,一次配好)
编辑 `~\.dsh\profiles\web\cordis.patch.yml`(Windows)或 `~/.dsh/profiles/web/cordis.patch.yml`:
```yaml
- id: mobile-gateway
config:
token: 把生成的随机串粘到这里
```
改这个文件需要重启 `dsh web` 生效。
途径 B:Web 设置卡(热生效,不用重启)
本机浏览器打开 `http://127.0.0.1:3080` → 设置 → 插件配置 → mobile-gateway → 填入 token。
保存即生效,无需重启。
> **token 未配置或为空时,插件会拒绝启动并明确报错**——这是故意的(fail-closed),不会静默暴露。
### 第 3 步:启动
```powershell
dsh web
```
不需要任何命令行参数。插件会自动:
- 绑定所有网卡(手机可达)
- 启动行打印局域网地址,例如:
```
dsh web: http://127.0.0.1:3080 (LAN: http://192.168.1.5:3080)
```
- 把官方 `/api` 收窄为仅本机访问(本机浏览器不受影响,局域网直连 `/api` 一律 403)
**首次启动防火墙**:Windows 会弹"允许 Node.js 入站连接",勾选**专用网络**(如果你的
Wi-Fi 被归为公用网络,公用也要勾)。漏点了用这条命令补:
```powershell
New-NetFirewallRule -DisplayName "dsh web" -Direction Inbound `
-Program "C:\Program Files\nodejs\node.exe" -Action Allow
```
Linux 则类似 `sudo ufw allow 3080`。
---
## 管理页:token、二维码与设备管理
插件自带一个**仅限本机访问**的管理页。两种进入方式:
- **侧边栏入口**(v0.3.0+):官方 Web GUI 侧边栏底部、设置按钮旁的**手机图标**,点击后管理页以**当前页弹窗**展开(Esc/关闭按钮/点遮罩收起;弹窗右上角「新标签页」可切换为完整页面)
- **直接输 URL**:电脑浏览器打开 `http://127.0.0.1:3080/m/`
(端口以你的启动输出为准;局域网地址打开会得到 403——token 是配对密钥,
不在本机以外的任何屏幕上显示。入口图标只在本机打开的页面上出现。)
不想显示入口图标时:设置 → 插件配置 → mobile-gateway → 关闭「显示侧边栏入口」
(即 `uiEntry: false`,热生效;`/m/` URL 本身永远可用)。
页面提供四块能力:
| 区块 | 说明 |
| --- | --- |
| **访问令牌** | 查看当前 token(默认打码,点击显示);「生成新 token」一键轮换——**热生效**:旧 token 的下一次请求立即 401,无需重启,且重启后依然保留(写入 `~/.dsh/settings.yaml`) |
| **扫码配对** | 每个局域网 IPv4 地址一张二维码(多网卡机器全部列出)。手机 App 连接页点「扫码配对」对准任意一张,地址与令牌自动填入 |
| **已连接设备** | 每台连接过的设备一行:名称(如 `Android 14`)、在线状态(含实时流计数)、最近活跃、请求数;可单台「屏蔽 / 解除」 |
| **状态横幅** | 未配置 token 时红条警示并引导生成;正常运行时显示端口与白名单开关状态 |
**设备屏蔽语义**:屏蔽在下一次请求生效、重启后清除(临时踢出);要永久
踢出所有设备,用「生成新 token」轮换。
**token 的三处来源,优先级从高到低**:管理页/Web 设置卡轮换结果(user 层,
`~/.dsh/settings.yaml`)→ profile patch 里的 `token:`(base 层)→ 空(未配对,
`/m/api` 拒绝一切连接,管理页红条提示)。未配置 token 插件**不会启动失败**——
管理页照常可用,配对从这里开始。
---
## 手机端:安装与连接
### 安装 App
- **直接安装**:从 GitHub Releases 下载最新 `dsh_mobile_app-v*.apk`(**需 v1.0.36+**:
此版本起默认走插件的 `/m/api` 通道并支持扫码配对;旧版 APK 走 `/api`,无法配合本插件)
- **源码构建**:克隆 [App 仓库](https://github.com/your-name/dsh-mobile-app),然后:
```powershell
flutter build apk --release # 通用
# 或使用仓库自带脚本(本机工具链版)
powershell -ExecutionPolicy Bypass -File build_apk.ps1
```
### 连接
**方式一:扫码(推荐)**
1. 手机连上与电脑相同的 Wi-Fi
2. 电脑浏览器打开 `http://127.0.0.1:3080/m/`,页面已生成二维码
3. 手机 App 连接页点「扫码配对」(首次会请求相机权限),对准二维码
4. 地址与令牌自动填入,点「连接」即可
**方式二:手动输入**
1. 打开 App,连接页填写:
- **服务器地址**:启动行里 `LAN:` 后面的地址,如 `http://192.168.1.5:3080`
- **访问令牌**:管理页或配置文件里的 token
2. 点连接。App 会调用 `host.describe` 验证地址和 token,成功后进入主界面
> **地址中不能有空格**——空格会被 URL 编码成 `%20`,触发 IPv6 zone-id 误判,报
> "not a valid link-local address"。IP 和端口之间只用冒号。
连接成功后,地址和 token 会安全存储在手机上,下次打开 App 自动回填。
---
## App 功能一览
| 功能 | 说明 |
| --- | --- |
| **会话抽屉** | 按工作区分组(组级折叠、有运行任务的组自动展开);分组头「新建会话」自动归入该工作区 |
| **聊天** | 流式实时输出;深度思考折叠展示;消息按 Markdown 渲染;支持发送图片附件 |
| **工具调用** | 工具树 + 底部抽屉查看每次调用的参数与结果 |
| **审批卡** | 危险操作弹出批准/拒绝卡,手机上一键应答(走 `/m/api/respond`) |
| **提问面板** | Agent 向你提问时底部弹层作答 |
| **模型切换** | provider 分组目录 + 每模型思考强度(effort),与桌面端同一会话设置实时同步 |
| **权限预设** | 会话级权限切换(只读 / 工作区可写 / 完全权限) |
| **Goal / Plan / Todo** | 目标、计划、任务清单实时投影面板 |
| **队列与打断** | 消息排队(queue/steer)、取消正在运行的任务 |
| **后台任务** | 每会话后台 job 列表与状态 |
| **子代理** | 子会话列表、追问、打断 |
| **搜索** | 全部会话全文搜索(`session.search`) |
| **会话导出** | 下载会话日志(`session.export`) |
| **设置页** | 通用/模型/Agent 预设/插件 只读详情(改配置请回桌面端或用本插件放行的写接口) |
| **外观** | 深色 / 浅色 / 跟随系统,选择持久化 |
| **断线重连** | 双 WebSocket 流掉线自动指数退避重连,状态条实时显示 |
## 配置参考
全部配置项(默认值就绪,只必须填 `token`):
```yaml
- id: mobile-gateway
config:
token: <必填,长随机串>
allowSettings: true # 放行 settings.* 读写(App 设置页要用)
allowCredentials: true # 放行 credentials.* 密钥管理
uiEntry: true # 桌面 Web GUI 侧边栏显示手机入口图标
extraMethods: [] # 追加放行方法,如 ['subagent.history']
maxRequestBodyBytes: 750000000
```
**默认白名单**(token 验证后可用):
| 域 | 方法 |
| --- | --- |
| 会话 | `session.list/create/history/prompt/rename/updateQueue/fork/cancel/attachment/models/selectModel/search/export` |
| 工作区 | `workspace.list/create/rename/delete/archiveSession` |
| 目录 | `host.describe/listDirectory/createDirectory` |
| 子代理 | `subagent.list/prompt/interrupt` |
| 目标 | `goal.create/edit/complete/clear/pause/resume` |
| 技能 | `skill.list` |
| 命令 | `commands/list`、`commands/execute`(会话级斜杠命令) |
| 模型目录 | `llm.models/providers` |
| 设置(可关) | `settings.describe/update/replace/mutate` |
| 凭据(可关) | `credentials.describe/set/unset` |
**始终拒绝**:`host.pickDirectory/openPath`(驱动电脑原生对话框)、`llm.discoverModels`、
`agentPreset.read/copy/...`(预设管理)。需要扩展时用 `extraMethods` 显式追加。
## 安全模型
- **token 是唯一的钥匙**:恒定时间比对,无法通过时间侧信道探测。持 token 者 = 拥有白名单内
完整 agent 能力(包括在你电脑上执行命令的会话)。请用足够长的随机串(≥32 字节)。
- **官方 `/api` 被本插件收窄为仅本机**:绑 `0.0.0.0` 后 SDK 默认会自动信任局域网 IP——
插件显式关闭了这个行为(`trustedHosts: []`),局域网内任何设备直连 `/api` 一律 403。
本机浏览器不受影响。
- **`--trusted-host` 在本插件下无效**:插件覆盖了对应的配置表达式。**永远不要**为了任何
目的去改回它——那会把官方 `/api` 整个裸露给局域网并绕过 token。
- **边界是局域网**:本插件不含公网隧道。需要外网访问请自行架设带认证的通道
(Cloudflare named tunnel、Tailscale 等),并确认它透传 WebSocket。
- **撤销访问**:改 token(Web 设置卡热生效,所有手机立即失效)或
`dsh plugin --profile web remove dsh-mobile-gateway` 后重启。
- **明文 HTTP**:局域网内传输不加密。家用 Wi-Fi 风险可控;公司/公共网络请慎用或加隧道。
## 故障排查
| 现象 | 原因与处理 |
| --- | --- |
| App 连接报 401 | token 不一致。管理页(/m/)或设置卡轮换后要重新扫码/更新 App 里的令牌 |
| App 连接报 403 | 方法不在白名单(连接探测 `host.describe` 被关)、**设备被屏蔽**(去 /m/ 解除)或官方 `/api` 被正确关闭——确认 App 是 v1.0.36+(走 `/m/api`),插件是最新版 |
| 管理页 /m/ 打开是 403 | 用了局域网地址打开。必须用 `http://127.0.0.1:<端口>/m/` 在**电脑本机**浏览器打开 |
| 扫码后提示"二维码内容不是配对信息" | 扫的不是管理页的二维码,或 token 含 `\|` 字符(管理页会警示,轮换即可) |
| 连接超时 / 无响应 | ① 不在同一 Wi-Fi;② 防火墙拦了 Node 入站(见上文 netsh 命令);③ 地址填了 `127.0.0.1`——手机上必须填电脑的局域网 IP |
| "无效的服务器地址" | 缺 `http://` 前缀或 host 为空;地址里不能有空格 |
| 设置页打不开 / 报错 | `allowSettings` 或 `allowCredentials` 被关了;或 App 版本低于 v1.0.36(旧版走 `/api`,特权方法仅本机) |
| 启动报 "apiProxy service lacks the required seams" | dsh 版本与插件不兼容(见下条) |
| 升级 dsh 后插件异常 | 插件启动自检会拒绝不兼容版本。把 dsh 固定在验证过的版本,或等插件更新 |
| 发图失败 413 | 请求体超上限;检查 `maxRequestBodyBytes` 与 attachment 限额是否被手动改小 |
| 手机收到消息延迟 | WebSocket 流断开重连中;状态条会显示,稳定 Wi-Fi 下自动恢复 |
## 常见问题
**多台手机能同时连吗?**
能。token 是共享的——所有持有同一 token 的设备能力相同。临时踢某一台用管理页
的「屏蔽」;踢全部就轮换 token。
**轮换 token 后要重启吗?**
不用。轮换热生效:旧 token 的下一个请求立即 401,新 token 立即可用;已打开的
WebSocket 流在下次重连时被拒。结果持久化在 `~/.dsh/settings.yaml`,重启不丢。
**换了 Wi-Fi / 电脑 IP 变了怎么办?**
管理页的二维码按当前所有 IPv4 地址生成,刷新 `/m/` 页重新扫即可;App 里也可以
手动改服务器地址(token 不变)。
**电脑上的浏览器还能正常用吗?**
能。本机 `http://127.0.0.1:3080` 的官方 Web GUI 完全不受影响。但**局域网内其他电脑的
浏览器**打开不了 Web GUI(`/api` 已仅本机)——这是刻意的安全姿态;其他电脑请也用手机 App
(或 Android/Windows 版 App)。
**和自编译 fork 的方案(`DSH_HOST_AUTH_TOKEN` + `--host 0.0.0.0`)什么关系?**
本插件是它的"零编译替代":官方 npm dsh + 一条安装命令即可,不再需要维护 fork。
Dart SDK 的 `DshApiClient` 保留了 `apiPrefix` 参数(默认 `/m/api`),传 `/api`
即可连回旧 fork 后端。
**token 在哪里改最方便?**
Web 设置卡(热生效):`http://127.0.0.1:3080` → 设置 → 插件配置 → mobile-gateway。
**已验证环境**
官方 `@deepseek-ai/dsh@0.1.1-rc.2`(npm latest)· Node 24 · Windows /
Android 真机 · 全链路实测(认证、会话、流式、审批、WS 事件流、日志导出)。