dsh-mobile 0.5.5 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/brand/repository-hero.png" alt="用手机使用电脑中的 DeepSeek Harness" width="100%">
2
+ <img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/brand/repository-hero.png?v=0bfc8f909993" alt="用手机使用电脑中的 DeepSeek Harness" width="100%">
3
3
  </p>
4
4
 
5
5
  <h1 align="center">DSH Mobile</h1>
@@ -31,14 +31,14 @@
31
31
 
32
32
  > DSH Mobile 是 DeepSeek Harness 社区插件,原生 App 仅支持 Android。
33
33
  >
34
- > **当前正式版本:0.5.5**。优化手机输入栏与模型搜索字号,修复 Android 模型菜单返回、旋转时附件丢失和撤销状态覆盖,新增 macOS cloudflared 支持。[更新记录](CHANGELOG.md#055---2026-10-04)。
34
+ > **当前正式版本:0.6.1**。优化远程 HTTPS 配置表单,完善 Caddy 固定构建与跨平台验证;Android App 同步版本,保留 0.6.0 的模块管理、图标选择、设备排序与扫码改进。[更新记录](CHANGELOG.md#061---2026-10-08)。
35
35
  >
36
- > **升级提醒**:请同步更新插件与 Android App 至 0.5.5,以获得完整修复;同一正式签名的旧 App 可原位升级并保留配对。[兼容说明](#兼容性)。
36
+ > **升级提醒**:请同步更新插件与 Android App 至 0.6.1;同一正式签名的旧 App 可原位升级并保留配对。[兼容说明](#兼容性)。
37
37
 
38
38
  <p align="center">
39
- <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.5.5/dsh-mobile-android-v0.5.5.apk"><img src="assets/brand/app-icon-rounded.svg" alt="DSH Mobile 安卓应用图标" width="72" height="72"></a><br>
40
- <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.5.5/dsh-mobile-android-v0.5.5.apk"><strong>下载 Android App 0.5.5</strong></a><br>
41
- <sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.5.5">版本说明与校验文件</a></sub>
39
+ <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.6.1/dsh-mobile-android-v0.6.1.apk"><img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/brand/app-icon-rounded.svg" alt="DSH Mobile 安卓应用图标" width="72" height="72"></a><br>
40
+ <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.6.1/dsh-mobile-android-v0.6.1.apk"><strong>下载 Android App 0.6.1</strong></a><br>
41
+ <sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.6.1">版本说明与校验文件</a></sub>
42
42
  </p>
43
43
 
44
44
  DSH Mobile 是一个 DeepSeek Harness 插件,让手机浏览器或 Android App 通过局域网,或可选的 Tailscale Funnel、cpolar、cloudflared、自建 FRP 或自有反向代理远程通道连接电脑,继续使用同一份会话、工作区、消息和工具。电脑端分别启停局域网与远程访问、分别管理配对授权;Android App 统一显示已配对电脑。插件不修改 DeepSeek Harness 源码。
@@ -62,13 +62,48 @@ DSH Mobile 是一个 DeepSeek Harness 插件,让手机浏览器或 Android App
62
62
 
63
63
  ## 0.5.5 移动体验改进
64
64
 
65
+ <details>
66
+ <summary>已发布版本的输入栏、返回键与旋转改进</summary>
67
+
65
68
  屏幕宽度不超过 720px 时,未编辑的输入栏保留可滚动的 72px 草稿预览,为会话留出更多空间;编辑区或工具栏获得焦点后恢复展开。文字、引用和附件保留,发送、排队、引导与停止按钮的触摸区域至少为 44px。提交和输入法行为继续由既有 DSH 与 App 机制处理。
66
69
 
67
70
  模型搜索框与配对页的输入字号已补齐,Android 返回键可逐级退出模型菜单。App 旋转和调整窗口大小时保留当前页面、引用与未发送图片,已收到的“电脑端已移除”状态不会被后续检测覆盖。请同步更新插件与 App;App 进程终止后,未发送附件仍可能丢失。
68
71
 
72
+ </details>
73
+
74
+ ## 0.6.1 更新
75
+
76
+ - **远程配置**:重新整理 HTTPS 地址、自备代理与托管 Caddy 的表单分组、字段和操作按钮,小窗口下也能清楚区分配置方式。
77
+ - **构建与验证**:固定 Caddy 构建输入,增加 Windows/Linux 双构建一致性及隔离 TLS、配对、API、WebSocket 和清理检查,修正 Windows Git Bash 路径与测试失败分类。[Caddy 状态](docs/CADDY_MANAGED.md)
78
+ - **Android App**:同步为 0.6.1(build 77),原生功能与配对协议不变,沿用正式签名;已有配对无需重建。
79
+
80
+ 感谢 [@abworks-dev](https://github.com/abworks-dev) 的 [#184](https://github.com/saya-ch/dsh-mobile/pull/184) 与 [#185](https://github.com/saya-ch/dsh-mobile/pull/185)。托管 Caddy 仍未开放正式安装;完整记录见 [CHANGELOG](CHANGELOG.md#061---2026-10-08)。
81
+
82
+ ## 0.6.0 更新
83
+
84
+ 本次更新聚焦模块管理、App 个性化与移动访问体验;完整记录见 [CHANGELOG](CHANGELOG.md#060---2026-10-07)。
85
+
86
+ - **连接与加载**:重试临时失败的静态资源请求,长时间 DSH API 操作不再被普通传输超时截断;不重放 API 或写入请求。[慢链路说明](docs/SLOW_CONNECTIONS.md)
87
+ - **移动布局**:输入栏更紧凑,改善键盘打开和大字号时的按钮可达性;支持独立移动字号、触屏浏览器 Enter 换行,以及窄屏面板“返回会话”。
88
+ - **Android 体验**:可选鲸鱼娘及六种鲸鱼标志配色;改进扫码预览、对焦与缩放。设备列表支持固定顺序、上移/置顶,长按左上角抽屉按钮可切换电脑;启动仍优先连接最近使用的电脑。
89
+ - **模块管理**:电脑默认与本设备选择分别保存,检查启动必需模块和依赖,不卸载插件、不自动刷新当前会话。旧选择失效时提供本设备恢复入口。[操作指南](docs/CLIENT_MODULES.md)
90
+ - **网络与兼容**:托管局域网可追加明确的可信来源网段;补齐部分旧 WebView 缺失 API,完善扩展调用超时与取消。[可信网段](docs/TRUSTED_NETWORKS.md) · [WebView 说明](#app-与手机浏览器)
91
+
92
+ > **托管 Caddy 尚未开放安装。**代码与隔离 TLS 验证已完成,但可信固定二进制尚未分发;现有自备代理和其他通道不受影响。[状态与条件](docs/CADDY_MANAGED.md)
93
+
69
94
  ## 快速开始
70
95
 
71
- 已经安装 `dsh` 命令:
96
+ 先在电脑安装插件,再用 Android App 或手机浏览器配对。按你使用的 DSH 选择安装方式。
97
+
98
+ ### 官方 DSH Desktop
99
+
100
+ 在应用的 **插件** 页面安装并启用 `dsh-mobile`,再打开 **移动访问** 配置局域网或远程通道。Desktop 的 profile 由应用管理,不适用下面的 `--profile web` 命令。
101
+
102
+ 安装或更新后,请从应用菜单正常退出并重新打开 Desktop。若提示 `dsh-mobile-question-fixes: failed to import`,先完成这次重启,让 Desktop 重新读取附带组件;无需清除会话或配对数据。
103
+
104
+ ### DSH Web
105
+
106
+ 已经安装 `dsh` 命令时,在终端执行:
72
107
 
73
108
  ```powershell
74
109
  dsh plugin --profile web add dsh-mobile@latest
@@ -76,17 +111,18 @@ dsh plugin --profile web exec dsh-mobile setup
76
111
  dsh --profile web
77
112
  ```
78
113
 
79
- 直接使用 DeepSeek Harness 源码:
114
+ <details>
115
+ <summary>使用 DeepSeek Harness 源码或社区插件市场</summary>
116
+
117
+ 在 DeepSeek Harness 源码目录中执行:
80
118
 
81
119
  ```powershell
82
- corepack enable; pnpm install
120
+ pnpm install
83
121
  pnpm dsh plugin --profile web add dsh-mobile@latest
84
122
  pnpm dsh plugin --profile web exec dsh-mobile setup
85
123
  pnpm dsh --profile web
86
124
  ```
87
125
 
88
- 使用官方 DSH Desktop:在应用的 **插件** 页面安装并启用 `dsh-mobile`,再打开 **移动访问** 完成局域网配置或选择远程通道。Desktop 的 profile 由应用管理,不适用上面的 `--profile web` 命令。
89
-
90
126
  也可以通过插件市场安装(可选):
91
127
 
92
128
  ```powershell
@@ -95,9 +131,13 @@ dsh plugin --profile web add dshmarket
95
131
 
96
132
  重启 DSH 后,在 **设置 → 插件市场** 里搜索 dsh-mobile 并安装。首次打开“移动访问”时,局域网页会列出电脑当前网络;确认网卡后由插件生成私有证书并配置局域网,按提示重启一次 DSH 即可使用,无需再打开终端运行 `setup`。
97
133
 
134
+ </details>
135
+
98
136
  `setup` 会自动选择并记住当前局域网,切换 Wi-Fi、热点或 IP 后通常自动恢复;仅在自动选择失败时使用 `--address 192.168.x.x`。设置、证书、设备和自定义文件保存在 `$DSH_HOME/mobile-access/`。
99
137
 
100
- 安装并启动 DSH 后,按照下一节选择局域网或远程连接。
138
+ ### 安装手机 App
139
+
140
+ 从上方链接下载正式 Android APK,或用手机浏览器打开电脑端生成的配对链接。随后按[连接教程](#连接教程)选择局域网或远程访问;手机不需要安装额外的隧道客户端。
101
141
 
102
142
  通过 npm 安装的插件会在桌面界面加载时检查新版本,有更新时在访问面板标题右侧显示“更新插件”,安装后需重启 DSH。App 下载入口展示最新版本;本地开发包不会被自动覆盖,Android App 不会主动检查或推送版本更新。
103
143
 
@@ -120,8 +160,15 @@ dsh plugin --profile web add dshmarket
120
160
 
121
161
  端口说明:`dsh web --port` 修改 DSH Web 上游端口(默认 3080),插件会自动跟随;`dsh-mobile setup --port` 修改 Mobile HTTPS 监听端口(默认 3443),配对二维码会包含实际端口。
122
162
 
163
+ <details>
164
+ <summary>无界面主机、内网代理与额外来源网段</summary>
165
+
123
166
  无图形界面的 Linux 主机,或通过局域网 IP / 反向代理打开 DSH Web 时,左下角 **移动访问** 管理口同样可用。管理 API 仍要求 TCP 对端是本机回环(例如本机 `socat` / 反向代理连到 `127.0.0.1`),不会把插件自己暴露到公网;浏览器 Host 可以是 `localhost`、RFC1918 或 IPv4 链路本地地址,公网 IP 和任意域名仍会返回 403。反向代理必须只对可信的本机或内网请求开放,不能把 `/api/mobile-access` 管理路径公开转发到互联网。手机走的独立 HTTPS 入口(默认 3443)仍是移动端界面,不会变成桌面管理口。
124
167
 
168
+ 已有 WireGuard 等受控路由网络时,0.6.0 提供[额外可信网段](docs/TRUSTED_NETWORKS.md)。它只追加明确的来源 CIDR,不建立 VPN 或路由,也不自动改 Windows 防火墙。
169
+
170
+ </details>
171
+
125
172
  不安装 App 也可以访问:点击 **复制配对链接**,在手机浏览器中打开。局域网使用私有证书,浏览器首次访问可能提示不受信任;只在确认这是自己的 DSH 网关后按浏览器指引继续。公开证书的远程入口不应出现证书警告,若出现请检查通道和证书,不要直接忽略。
126
173
 
127
174
  手机浏览器中的配对和重新连接页面会按浏览器的 `Accept-Language` 显示简体中文、英文或意大利文;Android App 则跟随系统语言。DSH 内的插件控制面板继续跟随 DSH 当前语言。
@@ -134,20 +181,39 @@ dsh plugin --profile web add dshmarket
134
181
 
135
182
  页面能打开但一直“重连中”,应先区分 WebSocket 升级失败、慢速数据同步和心跳超时,操作见[慢链路与反复重连指南](docs/SLOW_CONNECTIONS.md)。0.5.4 还提供按确切路径启用的 WebSocket 压缩,默认关闭;长会话或计量网络可按指南尝试,并对比实际传输量。
136
183
 
184
+ | 连接方式 | 需要准备 | 地址特点与注意事项 |
185
+ | --- | --- | --- |
186
+ | cpolar | cpolar 账号与 Authtoken | 国内网络可优先尝试;免费临时地址可能变化 |
187
+ | cloudflared 快速隧道 | 无需账号或域名 | 临时随机地址;不支持 SSE,[即时通知存在限制](docs/CLOUDFLARE_TUNNEL.md#快速隧道的功能限制) |
188
+ | cloudflared 命名隧道 | Cloudflare 账号与域名 | 固定域名;[配置指南](docs/CLOUDFLARE_TUNNEL.md) |
189
+ | Tailscale Funnel | Tailscale 登录与 Funnel 授权 | 中国大陆网络可能不稳定 |
190
+ | 自建 FRP | 已有 VPS,域名或真实公网 IPv4 | 需管理服务器;[部署](docs/SELF_HOSTED_FRP.md)或[接入既有 frps](docs/ATTACH_EXISTING_FRPS.md) |
191
+ | 自有反向代理 | 已有公开 HTTPS 代理 | 插件只提供私有认证后端;[配置指南](docs/SELF_HOSTED_ORIGIN.md) |
192
+
137
193
  <p align="center">
138
194
  <img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/remote-access.png" width="82%" alt="DSH Mobile 远程访问与通道选择">
139
195
  </p>
140
196
 
141
- 1. 在 DeepSeek Harness 左下角打开 **移动访问 → 远程**,选择一种连接方式:
197
+ 1. 在 DeepSeek Harness 左下角打开 **移动访问 → 远程**,按上表选择通道,完成登录或配置。
198
+ 2. 状态变为“远程访问已就绪”后,点击 **生成远程配对二维码**。自有反向代理仅显示“后端已监听”,仍需确认公网 HTTPS 与 WebSocket 可用。
199
+ 3. 在 Android App 中选择 **远程访问**,扫描当前二维码完成配对;公开证书入口也可用手机浏览器打开配对链接。
200
+ 4. 以后 App 使用保存的地址与凭据自动重连。临时地址改变时,重新扫描电脑端当前二维码验证新地址,不需要清除 App 数据;旧设备 token 不会发送给新 Origin。
201
+
202
+ <details>
203
+ <summary>各通道的安装与登录步骤</summary>
204
+
205
+ 在面板选择对应通道后:
142
206
  - **Tailscale Funnel**:点击 **启用远程访问**,在打开的官方页面完成一次 Tailscale 登录;按面板提示继续允许 Funnel,然后返回 DSH 等待连接就绪。
143
207
  - **cpolar**:点击 **安装官方组件**,登录 cpolar 控制台取得 Authtoken,粘贴后点击 **保存并连接**。组件只会在确认后下载到插件私有目录;免费临时地址可能在 DSH 或 cpolar 重启后变化。
144
208
  - **自建 FRP(高级)**:已有 VPS 时,展开 **自建连接**,填写 frps 信息和公开 HTTPS 域名或真实公网 IPv4。可以复制受限的 frps + Caddy 模板手动部署,也可以在 Ubuntu/Debian + systemd 上用 SSH 密钥一键部署;不支持密码登录,也不会覆盖非 DSH Mobile 管理的 Caddy 配置。部署或清理前须到 VPS 控制台核对主机指纹。本机 `frpc` 按需下载校验,VPS 需单独清理。需要 Android App 0.3.3 或更新版本;证书、端口和操作步骤见 [自建 FRP 使用指南](docs/SELF_HOSTED_FRP.md)。
145
209
  - **自有反向代理**:展开 **自建连接 → 自有反向代理**,填写公网 HTTPS 地址(支持自定义端口)、私有监听 IPv4、独立 HTTP 后端端口(默认 3444)和代理来源 CIDR,再点击 **保存并启动后端**。适合已有 Lucky/Nginx/Caddy 的用户,无需隧道组件;需要 Android App 0.4.0 或更高版本。详见 [自有反向代理指南](docs/SELF_HOSTED_ORIGIN.md)。
146
210
  - **cloudflared 快速隧道**:点击 **安装官方组件**,插件在确认后从官方发布页下载固定版本到插件私有目录,随后自动申请一个临时公网地址(quick tunnel),**无需注册或登录**。适合不想注册账号的用户;quick tunnel 地址每次重连都会变化,官方定位为测试用途、有限流且无可用性保证,请勿用于必须长期可达的生产访问,只适合临时或验证场景。
147
211
  - **cloudflared 命名隧道**:已有 Cloudflare 账号和域名时,把隧道类型切到 **命名隧道**,填入连接器令牌、公网域名与本机转发端口,即可获得重启后不变的固定地址(令牌只存私有目录、只经环境变量传给 cloudflared)。步骤见 [Cloudflare 命名隧道](docs/CLOUDFLARE_TUNNEL.md)。
148
- 2. 状态变为“远程访问已就绪”后,点击 **生成远程配对二维码**。自有反向代理仅显示“后端已监听”:它不验证公网连通性,仍需检查代理 HTTPS、证书与 WebSocket 并用手机验收。
149
- 3. 在 Android App 中进入 **远程访问**,扫描二维码完成独立配对。
150
- 4. 此后 App 会保存当前地址和设备凭据并自动重连。若 cpolar 免费临时地址发生变化,请扫描电脑端当前远程二维码重新验证连接;无需清除 App 数据。旧设备 token 只会发送到原先保存的精确 Origin,不会发送给二维码中的新域名。
212
+
213
+ </details>
214
+
215
+ <details>
216
+ <summary>远程通知、服务器运维与安全限制</summary>
151
217
 
152
218
  > **远程通知说明**:浏览器的 `Notification` 权限按 Origin 分别授权,网页系统通知只显示在运行该网页的设备上。Android App 的任务提醒是独立的 0.4.0 功能,需要在 App 内 DSH 的 **设置 → 通用设置** 中主动开启,且依赖 WebView 页面仍存活;它不是通用的后台推送。需要可靠的后台推送时,请使用你已配置的服务端 webhook 或机器人通道。
153
219
 
@@ -157,7 +223,9 @@ Tailscale Funnel 覆盖范围广,但在中国大陆网络下可能不稳定。
157
223
 
158
224
  自有反向代理的 HTTP 后端只允许留在可信私网;**不要把它映射到公网,也不要绕过它直连 DSH 或现有 LAN 3443**。来源 CIDR 匹配代理的直接 TCP 来源,不信任转发头;反代须保留外部 Host(含端口)、Origin、Cookie 和 WebSocket。清除代理配置不会删除已配对远程设备。
159
225
 
160
- 远程公开地址仍受 DSH 设备配对保护。内置 Funnel 与托管 cpolar、cloudflared 支持 Windows x64 与 Linux x64/arm64;cloudflared 还支持 macOS x64/arm64 按需安装。FRP 0.70.1 支持 Windows、Linux、macOS 的 x64 与 arm64。各通道的系统支持矩阵见[兼容性](#兼容性)。
226
+ 各通道的系统和组件支持范围见[兼容性](#兼容性)。本机托管的程序、凭据与配置可从面板清理;VPS 上的文件必须按对应指南单独清理。
227
+
228
+ </details>
161
229
 
162
230
  ## 扩展与自定义
163
231
 
@@ -194,14 +262,15 @@ Tailscale Funnel 覆盖范围广,但在中国大陆网络下可能不稳定。
194
262
 
195
263
  ## 设备管理
196
264
 
197
- Android App 用一个“已配对设备”列表同时显示多台电脑:局域网、cpolar、cloudflared、Tailscale Funnel 和自建 FRP 配对共处一处。首次升级会自动迁移旧版局域网与远程凭据,不要求重新配对;地址变化且 `instanceId` 与原记录一致时会合并条目,保留自定义名称。自签 FRP 使用独立 CA 指纹作为身份,首次与局域网配对时可能显示为另一条记录。设备 Token 和局域网 CA 由 Android Keystore 加密保存;0.4.6 App 还会加密保存自签 FRP 入口固定的远程 CA。这些信息不会显示在设备列表或二维码中。
265
+ Android App 在“已配对设备”中统一显示电脑及其局域网/远程连接记录。两种方式的授权彼此独立,同一台电脑可能有两条记录。升级会迁移旧凭据,无需重新配对;重新验证变化的地址后,相同身份的记录会合并并保留名称。自签 FRP 使用独立 CA 指纹,可能另成一条记录。Token 和固定的 CA 由 Android Keystore 加密;配对二维码含短期配对信息,不含长期设备 Token 或 CA 私钥。
198
266
 
199
267
  每条记录显示自定义名称、连接方式、Origin、定期更新的可达状态和最近连接时间。绿色状态点表示“可达”,灰色状态点表示“检测中”“暂不可达”“配对已过期”或“电脑端已移除”;可达性检查直接验证 DSH Gateway,不依赖 ICMP,也不会把暂时断网误判成电脑端撤销。
200
268
 
201
- - **启动时打开 → 直接进入 DSH**(默认):单设备直接连接;多设备优先连接上次使用的设备,其次按最近连接时间选择仍有效的设备。连接超过有限重试预算后自动回到列表,不会无限转圈。
202
- - **启动时打开 → 显示设备列表**:每次启动先选择电脑,适合经常在多台设备之间切换。该选项位于列表右上角的设置按钮中,修改后立即保存。
269
+ - **启动行为 → 直接进入 DSH**(默认):单设备直接连接;多设备优先连接上次使用的设备,其次按最近连接时间选择仍有效的设备。连接超过有限重试预算后自动回到列表,不会无限转圈。
270
+ - **启动行为 → 显示设备列表**:每次启动先选择电脑,适合经常在多台设备之间切换。入口是列表右上角 **设置 → 启动行为**,修改后立即保存。
203
271
  - 点按设备行可连接;右侧“…”和长按提供相同的操作面板,可编辑名称、立即检测、重新配对或删除本地记录。删除前会二次确认,并提供短暂撤销;撤销只恢复本机记录,不恢复电脑端已经撤销的授权。
204
- - 在 WebView 页面打开 DSH **设置 → 通用设置**,选择 **切换电脑** 可回到已配对设备列表;该动作仅在 Android App 中显示。在线收到电脑端撤销通知后,App 保留该条目并显示“电脑端已移除”,停止自动重连,同时提供 **重新配对** 和 **删除本地记录**。
272
+ - 在 WebView 页面打开 DSH **设置 → 通用设置 → 切换电脑** 可回到已配对设备列表。在线收到电脑端撤销通知后,App 保留条目并显示“电脑端已移除”,停止自动重连,提供重新配对和删除本地记录。
273
+ - **0.6.0 新增**:列表保持固定顺序,新配对追加到末尾,升级保留旧版可见顺序;操作面板提供 **上移/置顶**,顺序与启动选择分开。App 内还可长按左上角抽屉开关返回列表,短按仍开关抽屉;手机浏览器没有此原生动作。
205
274
 
206
275
  电脑端撤销会永久删除持久化存储中的设备记录及令牌摘要,而不是保留 `revokedAt` 标记;启动时也会清理旧版留下的已撤销记录。删除后,旧令牌与未知令牌一样返回 `401 authentication_failed`。现有 App 若在离线期间被撤销,再次检测时可能显示“配对已过期”,需要重新配对;在线会话仍会收到撤销通知并立即断开。
207
276
 
@@ -222,20 +291,27 @@ Android App 用一个“已配对设备”列表同时显示多台电脑:局
222
291
 
223
292
  0.5.3 包含默认启用、可独立启停的 `dsh-mobile-question-fixes` 提问卡片组件。在 DSH 插件详情的组件列表中关闭它,可立即恢复原卡片样式。长问题、选项和底部按钮在有限高度的卡片内共同滚动,收起时标题最多显示两行;手机浏览器的触屏 Enter 保留换行,Android App 则仅在软键盘已打开且未连接实体键盘时使用该行为。答案草稿仍由 DSH 管理,不另存到浏览器 localStorage。
224
293
 
225
- 移动适配保持 DSH 原有的工作区、任务管理、终端和文件面板入口,不会把第三方插件内容隔离成另一套页面。下面的宽屏截图展示 Android App 在宽屏下的布局。App 会根据屏幕宽度自适应:手机使用抽屉和浮层,宽屏使用并排面板;两种布局共享相同的功能和连接方式。第三方插件仍由 DSH 自己加载,移动层负责适配布局与连接,不修改 DeepSeek Harness 源码。
294
+ DSH Mobile 保留工作区、任务管理、终端和文件面板入口,第三方插件仍由 DSH 加载。移动层适配布局与连接,不修改 DeepSeek Harness 源码;具体插件在小屏上是否易用,仍取决于它自身的布局与交互。
226
295
 
227
- 兼容条件与 WebSocket 放行规则:
296
+ 第三方插件需要能在 DSH Web 页面中运行,并使用可达的 HTTP/WebSocket 接口。移动层不保证把所有插件的自定义布局自动改成手机布局,也不能替代插件依赖的桌面原生窗口。
228
297
 
229
- 代理页面为兼容部分社区插件允许嵌入 HTTP 页面;这类内容未加密,可能被篡改,浏览器也可能因混合内容策略拦截。处理敏感内容时请使用 HTTPS。通过 HTTPS 管理入口打开远程面板时,页面顶部会显示相同提醒。
298
+ ### WebSocket 放行
230
299
 
231
- - 0.5.2 不再因 DSH 版本号未列入依赖范围而阻止安装;已对 `0.2.0-rc.2` 进行源码契约、隔离配对、页面启动及工作区读取检查。未来新版本仍需实际验证。官方 DSH Desktop 的移动访问管理入口继续要求已认证的 DSH 会话,不放宽普通 Web 页面的管理请求。
232
- - 0.5.1 延续对 DSH `0.1.7-alpha.2`、`0.1.7-rc.1` 和 `0.1.7-rc.2` 的契约及启动检查,并适配 `0.2.0-rc.1`。官方 DSH Desktop `0.1.7-rc.2` 使用 `dsh-app://app/` 页面。
233
300
  - 网关默认只允许 DSH 内置的第一方 WebSocket 路径,包括 `/sidebar/ws/terminal`。社区侧边栏插件使用的其他路径默认拦截,通常会在诊断页显示为待处理项目;截图中的 `/sidebar/ws/agent-opens` 和 `/sidebar/ws/agent-terminals` 就属于这类需要按实际插件确认的路径。
234
301
  - 在 **连接诊断 → 第三方 WebSocket 路径** 中,只对确认过的精确路径点击 **允许**。系统不接受带查询字符串或模糊前缀的路径;不建议使用“全部允许”。已允许的路径可以随时移除,局域网和远程连接使用同一套规则。
235
302
  - 放行只代表该路径可以通过已认证、同源的 DSH Mobile 网关,不会开放任意 TCP/UDP 端口,也不会绕过设备配对。若社区插件仍然连接失败,先看诊断页的实际拦截路径,再按一条路径放行。
236
303
 
237
304
  若另一个远程访问插件在 DSH Mobile 页面上显示自己的“未配对”提示,那是两套不同的授权。可先在电脑端暂时关闭另一插件的远程访问,确认 Mobile 链路是否恢复;不要把 DSH Mobile 密钥输入它的配对页。仅用下面的模块排除选项不能保证移除该插件在页面启动前注入的请求改写脚本,参见 [#111](https://github.com/saya-ch/dsh-mobile/issues/111)。
238
305
 
306
+ 网关允许部分 HTTP iframe 不代表客户端会加载它:内容未加密,Android App 仍阻止 HTTPS 页面中的 HTTP 混合内容,浏览器也可能拦截。请优先配置 HTTPS;通过 HTTPS 管理入口打开远程面板时会显示相同风险提醒。
307
+
308
+ ### 可选模块与高级配置
309
+
310
+ 0.6.0 新增[移动页面模块设置](docs/CLIENT_MODULES.md),可分别设置电脑默认和当前设备选择,不卸载电脑插件。保存后下次手动打开生效,不自动刷新会话;已保存选择失效时提供恢复入口。
311
+
312
+ <details>
313
+ <summary>通过 profile 配置 excludedClientModules</summary>
314
+
239
315
  若某个不需要的客户端插件显著增加手机首次加载流量,高级用户可在当前 profile 的 `mobile-access` 插件配置中设置 `excludedClientModules`,填入启动清单中的**准确包名**,重启 DSH 后生效。它只改变经网关提供的专用移动页面;电脑端和 `?frontend=stock` 页面不变。例如,若当前启动清单同时包含以下两个包,下面的配置会移除文档预览及依赖它的“在应用中打开”功能:
240
316
 
241
317
  ```yaml
@@ -246,6 +322,10 @@ excludedClientModules:
246
322
 
247
323
  这是按安装环境选择的高级配置,没有默认排除列表。插件会拒绝不存在的包、启动必需模块,以及仍被保留模块通过 `inject` 或 `external` 引用的包;配置不匹配当前 DSH 或社区插件时,移动页面返回 `409 excluded_client_modules_invalid` 和冲突详情,电脑端也会提示,移除或调整配置即可恢复。模块内容与依赖会随 DSH 版本变化,不能把其他人的节省比例当作自己的预期。
248
324
 
325
+ 设置优先级为本设备选择 → 电脑默认 → 插件配置。详细生效与恢复规则见[模块指南](docs/CLIENT_MODULES.md)。
326
+
327
+ </details>
328
+
249
329
  <table>
250
330
  <tr>
251
331
  <td align="center" valign="top" width="50%">
@@ -268,7 +348,9 @@ excludedClientModules:
268
348
 
269
349
  Android App 只是 Kotlin WebView 薄壳,不内置另一份网页;手机浏览器访问的是同一页面。需要排查兼容性时,可在浏览器地址后追加 `?frontend=stock`,临时回到旧的桌面页面适配模式。
270
350
 
271
- 专用移动页面会在 DSH 的第一个启动脚本之前,同步加载经网关鉴权的同源 `/mobile-access/compat.js`,为缺少 `Iterator` / Iterator helpers 的 WebView 提供随插件打包的 core-js 兼容实现,避免启动时出现 `Iterator is not defined`。兼容层按能力检测保留或修正原生 helper,不依赖 CDN,也不会放宽 CSP;不修改 Android APK、桌面页面或 `?frontend=stock` 页面。此修复并不承诺支持所有旧内核,前端仍以 ES2022 为构建目标;若还有其他兼容错误,请优先更新 Android System WebView / Chrome。
351
+ 0.5.5 的专用移动页面会在 DSH 的第一个启动脚本之前,同步加载经网关鉴权的同源 `/mobile-access/compat.js`,为缺少 `Iterator` / Iterator helpers 的 WebView 提供随插件打包的 core-js 兼容实现,避免启动时出现 `Iterator is not defined`。兼容层按能力检测保留或修正原生 helper,不依赖 CDN,也不会放宽 CSP;桌面页面与 `?frontend=stock` 页面不会加载这个网关脚本。
352
+
353
+ 0.6.0 在同一个启动前脚本中增加 `Promise.withResolvers` 与 `AbortSignal.any` 的能力检测和兼容实现。Android App 还会在支持 document-start 注入的 WebView 中,为配对的精确 Origin 提前补齐 `Promise.withResolvers`。这只覆盖已识别的缺失 API,不是所有旧内核的支持承诺;前端仍以 ES2022 为构建目标,尚未在报告中的华为 WebView 114 真机上验收。出现其他兼容错误时,若设备允许更新,请优先更新 Android System WebView / Chrome;若系统组件不可更新,请保留完整错误、内核版本与进入方式供排查。
272
354
 
273
355
  > **社区客户端(非官方)**:[微信小程序客户端](https://github.com/StrawberryAO/dsh-mobile-minapp)
274
356
  > 原生微信小程序实现,复用「移动访问」的配对与 Remote 流协议(需 dsh-mobile ≥ 0.3.8)。
@@ -334,6 +416,13 @@ macOS 上局域网、cloudflared、自建 FRP 与自有反向代理可用;Funn
334
416
  | DSH Mobile 插件 | 验证支持的 DeepSeek Harness 版本 |
335
417
  | ----------------------------------------- | -------------------------------------------------------------- |
336
418
  | `0.5.5` | `0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2`、`0.2.0-rc.1`、`0.2.0-rc.2`(npm 打包安装、隔离配对、移动页面启动及 WebSocket 工作区读取) |
419
+ | `0.6.0` | `0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2`、`0.2.0-rc.1`、`0.2.0-rc.2`(打包安装、配对、移动页面启动与 WebSocket 工作区读取);另验证移动输入栏、模块设置及官方 Desktop 正常重启后的组件加载 |
420
+
421
+ <details>
422
+ <summary>历史版本验证记录</summary>
423
+
424
+ | DSH Mobile 插件 | 验证支持的 DeepSeek Harness 版本 |
425
+ | --- | --- |
337
426
  | `0.5.4` | `0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2`、`0.2.0-rc.1`、`0.2.0-rc.2`(npm 打包安装、隔离配对、移动页面启动及 WebSocket 工作区读取) |
338
427
  | `0.5.3` | `0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2`、`0.2.0-rc.1`、`0.2.0-rc.2`(源码契约、npm 打包安装、隔离配对、移动页面启动及 WebSocket 工作区读取) |
339
428
  | `0.5.2` | `0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2`、`0.2.0-rc.1`(延续既有验证);`0.2.0-rc.2`(源码契约、隔离配对、移动页面启动和 WebSocket 工作区读取) |
@@ -354,9 +443,11 @@ macOS 上局域网、cloudflared、自建 FRP 与自有反向代理可用;Funn
354
443
  | `0.3.0`-`0.3.3` | `0.1.2-alpha.1` |
355
444
  | `0.1.4`、`0.2.x` | `0.1.1-rc.2` |
356
445
 
446
+ </details>
447
+
357
448
  现有 App(0.3.3 及更新)无需重新配对;cpolar 用户应使用 0.3.15 或更新 App,较早版本可能在免费线路的慢速首次加载完成前超时;更早的 App 还使用不同的状态栏策略。App 0.4.0 才支持多设备列表、启动行为设置和电脑端撤销状态同步;旧版 App 仍可连接已保存的单台设备。App 0.1.3 及更早版本需卸载重装并重新配对。
358
449
 
359
- 0.5.5 的原生配对与续期协议保持兼容,旧 App 的正常连接可继续使用;建议同步升级至 0.5.5(build 75),以获得旋转保持页面、模型菜单返回和撤销状态修复,同时保留 0.5.4 的认证 Cookie 修正。
450
+ 0.6.1(build 77)的原生功能与配对、续期协议延续 0.6.0,已有配对无需重建;旧 App 的正常连接可继续使用。建议同步升级插件与 App,以保留 0.6.0 的图标选择、扫码改进、设备排序、切换快捷方式及此前修复。
360
451
 
361
452
  GitHub Release 的正式 APK 使用固定签名,可从同一签名的旧正式版原位升级并保留配对。自行构建的 Debug APK 若使用不同签名,不能直接覆盖安装正式版;切换前请准备重新配对。
362
453
 
@@ -390,18 +481,6 @@ npm ci
390
481
  npm run verify
391
482
  ```
392
483
 
393
- 修改提问卡片或移动布局时,完成构建后运行 `npx playwright install chromium --only-shell`、`npm run smoke:question-fixes` 和 `npm run smoke:native-layout`,检查实际客户端产物的滚动、键盘与布局行为。
394
-
395
- 真实启动冒烟另用临时 DSH Home、随机回环端口和 Chromium 配对,不访问现有用户配置,也不发送模型请求。CI 分别测试 DSH `0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2`、`0.2.0-rc.1` 与 `0.2.0-rc.2`;本机可把 DSH `0.2.0-rc.2` 装在独立目录,避免替换插件的开发依赖:
396
-
397
- ```powershell
398
- $dshMobileTestRuntime = Join-Path $env:TEMP 'dsh-mobile-test-runtime'
399
- npm install --prefix $dshMobileTestRuntime --no-save --package-lock=false @deepseek-ai/dsh@0.2.0-rc.2
400
- $env:DSH_BOOT_SMOKE_BIN = Join-Path $dshMobileTestRuntime 'node_modules/@deepseek-ai/dsh/lib/bin.js'
401
- npx playwright install chromium --only-shell
402
- npm run smoke:dsh-boot
403
- ```
404
-
405
- Android 构建见 [App 文档](https://github.com/saya-ch/dsh-mobile/blob/main/apps/mobile/README.zh-CN.md)。
484
+ 文档快速检查使用 `npm run check:docs`。按改动选择的浏览器测试、隔离 DSH 打包验收、截图和正式发布流程见 [CONTRIBUTING.md](CONTRIBUTING.md);Android 构建见 [App 手册](apps/mobile/README.zh-CN.md#构建)。不要用真实用户配置或正式 App 数据替代测试资料。
406
485
 
407
486
  Apache-2.0,详见 [LICENSE](LICENSE)。
package/SECURITY.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Security policy
2
2
 
3
- `dsh-mobile` exposes a control surface that can run tools on the host computer. Treat every paired device as security-sensitive.
3
+ `dsh-mobile` lets paired devices operate DSH and run tools with the computer user's authority. Pairing is not a read-only sandbox: trust each device and revoke it promptly if lost. Publish only the protected Mobile entry, never the ordinary DSH listener or a plaintext backend.
4
4
 
5
5
  ## Supported versions
6
6
 
@@ -14,34 +14,45 @@ The maintainer will acknowledge a complete report within seven days. Publication
14
14
 
15
15
  ## Deployment requirements
16
16
 
17
+ ### Listeners and desktop administration
18
+
17
19
  - Keep the ordinary DSH Web listener on loopback.
18
20
  - Expose only the plugin-owned HTTPS listener to the LAN.
19
21
  - The desktop admin API may accept an RFC1918 or IPv4 link-local Host only when the TCP peer is loopback, for a trusted local reverse proxy or headless host. Do not publicly forward `/api/mobile-access`; Host and loopback checks are not a replacement for proxy access control. Mutating admin requests require a same-origin `Origin` header, except official DSH Desktop requests: its forwarder strips `Origin` and `Sec-Fetch-Site` and supplies a signed Host browser-session Cookie, so the plugin client sends an exact non-simple marker that survives forwarding. The plugin requires both that marker and Connection's successful browser-session verification before accepting an Origin-less write.
22
+ ### Discovery, pairing and TLS
23
+
20
24
  - DNS-SD/mDNS, periodic UDP announcements, active UDP query replies, and HTTPS discovery return only the device name, public HTTPS origin, port, protocol version, and stable non-secret installation identifier. Discovery never returns the CA, a pairing key, a device token, Cookies, credentials, or private configuration.
21
25
  - For LAN pairing, only after a user selects a device and enters the fingerprint-bound pairing key may Android fetch the public CA from that exact HTTPS origin. The bootstrap GET sends no key or credential. The app retains the CA in its encrypted credential record and never adds it to Android's system trust settings. Native requests use a private trust store; WebView accepts only the otherwise-untrusted leaf signed by that CA, for the exact origin and validity period. Every other TLS error is cancelled.
22
26
  - Public remote origins provided by Funnel, cpolar, cloudflared, the FRP public-CA entry, or an operator-managed reverse proxy use platform-trusted HTTPS. Android stores no private CA for those credentials and cancels every TLS error. It sends a persisted device token only to the exact Origin that previously received it; a changed remote Origin requires a current one-time pairing token before the app replaces the saved credential. A QR-provided installation identifier alone never authorizes credential renewal. Browser clients likewise require a certificate trusted by their platform.
23
27
  - The 0.4.6 existing-frps self-signed entry instead forwards raw TCP to the computer-side HTTPS gateway. Its QR link and bare App key carry a versioned `dsh2` CA-required marker, which Android 0.4.5 and earlier reject. During explicit remote pairing, the 0.4.6 app retrieves the gateway's public CA without sending a credential, checks its validity and SHA-256 fingerprint against the pairing key, and pins it to the exact Origin; HTTP 404, a missing, changed, or untrusted CA cannot downgrade this flow to platform trust. Re-pairing an existing pinned identity retains that CA requirement. The existing exact-Origin device-token restriction still applies: a CA fingerprint alone cannot renew a device credential. The CA is not installed in Android's system trust store. The 0.4.5 app lacks this remote CA-pinning path, and ordinary phone browsers will not trust the self-signed certificate.
24
28
  - Keep pairing closed except during a short local onboarding action.
25
29
  - Revoke a lost device immediately and rotate the device registry if credential theft is suspected.
30
+ ### Remote components, diagnostics and cleanup
31
+
26
32
  - Do not expose the LAN gateway through router port forwarding. Funnel, cpolar, cloudflared, and FRP reach a separate loopback gateway through their selected remote path. The own reverse-proxy provider instead exposes a plugin-owned private HTTP origin restricted by its bind address and allowed source CIDRs; the operator's proxy must provide public HTTPS. Funnel, cpolar, cloudflared, Caddy, or the own proxy terminates public TLS in their respective modes; the 0.4.6 self-signed FRP entry terminates TLS on the computer. DSH pairing, device authentication, CSRF checks, and session revocation remain enforced by the plugin gateway.
27
33
  - The Funnel node stores its Tailscale login state under `$DSH_HOME/mobile-access/remote/tailscale/`. The plugin does not request or store a Tailscale password, Auth Key, or OAuth secret.
28
34
  - The proxy-assisted remote diagnostic probes only the public `/mobile-access/health` path. It tries direct HTTPS first and, only after direct failure, may use an HTTP CONNECT proxy named by the DSH process environment unless `NO_PROXY` excludes the target. The probe sends no device token or session Cookie; configured proxy credentials are sent to that proxy for CONNECT, not placed in the diagnostic report. A proxy-only success is a warning to test from the phone, not proof that the phone or the provider route is available. This diagnostic does not change the remote provider's traffic path.
29
- - cpolar is downloaded only after confirmation from a pinned official artifact whose size and SHA-256 are verified. Its Authtoken is stored in a private, self-update-disabled configuration under `$DSH_HOME/mobile-access/`, never returned by the admin API or written to logs. Cleanup removes the managed executable, configuration, logs, and independent remote device registry.
35
+ - cpolar is downloaded only after confirmation from a pinned official artifact whose size and SHA-256 are verified. Its Authtoken is stored in a private, self-update-disabled configuration under `$DSH_HOME/mobile-access/`, never returned by the admin API or written to logs. Component removal deletes its managed executable, Authtoken configuration, logs and staging files, but retains LAN and shared remote pairing records.
30
36
  - Self-hosted FRP accepts a VPS address, frps control port, operator-generated high-entropy shared token, and standard-port public HTTPS origin. The 0.4.6 self-signed entry additionally selects a non-reserved public TCP port on a globally routable IPv4 address. The plugin validates token length and format; the operator remains responsible for its entropy. Managed deployment creates one HTTP vhost and Caddy; existing-frps attachment changes no VPS files or services and allows either a restricted HTTP vhost with a public CA or one TCP passthrough to a computer-side HTTPS gateway. Arbitrary FRP configuration, general TCP/UDP forwarding, FRP plugins, PATH entries, and startup tasks are not supported.
31
37
  - For an HTTP-vhost entry, the generated managed-frps template binds the plaintext listener to `127.0.0.1` and uses Caddy for public HTTPS. An existing frps must have its actual `vhostHTTPPort` entered explicitly and its plaintext vhost kept off the public network; the plugin probes the configured port before starting `frpc`. frps `proxyBindAddr` governs proxy listeners globally: exposing a TCP passthrough on the same instance must not accidentally expose another plaintext vhost. A separate frps instance may be required. For self-signed ingress, the computer-side CA lasts five years and the leaf lasts 397 days; the leaf is renewed under the same CA, but an expired or replaced CA is not silently trusted and requires fresh pairing. A local readiness probe does not establish phone reachability; verify the public entry from an independent network.
32
38
  - The FRP token stays in a private file under `$DSH_HOME/mobile-access/`, is never returned by status, diagnostic, or attachment-plan responses, and is never logged. Copying a server template or an attach-mode `frpc.toml` after re-entering its token puts that token on the system clipboard; clear it after use and protect the VPS configuration. Local FRP cleanup removes the managed `frpc`, token, runtime configuration, ingress certificate material, staging files, and logs. Server-side cleanup scripts apply only to plugin-managed deployment and delete solely DSH Mobile-owned files, services, and tagged firewall rules; attach mode never deletes the operator's existing frps or Caddy files.
33
39
  - Automatic VPS deployment and one-click VPS cleanup first read the server's SSH host keys and require the operator to confirm every `SHA256:` fingerprint against the VPS console before any authenticated connection. All SSH/SCP channels pin `StrictHostKeyChecking=yes` to a temporary known_hosts file built from the confirmed keys; unknown, rotated, or extra keys abort the operation instead of being silently accepted. SSH credentials themselves are key-or-agent only, never passwords; the private key path is convenience state in the current browser profile, and key material is never uploaded or logged.
34
- - Disabling remote access stops the selected provider process without affecting LAN access. Resetting remote access also removes provider state and the independent remote device registry.
40
+ - Disabling remote access stops the selected provider without affecting LAN or pairing records. Resetting remote access also clears the shared remote-device registry, so all remote devices must pair again. A Funnel reset additionally removes the plugin's private Tailscale node login state and requires authorization again. Other providers retain settings and components until their separate removal actions are used.
41
+
42
+ ### Device authority, Android and extensions
43
+
35
44
  - Treat every paired device as a fully trusted operator. The authenticated proxy forwards ordinary DSH and community-plugin routes using GET, HEAD, POST, PUT, PATCH, or DELETE; writes require the gateway's exact Origin, valid Session, and Session-bound CSRF token. Upstream requests use DSH's loopback Host and the gateway's own DSH authentication, so a plugin cannot distinguish a paired phone from the computer user merely by checking that Host. Routes may read configuration, save settings, or run tools with the desktop user's authority; pairing is not a read-only sandbox. The separate `/api/mobile-access` admin API remains inaccessible through the mobile listener.
36
45
  - Android stores the paired-device list as an encrypted record. Removing a local row erases its local credential but does not revoke the phone at the computer; use the computer-side device controls to revoke access. A computer-side revocation invalidates the token even if the App retains the row's name for re-pairing. Network timeouts are not evidence of revocation.
37
46
  - Android grants WebView microphone access only for an audio-only request from the current paired HTTPS Origin, after Android's `RECORD_AUDIO` permission is granted. Other WebView resources and Origins remain denied. Client plugins within the authenticated page share that Origin and can invoke page APIs; install only trusted plugins before granting microphone access.
38
47
  - Treat `mobile.js` as application code with the paired page's same-origin authority. Restrict write access to trusted host-side DSH sessions and review generated API calls or browser-permission use.
39
48
  - Treat every extension `host.mjs` as a local program with the desktop user's Node.js privileges. It is never sandboxed and is not editable through the mobile gateway; only place code there that you trust.
40
49
  - Extension Actions and Routes receive filtered request data, a device identifier, and an abort signal. They cannot set proxy security headers or access the gateway's cookies, device tokens, CSRF tokens, or internal request headers.
41
- - Proxied GUI responses allow HTTP iframe sources for compatibility with community surfaces. HTTP content is unauthenticated and unencrypted; use HTTPS for sensitive work, and do not treat the compatibility allowance as a transport-security guarantee.
50
+ ### Framed content and browser policy
51
+
52
+ - Proxied GUI responses allow HTTP iframe sources for compatibility with community surfaces. HTTP content is unauthenticated and unencrypted; Android still blocks mixed HTTP content inside HTTPS pages, and browsers may block it too. Prefer HTTPS and do not treat a gateway framing allowance as a transport-security guarantee.
42
53
 
43
54
  ## Known limitation
44
55
 
45
56
  The current DSH HTML boot process contains inline JavaScript, revives Schemastery callbacks with `new Function`, and applies some styles dynamically. To keep the stock Web UI runnable, the gateway's Content Security Policy currently includes `script-src 'self' 'unsafe-inline' 'unsafe-eval'` and `style-src 'self' 'unsafe-inline'`. The remaining directives still restrict origins, connections, frames, objects, workers, images, and form targets, but this policy does not eliminate script-injection risk. Removing these allowances requires upstream DSH support for nonces, stable hashes, external boot resources, and a non-evaluating schema representation.
46
57
 
47
- This repository never accepts private keys, npm tokens, pairing values, device credentials, Cookies, or captured settings in issues, logs, fixtures, or example configuration.
58
+ Do not include live private keys, npm tokens, pairing values, device credentials, Cookies or captured private settings in issues, logs, committed fixtures or examples. Authentication tests use isolated temporary credentials, not real user state.
@@ -1,5 +1,11 @@
1
1
  # Third-party notices
2
2
 
3
+ ## Brand artwork
4
+
5
+ The Android launcher whale-mark variants were contributed by [@abworks-dev in PR #175](https://github.com/saya-ch/dsh-mobile/pull/175), which describes rendering the DeepSeek whale SVG path. They identify the upstream brand; DSH Mobile remains a community-maintained client, not an official DeepSeek Android application or an assertion of endorsement. This repository's code license does not assert ownership of third-party names or marks. The default character artwork and repository hero remain the project's existing brand assets.
6
+
7
+ ## Runtime components
8
+
3
9
  DSH Mobile includes platform-specific Windows and Linux Funnel helpers built from the source in `native/funnel-host`. They use [Tailscale tsnet](https://pkg.go.dev/tailscale.com/tsnet) `v1.102.3`, distributed under the BSD 3-Clause license. Their exact statically linked Go module versions and complete license and notice texts are generated from the bundled executables into `FUNNEL_THIRD_PARTY_LICENSES.txt`, which is included in every npm package.
4
10
 
5
11
  The optional cpolar component is not included in the npm package or Android App. When a user explicitly chooses cpolar installation, DSH Mobile downloads the pinned official archive shown in the UI, verifies its size and SHA-256 digest, and stores it only under the user's DSH Mobile data directory. cpolar remains subject to its [terms of service](https://www.cpolar.com/tos).
@@ -8,13 +14,15 @@ The optional [FRP](https://github.com/fatedier/frp) client is not included in th
8
14
 
9
15
  The optional [cloudflared](https://github.com/cloudflare/cloudflared) client is not included in the npm package or Android App. When a user explicitly chooses cloudflared installation, DSH Mobile downloads the pinned official release asset for the supported host platform (version recorded in `src/cloudflared-component.ts`), verifies its exact size and SHA-256 digest, and stores it only under the user's DSH Mobile data directory. The client is started with automatic updates disabled. Quick tunnels do not require account credentials; named tunnels use an operator-supplied connector token stored in the plugin's private directory. cloudflared is distributed under the Apache License 2.0; use of Cloudflare's quick tunnels remains subject to Cloudflare's [website terms](https://www.cloudflare.com/website-terms/).
10
16
 
17
+ The managed Caddy component is not included in the npm package or Android App. Version 0.6.0 has no published trusted pinned component, so installation remains unavailable. The reviewed build uses [Caddy v2.11.6](https://github.com/caddyserver/caddy/blob/v2.11.6/LICENSE), under Apache License 2.0, and [caddy-dns/tencentcloud v0.4.3](https://github.com/caddy-dns/tencentcloud/blob/v0.4.3/LICENSE), under the MIT License. Maintainer build artifacts record embedded dependency versions and licenses; they are not an automatically published or installed binary. Existing user-managed proxies are separate from this optional component.
18
+
11
19
  The npm package declares direct runtime dependencies on [Schemastery](https://github.com/shigma/schemastery) `^3.18.1`, [bonjour-service](https://github.com/onlxltd/bonjour-service) `1.4.4`, [qrcode](https://github.com/soldair/node-qrcode) `^1.5.4`, [selfsigned](https://github.com/jfromaniello/selfsigned) `^5.5.0`, and [ws](https://github.com/websockets/ws) `8.22.0`. These packages are distributed under the MIT License; the release checkout's exact transitive versions appear in the attached CycloneDX npm SBOM.
12
20
 
13
21
  The Android App runtime contains [Kotlin standard library](https://github.com/JetBrains/kotlin) `2.1.0`, [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) `1.6.4`, [JetBrains annotations](https://github.com/JetBrains/java-annotations) `13.0`, [AndroidX Core](https://developer.android.com/jetpack/androidx/releases/core) `1.15.0`, [AndroidX WebKit](https://developer.android.com/jetpack/androidx/releases/webkit) `1.17.1`, their AndroidX transitive components, [Guava listenablefuture](https://github.com/google/guava) `1.0`, and [ZXing Core](https://github.com/zxing/zxing) `3.5.4`. These runtime libraries are distributed under the Apache License 2.0; the exact resolved tree is attached to each release as `dsh-mobile-android-v<version>-dependencies.txt`.
14
22
 
15
23
  ## core-js browser compatibility bundle
16
24
 
17
- The standalone `lib/mobile-compat.js` bundles selected Iterator modules from [core-js](https://github.com/zloirock/core-js) `^3.50.0` (exact version recorded in `package-lock.json`). It is built into the npm package; browsers do not download a polyfill from a CDN. core-js is distributed under the following MIT License:
25
+ The standalone `lib/mobile-compat.js` bundles selected Iterator and `Promise.withResolvers` modules from [core-js](https://github.com/zloirock/core-js) `^3.50.0` (exact version recorded in `package-lock.json`). It is built into the npm package; browsers do not download a polyfill from a CDN. core-js is distributed under the following MIT License:
18
26
 
19
27
  ```text
20
28
  Copyright (c) 2013–2025 Denis Pushkarev (zloirock.ru)
@@ -2,7 +2,7 @@
2
2
 
3
3
  [中文指南](ATTACH_EXISTING_FRPS.md)
4
4
 
5
- > **Requires plugin 0.4.6**; the self-signed entry also requires the 0.4.6 Android app. Android apps 0.3.3–0.4.5 can use the public-CA entry but not the self-signed entry.
5
+ > The plugin requires **0.4.6 or later**; the self-signed entry also requires **Android app 0.4.6 or later**. Apps 0.3.3–0.4.5 can use the public-CA entry, not self-signed ingress. Do not downgrade a newer app.
6
6
 
7
7
  Use this path when you already run frps on a public VPS. The plugin does not install, edit, or restart that frps and does not automatically change your Caddyfile; public-CA mode requires **you** to add a Caddy snippet and import. Inspect the server's listeners before choosing an entry mode. The panel's **Copy attachment plan** action only produces a masked local `frpc.toml` preview and VPS instructions; it neither connects to the VPS nor writes configuration. To copy a token-bearing local config, re-enter the token and explicitly click its copy button; a saved token is never returned to the page.
8
8
 
@@ -11,7 +11,7 @@ Use this path when you already run frps on a public VPS. The plugin does not ins
11
11
  | Entry | Public route | VPS requirements | Client trust |
12
12
  | --- | --- | --- | --- |
13
13
  | **Public CA** (`public-ip-cert`, default) | HTTPS 443 → Caddy → loopback HTTP vhost → FRP | Enter the real `vhostHTTPPort`; keep that plaintext listener on loopback. A domain uses Caddy-managed certificates; public IPv4 needs a short-lived Let's Encrypt IP certificate and operator-managed renewal. | Phone browsers and Android app 0.3.3+ use the platform trust store and still require DSH pairing. |
14
- | **Self-signed passthrough** (`self-signed`) | HTTPS on `publicPort` (default 33080) → frps raw TCP proxy → computer-side HTTPS gateway | Use a globally routable public IPv4 address. The frps TCP listener must be publicly reachable; no new Caddy site, public certificate, or HTTP vhost is needed for this entry. | Requires the 0.4.6 Android app with remote CA pinning. Older apps cannot pair; ordinary phone browsers do not trust the self-signed certificate. |
14
+ | **Self-signed passthrough** (`self-signed`) | HTTPS on `publicPort` (default 33080) → frps raw TCP proxy → computer-side HTTPS gateway | Use a globally routable public IPv4 address. The frps TCP listener must be publicly reachable; no new Caddy site, public certificate or HTTP vhost is needed. | Requires Android app 0.4.6 or later with remote CA pinning. Older apps cannot pair; ordinary phone browsers do not trust this certificate. |
15
15
 
16
16
  frps `proxyBindAddr` is global to proxy listeners, not a per-entry switch. Binding it to `127.0.0.1` protects the HTTP vhost but also makes a TCP `remotePort` unreachable from the public network. Binding it publicly for the TCP entry may expose plaintext vhosts already running on that frps. Inspect every listener and firewall rule; use a separate frps instance or the public-CA mode when the requirements conflict. Neither mode permits arbitrary FRP proxy configuration or editing the operator's server from the plugin.
17
17
 
@@ -20,17 +20,18 @@ The [official frps configuration reference](https://gofrp.org/en/docs/reference/
20
20
  ## Public-CA mode
21
21
 
22
22
  1. In **Self-hosted FRP**, choose **Attach to my existing frps** and **Public CA certificate**. Enter the VPS address, frps control port, high-entropy shared token, public domain or IPv4 HTTPS origin, and the frps instance's actual `vhostHTTPPort`. Do not assume the managed deployment's default of 7080.
23
- 2. Copy the attachment plan. Confirm the plaintext vhost is loopback-only, then add the generated Caddy snippet and `import` manually. Caddy manages domain certificates. For public IPv4, the current plan pins Certbot 5.8.0 and uses `--standalone --preferred-profile shortlived --ip-address` for a [roughly six-day Let's Encrypt IP certificate](https://letsencrypt.org/2026/03/11/shorter-certs-certbot). Standalone issuance briefly stops Caddy to free port 80 and may interrupt other sites on the VPS; schedule a maintenance window, choose a domain, or use the 0.4.6 app's self-signed TCP mode if that interruption is unacceptable. The attach path only checks an existing `certbot.timer`; it does not install renewal or a certificate-copy/reload hook. Arrange renewal, installation into Caddy, and reload before expiry yourself. Copying the plan does not change the VPS.
23
+ 2. Copy the attachment plan. Confirm the plaintext vhost is loopback-only, then add the generated Caddy snippet and `import` manually. Caddy manages domain certificates. For public IPv4, the current plan pins Certbot 5.8.0 and uses `--standalone --preferred-profile shortlived --ip-address` for a [roughly six-day Let's Encrypt IP certificate](https://letsencrypt.org/2026/03/11/shorter-certs-certbot). Standalone issuance briefly stops Caddy to free port 80 and may interrupt other sites on the VPS; schedule a maintenance window, choose a domain, or use the self-signed TCP mode in app 0.4.6 or later if that interruption is unacceptable. The attach path only checks an existing `certbot.timer`; it does not install renewal or a certificate-copy/reload hook. Arrange renewal, installation into Caddy, and reload before expiry yourself. Copying the plan does not change the VPS.
24
24
  3. Install the pinned official `frpc` on the computer, save and verify the connection, and test `/mobile-access/discovery` from an independent external network with normal certificate validation. Confirm HTTP 200 and this computer's installation identifier before pairing through the app's **Remote access** flow or a phone browser. A local self-check alone does not verify public reachability.
25
25
 
26
26
  ## Self-signed passthrough mode
27
27
 
28
- The computer-side gateway terminates TLS with a self-signed CA valid for five years and a public-IPv4 leaf valid for 397 days. The leaf can be reissued under the same CA. An expired or replaced CA is not silently accepted: inspect the certificate status in the panel, confirm a new fingerprint, and pair the phone again. The QR link, copied link, and bare App key carry a `dsh2` marker requiring CA pinning; older apps reject that format. The CA is private to the 0.4.6 app's connection record, not installed in Android's system trust store.
28
+ The computer-side gateway terminates TLS with a five-year CA and 397-day public-IPv4 leaf. The leaf can renew under the same CA; an expired or replaced CA requires a new fingerprint check and pairing. QR links and App keys carry the CA-required `dsh2` marker, rejected by older apps. Supported apps keep this CA in their private connection records, never Android's system trust store.
29
29
 
30
30
  1. In **Self-hosted FRP**, enter the VPS address, frps control port, token, and `https://YOUR_PUBLIC_IPV4`. Choose **Attach to my existing frps**, **Self-signed passthrough**, and the public TCP port (default 33080; not 3080, 3443, or 3444).
31
- 2. Copy the attachment plan and review its masked local `frpc.toml` and VPS checklist. This is read-only. After you install `frpc` and select **Save and verify connection**, the plugin writes the real config under its private `remote/frp/config/` directory (Unix mode `0600`; restricted ACL on Windows). If you re-entered the token and explicitly copied a token-bearing config, clear the clipboard promptly.
31
+ 2. Copy and review the masked `frpc.toml` and VPS checklist. This is read-only: finish the server checks before selecting **Save and verify connection**.
32
32
  3. On the VPS, allow the chosen TCP entry port in its host firewall and cloud security group; the existing frps control port must already be reachable from the computer. Confirm frps is running and the TCP proxy listens on an address reachable from the public network. The plugin does not change the server's frps configuration or firewall.
33
- 4. From another network, check the entry with `curl -k -sS -o /dev/null -w '%{http_code}\n' https://YOUR_PUBLIC_IPV4:33080/mobile-access/discovery`. Replace the host and port first. HTTP 200 only shows transport reachability: `-k` bypasses curl's certificate verification and does **not** verify the gateway identity. Pair from the 0.4.6 Android app's **Remote access** flow, which fetches the public CA without a device credential, checks its fingerprint against the `dsh2` key, and pins it to that exact Origin. A 404 CA response cannot fall back to platform trust in this flow. A changed Origin or CA requires a new pairing.
33
+ 4. Install official frpc and select **Save and verify connection**. The plugin now writes the config in private `remote/frp/config/` (Unix `0600`, restricted Windows ACL), then runs `frpc verify` before startup. Clear the clipboard if you explicitly copied a token-bearing config. A public TCP proxy port normally appears only after frpc registers; check its actual listener then.
34
+ 5. From another network, check `curl -k -sS -o /dev/null -w '%{http_code}\n' https://YOUR_PUBLIC_IPV4:33080/mobile-access/discovery`, replacing the host/port. HTTP 200 proves reachability only: `-k` does not verify identity. Pair through **Remote access** in app 0.4.6 or later; it fetches the CA without device credentials, checks the `dsh2` fingerprint and pins the exact Origin. CA HTTP 404 cannot fall back to platform trust. An Origin or CA change requires new pairing.
34
35
 
35
36
  ## Troubleshooting and cleanup
36
37
 
@@ -40,8 +41,8 @@ The computer-side gateway terminates TLS with a self-signed CA valid for five ye
40
41
  | `frp_vhost_publicly_reachable` | The public-CA entry's plaintext vhost is reachable from outside. Restrict it without exposing another listener; a separate frps instance may be safer than changing global `proxyBindAddr`. |
41
42
  | `frp_attach_cert_unknown` | The self-signed certificate is absent or unreadable. Inspect the panel's certificate status and the private `remote/ingress/` files; do not bypass fingerprint validation. |
42
43
  | `frp_ingress_ca_expired` | The pinned CA expired. Reconfigure the self-signed entry and pair the phone again after checking the new fingerprint. |
43
- | Browser reports an untrusted certificate | Expected for the self-signed entry. Use the 0.4.6 Android app and explicit pairing; use public-CA mode for a browser. |
44
- | `frp_proxy_name_in_use` | frps keeps proxy names unique across **all** its clients, and this installation's own name (`dsh-mobile-<first 12 characters of the installation identity>`) is already registered — usually another DSH computer, or this computer's previous session before the frps heartbeat expires it. Disable self-hosted FRP there, or remove that proxy on the frps. frpc keeps running without publishing the entry port, so before this code the failure only ever appeared as a start timeout. |
44
+ | Browser reports an untrusted certificate | Expected for self-signed ingress. Use app 0.4.6 or later and explicit pairing, or public-CA mode for a browser. |
45
+ | `frp_proxy_name_in_use` | frps keeps proxy names unique across **all** its clients, and this installation's own name (`dsh-mobile-<first 12 characters of the installation identity>`) is already registered — usually another DSH computer, or this computer's previous session before the frps heartbeat expires it. Disable self-hosted FRP there, or remove that proxy on the frps. frpc keeps running without publishing the entry port, so check proxy registration rather than relying on process liveness. |
45
46
  | `frp_remote_port_in_use` | Another proxy on the same frps already owns the chosen entry port. Ports are first-come, first-served per server, so give this computer a different `publicPort` and allow it in the firewall. |
46
47
  | Public entry unreachable | Check the chosen port, cloud and host firewalls, frps listener address, running `frpc`, and the external-network probe. A computer-side probe is not proof that a phone can connect. |
47
48
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English guide](ATTACH_EXISTING_FRPS.en.md)
4
4
 
5
- > **需要 0.4.6 插件**;自签档还必须使用 0.4.6 Android App。Android App 0.3.3–0.4.5 可使用公开证书档,但不支持自签档。
5
+ > 插件最低版本为 **0.4.6**;自签档还需要 **0.4.6 或更高版本 Android App**。App 0.3.3–0.4.5 可使用公开证书档,不支持自签档,无需为此降级新版 App。
6
6
 
7
7
  本文面向**已经在一台公网 VPS 上跑着 frps**的用户。插件不会安装、修改或重启你的 frps,也不会自动改动 Caddyfile;若选择公开 CA 档,你需要自行新增 Caddy 片段和 `import`。本机侧不改动 DSH 自身配置。接入前须核对既有 frps 的监听;若它不满足所选入口档的要求,插件不会代你修改。
8
8
 
@@ -25,13 +25,13 @@
25
25
  - 公网入口:`443`,由 **VPS 上的 Caddy** 终止 TLS,它再反代到 `127.0.0.1:<vhostHTTPPort>`。
26
26
  - 证书:域名入口由 Caddy 自动申请与续期;公网 IPv4 入口使用 Certbot 5.8.0 的 `--standalone --preferred-profile shortlived --ip-address` 签发 [Let's Encrypt 的约 6 天 IP 证书](https://letsencrypt.org/2026/03/11/shorter-certs-certbot)。此 attach 流程**不会**安装续期定时器或部署 hook;你须在到期前续签、将新证书安装到 Caddy 并重载它。
27
27
  - 受信任的公开证书可供手机浏览器与支持自定义远程 Origin 的 Android App(0.3.3 及更新)使用;仍须完成设备配对。
28
- - VPS 侧要做:手动新增 Caddy 片段(`/etc/caddy/dsh-mobile-dsh.caddy`)和 Caddyfile 顶部一行 `import`;公网 IPv4 还需安排证书初次签发与持续续期。当前清单的 Certbot `--standalone` 命令会短暂停止 Caddy 以占用 80 端口,可能中断同机原有网站;请安排维护窗口。若不能接受中断,优先选域名公开证书档,或使用 0.4.6 App 的自签 TCP 档。
28
+ - VPS 侧要做:手动新增 Caddy 片段(`/etc/caddy/dsh-mobile-dsh.caddy`)和 Caddyfile 顶部一行 `import`;公网 IPv4 还需安排证书初次签发与持续续期。当前清单的 Certbot `--standalone` 命令会短暂停止 Caddy 以占用 80 端口,可能中断同机原有网站;请安排维护窗口。若不能接受中断,优先选域名公开证书档,或使用 0.4.6 或更新 App 的自签 TCP 档。
29
29
 
30
30
  ### B. 自签穿透档(`self-signed`)——**不需要公开证书**
31
31
 
32
32
  - 公网入口:`publicPort`(缺省 **33080**,可改;不得为 3080/3443/3444)。frps 只做**纯 TCP 透传,不解密**。
33
33
  - TLS 由**运行 DSH 的电脑上的网关终止**,当前仅使用公网 IPv4 入口。插件自签 CA 有效期 5 年,公网 IPv4 叶证书有效期 397 天;叶证书可在保留同一 CA 时续签,CA 到期会阻止连接,不能静默换成新 CA。应留意面板证书状态,CA 到期或更换后须重新配对。
34
- - 此档需要 **0.4.6 Android App**:二维码、链接和 App 配对密钥以 `dsh2` 标记必须固定 CA;App 会从网关的 `GET /mobile-access/ca.cer` 读取 CA,并核对密钥中的指纹,若接口返回 404 也不会降级为系统信任。旧版 App 不支持此档,仍可使用 A 档。
34
+ - 此档需要 **0.4.6 或更新的 Android App**:二维码、链接和 App 配对密钥以 `dsh2` 标记必须固定 CA;App 从 `GET /mobile-access/ca.cer` 读取 CA 并核对密钥指纹,404 不会降级为系统信任。更早的 App 仍可使用 A 档。
35
35
  - 手机**浏览器**访问会提示证书不受信任(这是自签的必然结果),请用 App 扫码配对使用。
36
36
  - VPS 不需要为此入口配置 Caddy、certbot 或新的 HTTP vhost;但既有 frps 的代理监听地址必须允许该 TCP 入口公网访问,且不得因此暴露其他明文服务。
37
37
 
@@ -43,30 +43,33 @@
43
43
  2. 复制接入清单,先核对 frps 的明文 vhost 只在回环监听,再按清单手动添加 Caddy 片段与 `import`。域名证书由 Caddy 管理;公网 IPv4 请在维护窗口运行清单固定的 Certbot 5.8.0 `--standalone` 命令,它会短暂停止 Caddy。此 attach 流程只查询现有 `certbot.timer`,不代你配置续期、证书复制或重载 hook;须自行保持这些步骤运行。插件不会代你修改 VPS。
44
44
  3. 在电脑端按需安装官方 `frpc`,保存并验证连接。在独立外部网络打开公开地址的 `/mobile-access/discovery`,确认可信证书、HTTP 200 与当前电脑的安装标识;然后从 App 的「远程访问」或手机浏览器完成配对。仅在电脑端的自检通过不代表外网可达。
45
45
 
46
- ## 操作步骤(自签穿透档,共 7 步)
46
+ ## 自签穿透档:按准备顺序连接
47
47
 
48
- ### 本机(3 步)
48
+ ### 1. 本机填写并预览
49
49
 
50
50
  1. 面板「自建 FRP → 步骤 1」填写:VPS 地址、frps 端口(你 frps 的 `bindPort`)、Token、公网入口
51
51
  `https://<公网IP>`;**置备方式 = 接入我已有的 frps**;**入口证书档 = 自签穿透**;公网入口端口 = `33080`(或你选的端口)。
52
- 2. 「复制接入清单」,核对其中脱敏的 `frpc.toml` 和 VPS 待办;复制本身不会保存配置。完成下一步并点击「保存并验证连接」后,插件才将实际配置写入私有目录 `…/remote/frp/config/frpc.toml`(Unix 为 `0600`,Windows 使用受限 ACL)。若重新输入 Token 并明确复制含 Token 的本机配置,请及时清除剪贴板。
53
- 3. 装官方 frpc(面板「步骤 3」)→ 点「保存并验证连接」。启动前插件会自检 `frpc verify -c <配置>`。
52
+ 2. 点击「复制接入清单」,核对脱敏的 `frpc.toml` 和 VPS 待办。复制不连接服务器、不保存配置;先完成下面的 VPS 检查,再连接。
54
53
 
55
- ### VPS(2 步)
54
+ ### 2. VPS 检查与端口
56
55
 
57
- 4. 在既有 frps 控制端口已经可从电脑连接的前提下,为**新增 TCP 入口**放行公网端口:
56
+ 3. 确认既有 frps 控制端口可从电脑连接,代理监听地址允许所选 TCP 入口,且不会暴露其他明文服务。为新增入口放行公网端口:
58
57
  ```sh
59
58
  ufw allow 33080/tcp
60
59
  ufw status | grep 33080
61
60
  ```
62
61
  若用 firewalld 或云厂商安全组,放行同样的 **TCP 33080**。
63
- 5. 确认既有 frps 已就绪,并核对 TCP 入口实际监听地址(插件不会改它的配置;若不是 systemd 服务,使用你自己的管理方式检查进程):
62
+ 4. 检查 frps 的服务与监听(非 systemd 服务使用自己的管理方式):
64
63
  ```sh
65
64
  systemctl is-active frps || true
66
65
  ss -lnt | grep -E ':(<你的 bindPort>|33080)\b' || true
67
66
  ```
68
67
 
69
- ### 手机(2 步)
68
+ 控制端口应已监听;33080 这类代理入口通常在 frpc 成功注册后才出现,连接完成后再核对它的实际地址。
69
+
70
+ ### 3. 本机连接,再让手机配对
71
+
72
+ 5. 按需安装官方 frpc,点击「保存并验证连接」。配置此时才写入私有 `remote/frp/config/frpc.toml`(Unix `0600`,Windows 受限 ACL),启动前运行 `frpc verify`。若明确复制过含 Token 的配置,请及时清理剪贴板。
70
73
 
71
74
  6. 在电脑面板「远程访问」生成**远程配对二维码 / 配对链接**(含 CA 指纹、一次性 Token 和必须固定 CA 的 `dsh2` 标记)。
72
75
  7. 用 Android App 扫码(或粘贴链接)完成配对,即可远程进入 DSH。
@@ -80,7 +83,7 @@ curl -k -sS -o /dev/null -w '%{http_code}\n' https://YOUR_PUBLIC_IPV4:33080/mobi
80
83
  ```
81
84
 
82
85
  面板「步骤 2 → 运行自检」还会显示:入口证书剩余天数、CA 指纹、frps 控制端口可达性、公网入口可达性。
83
- **本机探测 ≠ 公网验证**:请在外部再跑一次上面的 curl(手机流量即可),然后让 0.4.6 App 校验配对密钥里的 CA 指纹并实际建立会话。
86
+ 本机探测不能代替公网验证。请从外部网络再检查入口,然后让支持远程 CA 固定的 App 核对配对密钥的指纹并建立会话。
84
87
 
85
88
  ## 常见问题
86
89
 
@@ -92,7 +95,7 @@ curl -k -sS -o /dev/null -w '%{http_code}\n' https://YOUR_PUBLIC_IPV4:33080/mobi
92
95
  | `frp_ingress_ca_expired` | 自签 CA 已过期,插件不会静默替换受信任 CA;重新生成 CA 后,手机须核对新指纹并重新配对。 |
93
96
  | 手机浏览器提示证书不受信任 | 自签档的正常现象;请在 App 内配对使用 |
94
97
  | 自检显示「公网入口不可达」 | 端口未放行、frpc 未启动,或 frps 未把该 `remotePort` 转发出来 |
95
- | `frp_proxy_name_in_use` | frps 的代理名在**整台服务器**上唯一。本机安装名(`dsh-mobile-<安装标识前 12 位>`)已被占用:多半是另一台 DSH 电脑,或本机上一次连接尚未等到 frps 心跳超时。到那台机器上关闭自建 FRP,或在 frps 上移除该代理。frpc 此时仍在运行、只是入口端口没被发布,所以在此之前只会表现为「公网端点未能就绪」。 |
98
+ | `frp_proxy_name_in_use` | frps 的代理名在**整台服务器**上唯一。本机安装名(`dsh-mobile-<安装标识前 12 位>`)已被占用:多半是另一台 DSH 电脑,或本机上一次连接尚未等到 frps 心跳超时。到那台机器上关闭自建 FRP,或在 frps 上移除该代理。frpc 此时仍在运行、只是入口端口没被发布,应检查代理是否注册成功,不能仅凭进程存在判断就绪。 |
96
99
  | `frp_remote_port_in_use` | 同一台 frps 上该入口端口已被别的代理占用(端口先到先得)。给这台电脑换一个 `publicPort`,并在防火墙/安全组放行。 |
97
100
  | App 报「连接到另一台 DSH」 | 公网入口指向了别的 DSH:检查 `remotePort` 与 frpc 是否在本机运行 |
98
101