# dsh-mobile

> Bring DeepSeek Harness to mobile browsers and Android apps, securely access your computer's sessions and workspaces via home or office LAN, with support for on-demand mobile interface customization and extending computer capabilities.

## Metadata

- Author: [@saya-ch](https://github.com/saya-ch)
- Repo: <https://github.com/saya-ch/dsh-mobile.git>
- GitHub: [saya-ch/dsh-mobile](https://github.com/saya-ch/dsh-mobile)
- Stars: 61
- Language: TypeScript
- License: [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html)
- Topics: `android`, `deepseek-harness`, `dsh-plugin`, `lan`, `mobile`, `webview`
- Forks: 4
- Open Issues: 0
- Last push: 2026-08-19T09:04:15.000Z
- Added: 2026-08-20T00:00:00.000Z

## Install

```bash
dsh plugin --profile web add github:saya-ch/dsh-mobile
```

## Wiki

## 一句话定位
把电脑上的 DeepSeek Harness 带到手机浏览器或 Android App，通过受保护的局域网继续用同一份会话、工作区、消息和工具；不修改 DSH 源码，不暴露公网通道。

## 核心能力
- 在手机上继续电脑端工作：同一份会话、工作区、消息和工具实时同步，电脑只跑 DSH 这一个前端，手机只是另一个客户端
- 三种设备配对方式：扫码、配对链接、一次性密钥，配对成功后会持久化设备信任，后续打开 App 不再要求重新输入
- 自动局域网发现与适配：监听 DNS-SD/mDNS 与周期性 UDP 公告（默认 3 秒间隔），切换 Wi-Fi、热点或 IP 后通常自动恢复
- 独立 HTTPS 与证书固定：自签名 CA 只在下发 Android App 时绑定指纹；手机浏览器首次需手动信任该证书
- 会话级别回环代理与设备/会话授权：设备 token 只保存 SHA-256 摘要，配对/会话/活跃请求上限可调，配对设备和电脑端 DSH 完全等权
- 对话驱动的手机端定制：在 DSH 会话里 `/mobile <需求>` 由 agent 直接改 `$DSH_HOME/mobile-access/` 下的 `mobile.css`、`mobile.js` 或扩展目录，几秒后手机端自动生效
- 移动扩展（Extensions）机制：在本机额外目录里写 `host.mjs`（电脑端 Node.js 权限）+ `mobile.js/css`（手机端脚本），手机端通过 `api.host.invoke()` / `api.host.fetch()` 调用电脑能力（读写文件、执行命令等）

## 技术实现
- **语言**: TypeScript（编出 ESM `.mjs`，并出一份 `mobile-layout.js` 的 Client 端浏览器 bundle）；配套 Android App 是 Kotlin WebView 薄壳
- **关键依赖**: `@deepseek-ai/cordis`（Cordis 宿主容器）、`@deepseek-ai/schemastery`（配置与扩展清单 Schema）、`bonjour-service`（mDNS / DNS-SD 服务发布）、`qrcode`（生成配对二维码 SVG）、`selfsigned`（自签发 CA 与服务端证书）
- **架构模式**: 三层分面 — Host face（`src/plugin.ts` 注册 Cordis 插件，挂载 WebServer 的 `/mobile-access/...` 回环管理路由以及 `/api/mobile-access/...` 已配对外 HTTPS 网关；依赖 `webServer` + `commands`） + Client face（`src/client.ts` + `src/mobile-layout.ts`，DSH `web` profile 启动时按 platform=`web` 立即注入，替换 desktop layout module 指向移动版） + Android App（同一 HTTPS URL，App 内 WebView 接 `dshMobile` Bridge 暴露文件选择、相机扫描、分享、剪贴板、通知等受控原生能力）
- **入口文件**: `src/plugin.ts:39` 的 `name='dsh-mobile'`、`inject=['webServer','commands']`、`apply(ctx, config)`；配合 `src/index.ts` 暴露所有子模块（含 `MobileAccessGateway`、`AccessController`、`MobileAccessService`、`assertSupportedDshVersion`、`parseGatewayConfig` 等）；Client 端入口 `src/mobile-layout.ts` 与 `src/client.ts`

## 适用场景
需要在家里沙发上、Wi-Fi 切换到手机热点时、或出差到酒店/办公局域网里继续使用电脑端 DeepSeek Harness，并希望在同一份会话和工作区里推进任务时安装使用。常见搭配是：电脑常驻 DSH Web，手机扫码配对一次后随时打开 Android App 或浏览器访问，配对设备可以执行工具、读写工作区文件、调用电脑侧本地脚本完成自动化。

## 前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| DSH Host（`@deepseek-ai/dsh-host-webserver`） | `0.1.0-rc.5` / `0.1.0-rc.6` / `0.1.0-rc.7` | 启动时 `assertSupportedDshVersion` 强制校验该范围，未通过会直接拒绝；不通过则不会变通启动 |
| `@deepseek-ai/dsh-commands` | `0.1.0-rc.6` 或 `0.1.0-rc.7` | 提供 `/mobile <需求>` 对话命令，依赖其 `ctx.commands.register` |
| `@deepseek-ai/dsh-llm` | `0.1.0-rc.6` 或 `0.1.0-rc.7` | `/mobile` 调用 `agent.steer` 需要其 `createUserMessage` 与 `boundContextSummary` |
| React | `^18.2.0` | 仅为 Client face 的 peer（用于 `createElement` 渲染桥接 UI） |
| Node.js | `^22.19.0` 或 `>=24.0.0` | `package.json:66` 的 engines 字段 |
| 平台（宿主机） | macOS / Windows / Linux | 跨平台，依赖 `selfsigned`/`bonjour-service`/`qrcode` 均为纯 JS，无原生模块；`setup` 在 Windows 上会额外通过 PowerShell 加两条仅限 LocalSubnet 的入站防火墙规则 |
| Android App | Android（Kotlin WebView）| 唯一受支持的手机原生 App；通过 GitHub Release 取得 APK；配对使用 App 内固定的 CA 与 Android Keystore 加密的设备 token |

## 安装方式
```bash
dsh plugin --profile web add github:saya-ch/dsh-mobile
```

> 安装后必须运行 `dsh plugin --profile web exec dsh-mobile setup` 完成首次的自签名 CA + 局域网选择；之后重启 DSH，在左下角“移动访问”卡片即可生成密钥/二维码。

## 配置项
| 配置 | 类型 | 说明 | 默认值 |
|---|---|---|---|
| `setupFile` | 隐藏字段 | 由 `setup` 命令写入的受管配置文件绝对路径；用于自动跟随网卡 | `cordis.patch.yml` 中默认 `$DSH_HOME/mobile-access/setup.json` |
| `listenHost` / `listenPort` | 字符串 / 端口号 | LAN 监听地址；纯数字端口；与 `publicOrigin` 互斥 | `127.0.0.1` / `3443` |
| `upstreamOrigin` | 字符串 | 回环代理要桥接到的 DSH Web 监听源，必须是带端口的回环 HTTP | `http://127.0.0.1:3080` |
| `publicOrigin` / `publicAuthorities` | 字符串数组 | 设备端要使用的对外 HTTPS 主机名集合；非回环监听必须显式给出 | 无（设了 `publicOrigin` 则禁用 `listenPort/publicAuthorities`） |
| `allowedCidrs` | CIDR 数组 | 允许直连的客户端网段；非回环监听必须给出 | 回环监听默认 `127.0.0.0/8, ::1/128` |
| `stateFile` | 路径 | 设备注册表持久化文件（仅存 token digest 与吊销时间戳） | 必填 |
| `controlFile` | 隐藏字段 | DSH 插件卡片的开/关状态文件 | `$DSH_HOME/mobile-access/control.json` |
| `customCssFile` / `customScriptFile` | 路径 | 手机端可选的自定义 `mobile.css` / `mobile.js`，存改就生效 | 默认位于 `$DSH_HOME/mobile-access/` |
| `mobileLayoutFile` | 隐藏字段 | Host face 注入手机端的 browser bundle | 默认 `./mobile-layout.js` |
| `instanceId` | SHA-256 | 局域网发现/绑定 CA 用的稳定安装标识；非认证机密 | 默认为 `stateFile` 路径的 SHA-256 |
| `pairingCaFile` | 路径 | 给 Android 安装器下发并绑定指纹的 CA 证书绝对路径；必须是 `instanceId` 对应的自签 CA | 受管 setup 自动写入 |
| `initiallyEnabled` | 布尔 | 控制首启时插件开关（由插件卡片持久化覆盖） | `false` |
| `tls` | 子对象 | 服务端 TLS 来源：`mode: 'provided'`（自备 cert/key，可选中间 CA）或 `mode: 'disabled'`（仅回环监听） | 受管 setup 自动签 |
| `pairingTtlMs` / `deviceTtlMs` / `sessionTtlMs` | 毫秒 | 配对码、设备 token、会话的有效期 | `120000` / `90 天` / `8 小时` |
| `maxDevices` / `maxSessions` / `maxConnections` / `maxActiveRequests` / `maxWebSockets` | 整数 | 并发与持久化资源上限 | `32` / `64` / `64` / `32` / `16` |
| `maxBodyBytes` / `upstreamTimeoutMs` | 字节 / 毫秒 | 单次回环代理请求体上限 / 上游回环代理超时 | `160 MiB` / `30000` |
| `rateLimitWindowMs` / `maxPairingAttempts` / `maxRateLimitKeys` | 整数 | 限流窗口、配对失败上限、限流键表上限 | `60000` / `8` / `256` |

> 说明：上表的 `stateFile`、`controlFile`、`setupFile`、`customCssFile`、`customScriptFile`、`mobileLayoutFile`、`pairingCaFile` 这些“隐藏字段”默认在 Schema 中标记为 hidden，普通用户无需手动写；插件市场安装 + `dsh-mobile setup` 一条命令会生成默认值。如需调整，主要是改 `cordis.patch.yml` 里的 `mobile-access` 行的 `config:` 子键，或用 `setup --address <IP> --port <port>` 改 `setupFile` 重定向。

## 常见问题
**Q: 安装后界面里没看到“移动访问”卡片怎么办？**

A: 首先确认已运行 `dsh plugin --profile web exec dsh-mobile setup` 写入 `$DSH_HOME/mobile-access/setup.json`，并已重启 DSH；如果还是没显示，检查 DSH Host 版本是否在白名单 `0.1.0-rc.5/rc.6/rc.7` 内；不兼容时插件会直接抛错而不是带病启动，启动日志中会出现 `unsupported DeepSeek Harness version ...`。

**Q: 用手机浏览器第一次访问被提示证书不受信任怎么办？**

A: 这是预期行为。LAN 网关使用自签 HTTPS，需要在手机浏览器里手动信任这套设备证书，或改用 Android App（App 内私有 trust store 自动接受该 CA，且不会写入系统信任区）。

**Q: 自动发现没找到我的电脑，配对二维码扫不出来怎么办？**

A: 先在电脑端用 `dsh plugin --profile web exec dsh-mobile setup --address 192.168.x.x` 显式绑定 IP；如果多网卡地址不在同一网段，也可以指向 `https://IP:端口` 手动连接；Windows 上 `setup` 默认会添加只对 LocalSubnet 开放的入站防火墙规则，被防火墙拦截时可重审。

**Q: 设备凭据丢了、或者借给别人用过的手机，怎么撤销？**

A: 配对设备拥有电脑端 DSH 的全部操作权限，必须当完全可信设备对待。丢失/转手时应在电脑端插件卡片里删除该设备（删除会立即使其失效），必要时用 `dsh plugin --profile web exec dsh-mobile purge --yes` 清空 `$DSH_HOME/mobile-access/` 全部数据并轮换所有 token。

**Q: `mobile.css` / `mobile.js` 改完没生效？**

A: 手机端在 5 秒内会拉取一次保存后的版本，请确认文件确实写到了 `$DSH_HOME/mobile-access/` 下，而非被系统重定向到只读/隔离位置；另外 `mobile.js` 必须用 `window.dshMobile.register(({ root }) => { ... })` 形式挂载到 `root`，否则插件运行时不会调用。

**Q: 为什么我的电脑装的是其他版本 DSH，插件报错不让启动？**

A: 见 `src/compatibility.ts` — 插件强制限定已验证的 3 个 DSH 版本，未通过会立刻失败，这是为了避免在桌面页面或 layout 契约已变更的情况下硬撑成“勉强能用”。升级 DSH Host 后请同时升级 dsh-mobile。

**Q: 同一个网络里有多台电脑怎么区分？**

A: 每台电脑自动派一个 stable `instanceId`（来自 `stateFile` 路径的 SHA-256），Android App 扫码或局域网扫描后用这个 ID 匹配同一台电脑，不会把多台电脑的同名实例串起来；也可以在“移动访问”卡片里看到当前分配的 `instanceId`。

**Q: 卸载 `dsh-mobile` 后会留下数据吗？**

A: 会。仅 `remove` 时退出进程，不动 `$DSH_HOME/mobile-access/`（证书、设备注册表、控制状态、自定义文件、扩展都在这里）；要彻底清掉证书、设备和自定义文件，先 `dsh plugin --profile web exec dsh-mobile purge --yes`，再 `remove`。Windows 上 purge 会同步移除 setup 时添加的两条防火墙规则。

## 上手难度
进阶 — 安装 + setup 命令本身就两条，但要安全使用需要理解“配对设备完全可信、不应放在公网、丢失必撤销”的安全模型；要发挥 `/mobile` 定制和扩展能力则需要熟悉 `$DSH_HOME/mobile-access/` 目录约定和 `host.mjs` 的本地用户权限边界；遇到多网卡/防火墙场景需要会看 `setup --address` 显式绑定。

## 已知问题与限制
- Alpha 阶段：仅维护最新的 prerelease；早期 alpha 不接收安全修复（`SECURITY.md:5-7`）
- iOS 客户端未发布：仅 Android App 在 Release 范围内；iOS 仍是本地实验，不进入构建与 Release（`README.md:24`、`apps/mobile/README.zh-CN.md:7`）
- 移动网关必须放宽 CSP：DSH 上游 HTML 自带 inline JS、用 `new Function` 复活 Schemastery 回调、动态样式，因此网关 CSP 当前包含 `script-src 'self' 'unsafe-inline' 'unsafe-eval'` 和 `style-src 'self' 'unsafe-inline'`；其余指令仍按 HTTPS-only 收严，但脚本注入风险未被零化（`SECURITY.md:30-32`），依赖上游 DSH 引入 nonce / 稳定 hash / 外部引导资源后才能收紧
- 强制 DSH 版本白名单：`src/compatibility.ts:2-6` 只放 `0.1.0-rc.5/rc.6/rc.7`，其它版本启动即失败；升级 DSH 后需同时升级 dsh-mobile
- 依赖 `0.0.0.0` 内网监听时必须显式给出 `publicAuthorities`：不能用回环默认值；并且一旦设了 `publicOrigin`，TLS 必须保留为 `mode: 'provided'`，不能禁用（`src/config.ts:211`、`238-240`）
- 自签 CA 必须在 LAN 内单独下发给 Android：Android App 只信任自己 `instanceId` 指纹绑定过的 CA，不调用 Android 系统信任区；浏览器需要手动信任（`SECURITY.md:19-22`）
- 设备注册表用临时文件原子重命名写入，文件大小上限 1 MiB、设备上限默认 32（`src/storage.ts:89,302`）；不可绕过 `JsonDeviceStore` 直写多个实例
- `mobile-access` 在 `setup` 后必须能写入 `$DSH_HOME/mobile-access/`；只读文件系统/不可写 HOME 会让 setup 抛错；多 profile 并存时各自独立的 `stateFile` 和 `controlFile` 路径由 `cordis.patch.yml` 注入

---

This document is auto-generated by [deepseek-plugin.org](https://deepseek-plugin.org). HTML page: [dsh-mobile](https://deepseek-plugin.org/plugins/saya-ch/dsh-mobile)
Wiki generated by AI (model: `MiniMax-M3`)
