dsh-mobile 0.4.3 → 0.4.5
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/CHANGELOG.md +15 -0
- package/CONTRIBUTORS.md +55 -0
- package/README.en.md +16 -9
- package/README.md +16 -10
- package/SECURITY.md +2 -2
- package/lib/client.js +104 -7
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +16 -1
- package/lib/index.mjs +127 -37
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-compat.js +1 -1
- package/lib/mobile-compat.js.map +1 -1
- package/package.json +22 -21
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
Notable changes to DSH Mobile are recorded here. GitHub Releases remain the source for downloadable packages and complete generated commit notes.
|
|
4
4
|
|
|
5
|
+
## 0.4.5 - 2026-09-23
|
|
6
|
+
|
|
7
|
+
- Support the document-relative plugin URLs introduced in DSH 0.1.7 without weakening the same-origin `/plugins/` check. This fixes mobile boot batches returning `502 upstream_unavailable`; local and remote gateways were verified against DSH 0.1.7-alpha.2 (thanks @azri57806-design for [#97](https://github.com/saya-ch/dsh-mobile/issues/97)).
|
|
8
|
+
- Keep failed revisioned plugin scripts and hashed assets on `no-store` instead of caching their 404 responses as immutable for a year (thanks @abworks-dev for [PR #98](https://github.com/saya-ch/dsh-mobile/pull/98)).
|
|
9
|
+
- Guard WebSocket upgrade sockets before asynchronous authentication so disconnects during reconnect cannot emit an unhandled socket error (follow-up to [#95](https://github.com/saya-ch/dsh-mobile/issues/95)).
|
|
10
|
+
- Give Android connection restoration an exit after a short wait: direct-start Retry stays bound to that paired computer, while manual and legacy flows offer the Device list without retrying a different computer. Existing credentials remain intact (thanks @sznyhgm for [#96](https://github.com/saya-ch/dsh-mobile/issues/96)).
|
|
11
|
+
- Restore the task-notification permission entry in the Android App's DSH General settings without bringing back the hidden native toolbar; the action uses the exact-origin, main-frame native bridge and has Chinese, English, and Italian copy.
|
|
12
|
+
- Align development dependencies and optional peer ranges with DSH 0.1.7-alpha.2, and use the producer-owned message source for `/mobile` guidance.
|
|
13
|
+
|
|
14
|
+
## 0.4.4 - 2026-09-20
|
|
15
|
+
|
|
16
|
+
- Keep DSH running if mDNS response callbacks, the multicast-dns emitter, or the already-bound UDP discovery socket reports a network error. LAN discovery records the degraded channel and logs the error code while the authenticated HTTP and WebSocket gateway remains available (thanks @KMGTPEZY for [#95](https://github.com/saya-ch/dsh-mobile/issues/95)).
|
|
17
|
+
- Accept pinned cpolar and cloudflared component downloads when a proxy omits `Content-Length`, and stop reading as soon as the response exceeds the pinned size.
|
|
18
|
+
- Credit community PR authors, including work adapted after a PR was closed, and every historical issue author by contribution type in [CONTRIBUTORS.md](CONTRIBUTORS.md), separately from GitHub's commit-based Contributors panel.
|
|
19
|
+
|
|
5
20
|
## 0.4.3 - 2026-09-19
|
|
6
21
|
|
|
7
22
|
- Support Linux for the Funnel, cpolar and cloudflared remote providers, including x64 and arm64 Funnel host binaries.
|
package/CONTRIBUTORS.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# 贡献者 / Contributors
|
|
2
|
+
|
|
3
|
+
GitHub 的 [Contributors 图表](https://github.com/saya-ch/dsh-mobile/graphs/contributors) 按进入默认分支的提交自动统计,无法手动加入仅提交 issue 或 PR 提案的成员。本页按实际参与方式感谢社区成员;仓库维护者与自动依赖更新仍可在 GitHub 历史记录中查看。
|
|
4
|
+
|
|
5
|
+
GitHub's Contributors graph counts commits on the default branch; issue reports and PR proposals cannot be manually added to it. This page credits community members by how they contributed, through 2026-09-23. The repository maintainer and automated dependency updates remain visible in GitHub's history.
|
|
6
|
+
|
|
7
|
+
## 已合并 PR / Merged pull requests
|
|
8
|
+
|
|
9
|
+
| 社区成员 / Community member | 已合并 PR / Merged PRs |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| [@1624318455](https://github.com/1624318455) | [#93](https://github.com/saya-ch/dsh-mobile/pull/93) |
|
|
12
|
+
| [@abworks-dev](https://github.com/abworks-dev) | [#91](https://github.com/saya-ch/dsh-mobile/pull/91), [#98](https://github.com/saya-ch/dsh-mobile/pull/98) |
|
|
13
|
+
| [@idoall](https://github.com/idoall) | [#61](https://github.com/saya-ch/dsh-mobile/pull/61), [#66](https://github.com/saya-ch/dsh-mobile/pull/66), [#67](https://github.com/saya-ch/dsh-mobile/pull/67), [#77](https://github.com/saya-ch/dsh-mobile/pull/77) |
|
|
14
|
+
| [@JackRushante](https://github.com/JackRushante) | [#16](https://github.com/saya-ch/dsh-mobile/pull/16) |
|
|
15
|
+
| [@longisland-icetea](https://github.com/longisland-icetea) | [#59](https://github.com/saya-ch/dsh-mobile/pull/59), [#60](https://github.com/saya-ch/dsh-mobile/pull/60), [#63](https://github.com/saya-ch/dsh-mobile/pull/63) |
|
|
16
|
+
| [@qzyqmzn](https://github.com/qzyqmzn) | [#38](https://github.com/saya-ch/dsh-mobile/pull/38), [#75](https://github.com/saya-ch/dsh-mobile/pull/75) |
|
|
17
|
+
| [@StrawberryAO](https://github.com/StrawberryAO) | [#34](https://github.com/saya-ch/dsh-mobile/pull/34), [#40](https://github.com/saya-ch/dsh-mobile/pull/40) |
|
|
18
|
+
| [@xingleiwu](https://github.com/xingleiwu) | [#79](https://github.com/saya-ch/dsh-mobile/pull/79), [#84](https://github.com/saya-ch/dsh-mobile/pull/84), [#85](https://github.com/saya-ch/dsh-mobile/pull/85), [#86](https://github.com/saya-ch/dsh-mobile/pull/86) |
|
|
19
|
+
|
|
20
|
+
## 已吸收但未直接合并的 PR / Incorporated without a direct merge
|
|
21
|
+
|
|
22
|
+
[@BlueandwhiteXD](https://github.com/BlueandwhiteXD) 的 [#15](https://github.com/saya-ch/dsh-mobile/pull/15) 提出了并实现了 Android 键盘遮挡修复。由于发布分支已有重叠的状态栏改动,该方案改写后进入主线提交 [`09ef315`](https://github.com/saya-ch/dsh-mobile/commit/09ef31590219f2a436bd801214703338c215b00d),并在 [`04e6a0d`](https://github.com/saya-ch/dsh-mobile/commit/04e6a0da030353d4f0897419d0f692dc538a752d) 以 `Co-authored-by` 致谢。PR 的 Closed 状态不应掩盖这项已采用的代码贡献。
|
|
23
|
+
|
|
24
|
+
[@qzyqmzn](https://github.com/qzyqmzn) 的 [#32](https://github.com/saya-ch/dsh-mobile/pull/32) 和 [#37](https://github.com/saya-ch/dsh-mobile/pull/37) 被后续已合并的 [#38](https://github.com/saya-ch/dsh-mobile/pull/38) 取代;其正式贡献已列在上方。
|
|
25
|
+
|
|
26
|
+
[@BlueandwhiteXD](https://github.com/BlueandwhiteXD)'s [#15](https://github.com/saya-ch/dsh-mobile/pull/15) supplied the Android keyboard-overlap fix. It was adapted into [`09ef315`](https://github.com/saya-ch/dsh-mobile/commit/09ef31590219f2a436bd801214703338c215b00d) alongside overlapping status-bar work and credited with `Co-authored-by` in [`04e6a0d`](https://github.com/saya-ch/dsh-mobile/commit/04e6a0da030353d4f0897419d0f692dc538a752d). Its Closed PR status does not mean the code contribution was rejected. @qzyqmzn's [#32](https://github.com/saya-ch/dsh-mobile/pull/32) and [#37](https://github.com/saya-ch/dsh-mobile/pull/37) were superseded by merged [#38](https://github.com/saya-ch/dsh-mobile/pull/38).
|
|
27
|
+
|
|
28
|
+
## 问题反馈与建议 / Issue reports and ideas
|
|
29
|
+
|
|
30
|
+
以下名单包括提出问题、复现缺陷和建议功能的成员;出现在本表不表示提交过代码。 / These members opened repository issues; listing them does not imply code authorship.
|
|
31
|
+
|
|
32
|
+
| 反馈贡献者 / Issue reporter | 提交的问题 / Issues |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| [@1624318455](https://github.com/1624318455) | [#92](https://github.com/saya-ch/dsh-mobile/issues/92) |
|
|
35
|
+
| [@azri57806-design](https://github.com/azri57806-design) | [#97](https://github.com/saya-ch/dsh-mobile/issues/97) |
|
|
36
|
+
| [@CESAEREE](https://github.com/CESAEREE) | [#1](https://github.com/saya-ch/dsh-mobile/issues/1) |
|
|
37
|
+
| [@CharlesLueng](https://github.com/CharlesLueng) | [#68](https://github.com/saya-ch/dsh-mobile/issues/68) |
|
|
38
|
+
| [@chmzs](https://github.com/chmzs) | [#73](https://github.com/saya-ch/dsh-mobile/issues/73) |
|
|
39
|
+
| [@idoall](https://github.com/idoall) | [#42](https://github.com/saya-ch/dsh-mobile/issues/42), [#45](https://github.com/saya-ch/dsh-mobile/issues/45), [#46](https://github.com/saya-ch/dsh-mobile/issues/46), [#47](https://github.com/saya-ch/dsh-mobile/issues/47), [#62](https://github.com/saya-ch/dsh-mobile/issues/62), [#64](https://github.com/saya-ch/dsh-mobile/issues/64) |
|
|
40
|
+
| [@IvyC-zz](https://github.com/IvyC-zz) | [#58](https://github.com/saya-ch/dsh-mobile/issues/58), [#78](https://github.com/saya-ch/dsh-mobile/issues/78) |
|
|
41
|
+
| [@jillkayat](https://github.com/jillkayat) | [#10](https://github.com/saya-ch/dsh-mobile/issues/10) |
|
|
42
|
+
| [@KMGTPEZY](https://github.com/KMGTPEZY) | [#95](https://github.com/saya-ch/dsh-mobile/issues/95) |
|
|
43
|
+
| [@LWping](https://github.com/LWping) | [#94](https://github.com/saya-ch/dsh-mobile/issues/94) |
|
|
44
|
+
| [@oliverwan97](https://github.com/oliverwan97) | [#26](https://github.com/saya-ch/dsh-mobile/issues/26) |
|
|
45
|
+
| [@rgshendong](https://github.com/rgshendong) | [#87](https://github.com/saya-ch/dsh-mobile/issues/87) |
|
|
46
|
+
| [@RonaldinhoL](https://github.com/RonaldinhoL) | [#89](https://github.com/saya-ch/dsh-mobile/issues/89) |
|
|
47
|
+
| [@shengyvself](https://github.com/shengyvself) | [#13](https://github.com/saya-ch/dsh-mobile/issues/13), [#14](https://github.com/saya-ch/dsh-mobile/issues/14) |
|
|
48
|
+
| [@sznyhgm](https://github.com/sznyhgm) | [#96](https://github.com/saya-ch/dsh-mobile/issues/96) |
|
|
49
|
+
| [@sortjiajun](https://github.com/sortjiajun) | [#90](https://github.com/saya-ch/dsh-mobile/issues/90) |
|
|
50
|
+
| [@StrawberryAO](https://github.com/StrawberryAO) | [#39](https://github.com/saya-ch/dsh-mobile/issues/39) |
|
|
51
|
+
| [@Surpass567](https://github.com/Surpass567) | [#80](https://github.com/saya-ch/dsh-mobile/issues/80), [#81](https://github.com/saya-ch/dsh-mobile/issues/81) |
|
|
52
|
+
| [@taki-ohh](https://github.com/taki-ohh) | [#19](https://github.com/saya-ch/dsh-mobile/issues/19) |
|
|
53
|
+
| [@Xingcheng-wang](https://github.com/Xingcheng-wang) | [#76](https://github.com/saya-ch/dsh-mobile/issues/76) |
|
|
54
|
+
| [@Yurzi](https://github.com/Yurzi) | [#17](https://github.com/saya-ch/dsh-mobile/issues/17) |
|
|
55
|
+
| [@Ztyss](https://github.com/Ztyss) | [#22](https://github.com/saya-ch/dsh-mobile/issues/22), [#23](https://github.com/saya-ch/dsh-mobile/issues/23) |
|
package/README.en.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://www.npmjs.com/package/dsh-mobile"><img src="https://img.shields.io/npm/v/dsh-mobile?label=npm&color=CB3837" alt="npm version"></a>
|
|
11
|
-
<a href="https://www.npmjs.com/package/dsh-mobile"><img src="https://img.shields.io/npm/
|
|
11
|
+
<a href="https://www.npmjs.com/package/dsh-mobile"><img src="https://img.shields.io/npm/dt/dsh-mobile?label=downloads&color=2563EB" alt="total npm downloads"></a>
|
|
12
12
|
<a href="https://github.com/saya-ch/dsh-mobile/actions/workflows/ci.yml"><img src="https://github.com/saya-ch/dsh-mobile/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
13
13
|
<a href="https://github.com/saya-ch/dsh-mobile/releases"><img src="https://img.shields.io/badge/Android-10%2B-3DDC84?logo=android&logoColor=white" alt="Android 10+"></a>
|
|
14
14
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-0F172A" alt="Apache-2.0"></a>
|
|
@@ -24,20 +24,21 @@
|
|
|
24
24
|
<a href="#third-party-plugin-compatibility">Third-party plugins</a> ·
|
|
25
25
|
<a href="#security">Security</a> ·
|
|
26
26
|
<a href="#compatibility">Compatibility</a> ·
|
|
27
|
+
<a href="#contributors">Contributors</a> ·
|
|
27
28
|
<a href="CHANGELOG.md">Changelog</a> ·
|
|
28
29
|
<a href="README.md">简体中文</a>
|
|
29
30
|
</p>
|
|
30
31
|
|
|
31
32
|
> DSH Mobile is a DeepSeek Harness community plugin; the native app supports Android only.
|
|
32
33
|
>
|
|
33
|
-
> **0.4.
|
|
34
|
+
> **0.4.5 update**: support DSH `0.1.7-alpha.2` mobile boot URLs, fix reconnect failures and long-lived caching of failed resources, and add an exit from App connection restoration. [Details](CHANGELOG.md).
|
|
34
35
|
>
|
|
35
|
-
> **Upgrade reminder**: the 0.4.
|
|
36
|
+
> **Upgrade reminder**: update both the plugin and Android app to 0.4.5 when practical. Existing pairings are retained and older apps can still connect, but Retry and Device list during connection restoration require the new app. [Compatibility notes](#compatibility).
|
|
36
37
|
|
|
37
38
|
<p align="center">
|
|
38
|
-
<a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.
|
|
39
|
-
<a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.
|
|
40
|
-
<sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.4.
|
|
39
|
+
<a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.5/dsh-mobile-android-v0.4.5.apk"><img src="assets/brand/app-icon-rounded.svg" alt="DSH Mobile Android app icon" width="72" height="72"></a><br>
|
|
40
|
+
<a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.5/dsh-mobile-android-v0.4.5.apk"><strong>Download Android app 0.4.5</strong></a><br>
|
|
41
|
+
<sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.4.5">Release notes and checksums</a></sub>
|
|
41
42
|
</p>
|
|
42
43
|
|
|
43
44
|
DSH Mobile is a DeepSeek Harness plugin that lets a mobile browser or the Android app connect over a protected LAN or an optional Tailscale Funnel, cpolar, cloudflared, self-hosted FRP, or own reverse-proxy remote path. Local and remote access keep the same sessions, Workspaces, messages, and tools while using separate switches and paired-device stores without modifying DeepSeek Harness source.
|
|
@@ -54,7 +55,7 @@ It also lets you customize the phone from a DSH conversation: `/mobile <what you
|
|
|
54
55
|
- **Every remote path covered**: Tailscale, cpolar, cloudflared quick/named tunnels, self-hosted FRP, or your own reverse proxy.
|
|
55
56
|
- **Pairing and multi-device**: pair once via QR code, link, or key; Wi-Fi, hotspot, or IP changes normally recover automatically; the app shows all paired computers together in one device list (LAN and every remote), each with live reachability — switch, re-pair, or delete in one tap.
|
|
56
57
|
- **One-click diagnostics and approval**: check versions, gateway, network interface, firewall, and the remote path with a redacted report; approve blocked third-party plugin connections per exact path.
|
|
57
|
-
- **Task system notifications**: completion and pending-input alerts via Android system notifications, enabled from the app
|
|
58
|
+
- **Task system notifications**: completion and pending-input alerts via Android system notifications, enabled from DSH General settings inside the app, with redacted lock-screen text.
|
|
58
59
|
- **Defense in depth**: dedicated HTTPS with pinned certificates, Keystore-backed credentials, device tokens sent only to their exact Origin, third-party WebSockets blocked by default.
|
|
59
60
|
|
|
60
61
|
A paired device is fully trusted and can operate the DSH on the computer. Use this only on a trusted home or office LAN, or a trusted VPN.
|
|
@@ -215,7 +216,7 @@ Compatibility and WebSocket rules:
|
|
|
215
216
|
|
|
216
217
|
Proxied pages allow HTTP frames for compatibility with some community plugins; those pages are unencrypted and can be altered, and browsers may still block them as mixed content. Use HTTPS for sensitive work. The same warning appears at the top of the remote panel when it is opened over HTTPS.
|
|
217
218
|
|
|
218
|
-
-
|
|
219
|
+
- Version 0.4.5 has been contract-checked and boot-tested against DSH `0.1.7-alpha.2` (renderer-v2). The DSH page must expose the standard session, `main`/`panelInfo`, and `rightbar` slots; the community plugin must register its panel or sidebar content through DSH's standard entry points.
|
|
219
220
|
- The gateway allows first-party DSH WebSocket paths by default, including `/sidebar/ws/terminal`. Other paths used by community sidebar plugins are blocked by default and appear in Diagnostics; the `/sidebar/ws/agent-opens` and `/sidebar/ws/agent-terminals` paths in the image are examples that must be reviewed for the actual plugin.
|
|
220
221
|
- In **Connection diagnostics → Third-party WebSocket paths**, select **Allow** only for an exact path you have verified. Query strings and fuzzy prefixes are rejected; **Allow all** is not recommended. Approved paths can be removed at any time, and the same policy applies to LAN and remote connections.
|
|
221
222
|
- Approval only lets that path pass through the authenticated, same-origin DSH Mobile gateway. It does not open arbitrary TCP/UDP ports or bypass device pairing. If a community plugin still fails, check the path recorded by Diagnostics and approve one path at a time.
|
|
@@ -285,7 +286,7 @@ See [SECURITY.md](SECURITY.md).
|
|
|
285
286
|
|
|
286
287
|
## Compatibility
|
|
287
288
|
|
|
288
|
-
The table
|
|
289
|
+
The table records tested DSH and plugin version combinations; it does not imply automatic compatibility with other versions. Since 0.3.6 the plugin has not rejected DSH solely by version number. If mobile access breaks after upgrading DSH, check this table and update the plugin first. History lives in [CHANGELOG.md](CHANGELOG.md).
|
|
289
290
|
|
|
290
291
|
### OS support matrix
|
|
291
292
|
|
|
@@ -302,6 +303,8 @@ On macOS, local network, self-hosted FRP, and the own reverse proxy work; the th
|
|
|
302
303
|
|
|
303
304
|
| DSH Mobile plugin | Verified DeepSeek Harness version |
|
|
304
305
|
| --- | --- |
|
|
306
|
+
| `0.4.5` | `0.1.7-alpha.2` (source contract check, local and remote gateway integration) |
|
|
307
|
+
| `0.4.4` | `0.1.6-alpha.2` (local source and renderer-v2 contract check) |
|
|
305
308
|
| `0.4.3` | `0.1.6-alpha.2` (local source and renderer-v2 contract check) |
|
|
306
309
|
| `0.4.2` | `0.1.6-alpha.1` (local source and renderer-v2 contract check) |
|
|
307
310
|
| `0.4.1` | `0.1.6-alpha.1` (local source and renderer-v2 contract check) |
|
|
@@ -330,6 +333,10 @@ dsh plugin --profile web remove dsh-mobile
|
|
|
330
333
|
|
|
331
334
|
Source users replace `dsh` with `pnpm dsh`.
|
|
332
335
|
|
|
336
|
+
## Contributors
|
|
337
|
+
|
|
338
|
+
Thanks to everyone who submitted PRs, reproduced issues, or proposed improvements. [The contributors list](CONTRIBUTORS.md) credits merged PRs, PR work later incorporated without a direct merge, and issue reports separately. GitHub's sidebar Contributors panel is generated from commits on the default branch and cannot be manually extended with issue-only contributors.
|
|
339
|
+
|
|
333
340
|
## Development
|
|
334
341
|
|
|
335
342
|
```powershell
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://www.npmjs.com/package/dsh-mobile"><img src="https://img.shields.io/npm/v/dsh-mobile?label=npm&color=CB3837" alt="npm 版本"></a>
|
|
11
|
-
<a href="https://www.npmjs.com/package/dsh-mobile"><img src="https://img.shields.io/npm/
|
|
11
|
+
<a href="https://www.npmjs.com/package/dsh-mobile"><img src="https://img.shields.io/npm/dt/dsh-mobile?label=downloads&color=2563EB" alt="npm 总下载量"></a>
|
|
12
12
|
<a href="https://github.com/saya-ch/dsh-mobile/actions/workflows/ci.yml"><img src="https://github.com/saya-ch/dsh-mobile/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
13
13
|
<a href="https://github.com/saya-ch/dsh-mobile/releases"><img src="https://img.shields.io/badge/Android-10%2B-3DDC84?logo=android&logoColor=white" alt="Android 10+"></a>
|
|
14
14
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-0F172A" alt="Apache-2.0"></a>
|
|
@@ -24,20 +24,21 @@
|
|
|
24
24
|
<a href="#第三方插件适配">第三方插件适配</a> ·
|
|
25
25
|
<a href="#安全">安全</a> ·
|
|
26
26
|
<a href="#兼容性">兼容性</a> ·
|
|
27
|
+
<a href="#贡献者">贡献者</a> ·
|
|
27
28
|
<a href="CHANGELOG.md">更新记录</a> ·
|
|
28
29
|
<a href="README.en.md">English</a>
|
|
29
30
|
</p>
|
|
30
31
|
|
|
31
32
|
> DSH Mobile 是 DeepSeek Harness 社区插件,原生 App 仅支持 Android。
|
|
32
33
|
>
|
|
33
|
-
> **0.4.
|
|
34
|
+
> **0.4.5 更新**:适配 DSH `0.1.7-alpha.2` 的移动端启动脚本地址,修复重连异常与失败资源的长期缓存,并为 App 的连接恢复增加退出入口。[详细记录](CHANGELOG.md)。
|
|
34
35
|
>
|
|
35
|
-
>
|
|
36
|
+
> **升级提醒**:建议插件与 Android App 同步更新至 0.4.5;已有配对会保留,旧版 App 仍可连接,但需要新版 App 才能使用连接恢复时的重试和设备列表入口。[兼容说明](#兼容性)。
|
|
36
37
|
|
|
37
38
|
<p align="center">
|
|
38
|
-
<a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.
|
|
39
|
-
<a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.
|
|
40
|
-
<sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.4.
|
|
39
|
+
<a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.5/dsh-mobile-android-v0.4.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.4.5/dsh-mobile-android-v0.4.5.apk"><strong>下载 Android App 0.4.5</strong></a><br>
|
|
41
|
+
<sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.4.5">版本说明与校验文件</a></sub>
|
|
41
42
|
</p>
|
|
42
43
|
|
|
43
44
|
DSH Mobile 是一个 DeepSeek Harness 插件,让手机浏览器或 Android App 通过局域网,或可选的 Tailscale Funnel、cpolar、cloudflared、自建 FRP 或自有反向代理远程通道连接电脑,继续使用同一份会话、工作区、消息和工具。局域网与远程访问分别启停、分别管理设备,且都不修改 DeepSeek Harness 源码。
|
|
@@ -54,7 +55,7 @@ DSH Mobile 是一个 DeepSeek Harness 插件,让手机浏览器或 Android App
|
|
|
54
55
|
- **多种远程通道**:Tailscale、cpolar、cloudflared 快速/命名隧道、自建 FRP、自有反向代理,按网络任选。
|
|
55
56
|
- **配对与多设备**:扫码、链接或密钥配对一次;切换 Wi-Fi、热点或 IP 后通常自动恢复;App 在一个设备列表中同时显示多台已配对电脑(局域网与全部远程),每台实时显示可达状态,一键切换、重新配对或删除。
|
|
56
57
|
- **一键诊断与放行**:检查版本、网关、网卡、防火墙和远程通道,生成脱敏报告;被拦截的第三方插件连接按确切路径一键放行。
|
|
57
|
-
- **任务系统通知**:任务完成与待输入以 Android
|
|
58
|
+
- **任务系统通知**:任务完成与待输入以 Android 系统通知提醒,可在 App 内的 DSH 常规设置中开启,锁屏文本脱敏。
|
|
58
59
|
- **纵深安全**:独立 HTTPS 与证书固定,凭据存 Keystore,设备令牌只发往精确 Origin,第三方 WS 默认拦截。
|
|
59
60
|
|
|
60
61
|
配对设备被视为完全信任,可以操作电脑上的 DSH;建议只在可信的家庭、办公局域网或可信 VPN 中使用。
|
|
@@ -215,7 +216,7 @@ Android App 用一个“已配对设备”列表同时显示多台电脑:局
|
|
|
215
216
|
|
|
216
217
|
代理页面为兼容部分社区插件允许嵌入 HTTP 页面;这类内容未加密,可能被篡改,浏览器也可能因混合内容策略拦截。处理敏感内容时请使用 HTTPS。通过 HTTPS 管理入口打开远程面板时,页面顶部会显示相同提醒。
|
|
217
218
|
|
|
218
|
-
-
|
|
219
|
+
- 0.4.5 已按 DSH `0.1.7-alpha.2` 做 renderer-v2 契约检查与启动包联调。DSH 页面需要提供标准的会话、`main`/`panelInfo` 和 `rightbar` 插槽;插件自身需要通过 DSH 的标准面板或侧边栏入口注册内容。
|
|
219
220
|
- 网关默认只允许 DSH 内置的第一方 WebSocket 路径。社区侧边栏插件使用的其他路径默认拦截,通常会在诊断页显示为待处理项目;截图中的`/sidebar/ws/agent-opens` 和`/sidebar/ws/agent-terminals` 就属于这类需要按实际插件确认的路径。
|
|
220
221
|
- 在 **连接诊断 → 第三方 WebSocket 路径** 中,只对确认过的精确路径点击 **允许**。系统不接受带查询字符串或模糊前缀的路径;不建议使用“全部允许”。已允许的路径可以随时移除,局域网和远程连接使用同一套规则。
|
|
221
222
|
- 放行只代表该路径可以通过已认证、同源的 DSH Mobile 网关,不会开放任意 TCP/UDP 端口,也不会绕过设备配对。若社区插件仍然连接失败,先看诊断页的实际拦截路径,再按一条路径放行。
|
|
@@ -285,7 +286,7 @@ flowchart LR
|
|
|
285
286
|
|
|
286
287
|
## 兼容性
|
|
287
288
|
|
|
288
|
-
|
|
289
|
+
下表记录已验证的 DSH 与插件版本组合,不表示其他版本自动兼容。0.3.6 起插件不会仅凭版本号拒绝启动;升级 DSH 后若遇到移动端异常,请先核对兼容表并更新插件。历史记录见 [CHANGELOG.md](CHANGELOG.md)。
|
|
289
290
|
|
|
290
291
|
### 系统支持矩阵
|
|
291
292
|
|
|
@@ -300,9 +301,10 @@ flowchart LR
|
|
|
300
301
|
|
|
301
302
|
macOS 上局域网、自建 FRP 与自有反向代理可用;三个托管组件暂未提供 macOS 包。诊断页的防火墙检查目前仅覆盖 Windows,其他系统显示“不适用”。
|
|
302
303
|
|
|
303
|
-
|
|
304
304
|
| DSH Mobile 插件 | 验证支持的 DeepSeek Harness 版本 |
|
|
305
305
|
| ----------------------------------------- | -------------------------------------------------------------- |
|
|
306
|
+
| `0.4.5` | `0.1.7-alpha.2`(源码契约检查、本机局域网及远程网关联调) |
|
|
307
|
+
| `0.4.4` | `0.1.6-alpha.2`(本机源码与 renderer-v2 契约检查) |
|
|
306
308
|
| `0.4.3` | `0.1.6-alpha.2`(本机源码与 renderer-v2 契约检查) |
|
|
307
309
|
| `0.4.2` | `0.1.6-alpha.1`(本机源码与 renderer-v2 契约检查) |
|
|
308
310
|
| `0.4.1` | `0.1.6-alpha.1`(本机源码与 renderer-v2 契约检查) |
|
|
@@ -331,6 +333,10 @@ dsh plugin --profile web remove dsh-mobile
|
|
|
331
333
|
|
|
332
334
|
源码模式把上述 `dsh` 换成 `pnpm dsh`。
|
|
333
335
|
|
|
336
|
+
## 贡献者
|
|
337
|
+
|
|
338
|
+
感谢提交 PR、复现问题和提出建议的社区成员。[贡献者名单](CONTRIBUTORS.md)按已合并 PR、后来吸收的 PR 工作和 issue 反馈分别致谢;GitHub 右侧的 Contributors 区域由进入默认分支的提交自动生成,不能手动加入仅反馈问题的成员。
|
|
339
|
+
|
|
334
340
|
## 开发
|
|
335
341
|
|
|
336
342
|
```powershell
|
package/SECURITY.md
CHANGED
|
@@ -19,10 +19,10 @@ The maintainer will acknowledge a complete report within seven days. Publication
|
|
|
19
19
|
- 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 must carry a same-origin `Origin` header.
|
|
20
20
|
- 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
21
|
- 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
|
-
- Public remote origins provided by Funnel, cpolar,
|
|
22
|
+
- Public remote origins provided by Funnel, cpolar, cloudflared, self-hosted FRP, 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
23
|
- Keep pairing closed except during a short local onboarding action.
|
|
24
24
|
- Revoke a lost device immediately and rotate the device registry if credential theft is suspected.
|
|
25
|
-
- Do not expose the LAN gateway through router port forwarding.
|
|
25
|
+
- Do not expose the LAN gateway through router port forwarding. Funnel, cpolar, cloudflared, and self-hosted FRP use a separate loopback gateway behind the 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. The outer provider or proxy terminates public TLS, while DSH pairing, device authentication, CSRF checks, and session revocation remain enforced by the plugin gateway.
|
|
26
26
|
- 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.
|
|
27
27
|
- 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.
|
|
28
28
|
- Self-hosted FRP accepts only a VPS address, frps control port, operator-generated high-entropy shared token, and standard-port public HTTPS origin. The plugin validates the token's length and format; the operator remains responsible for its entropy. It generates one HTTP vhost pointed at the plugin's ephemeral loopback gateway; arbitrary FRP configuration, TCP/UDP forwarding, FRP plugins, services, PATH entries, and startup tasks are not supported.
|
package/lib/client.js
CHANGED
|
@@ -1851,8 +1851,10 @@ window.__ModuleLoader__.load({
|
|
|
1851
1851
|
.dsh-mobile-settings_rowText { flex:1; min-width:0; display:flex; flex-direction:column; gap:4px; padding-right:48px; }
|
|
1852
1852
|
.dsh-mobile-settings_title { color:var(--dsw-alias-label-primary); font-size:14px; font-weight:400; line-height:22px; }
|
|
1853
1853
|
.dsh-mobile-settings_desc { color:var(--dsw-alias-label-tertiary); font-size:12px; font-weight:400; line-height:18px; }
|
|
1854
|
-
.dsh-mobile-settings_selector { display:inline-flex; align-items:center; justify-content:center; min-width:max-content; height:
|
|
1854
|
+
.dsh-mobile-settings_selector { display:inline-flex; align-items:center; justify-content:center; min-width:max-content; min-height:48px; padding:0 14px; border:0; border-radius:24px; background:var(--dsw-alias-bg-module-platform); color:var(--dsw-alias-label-primary); font:inherit; font-size:14px; line-height:22px; cursor:pointer; }
|
|
1855
1855
|
.dsh-mobile-settings_selector:hover:not(:disabled) { background:var(--dsw-alias-interactive-bg-hover); }
|
|
1856
|
+
.dsh-mobile-settings_selector:active:not(:disabled) { background:var(--dsw-alias-interactive-bg-active,var(--dsw-alias-interactive-bg-hover)); }
|
|
1857
|
+
.dsh-mobile-settings_selector:focus-visible { outline:2px solid var(--dsw-alias-label-primary-bluish,#2563eb); outline-offset:2px; }
|
|
1856
1858
|
.dsh-mobile-settings_selector:disabled { cursor:wait; opacity:.55; }
|
|
1857
1859
|
@media ${NATIVE_MOBILE_OVERLAY_QUERY} {
|
|
1858
1860
|
html.dsh-native-mobile-active,html.dsh-native-mobile-active body { width:100%; height:100%; overflow:hidden; }
|
|
@@ -1923,7 +1925,7 @@ window.__ModuleLoader__.load({
|
|
|
1923
1925
|
[data-dsh-mobile-settings-options] > * { width:100% !important; min-width:0 !important; }
|
|
1924
1926
|
[data-dsh-mobile-settings-options] [data-slot="settings.general.item"] > [class*="_row"] { flex-direction:column !important; align-items:stretch !important; gap:12px !important; }
|
|
1925
1927
|
[data-dsh-mobile-settings-options] [data-slot="settings.general.item"] [class*="_rowText"] { width:100% !important; padding-right:0 !important; }
|
|
1926
|
-
[data-dsh-mobile-settings-options] [data-slot="settings.general.item"] [class*="_selector"] { box-sizing:border-box !important; align-self:flex-start !important; justify-content:space-between !important; min-width:0 !important; min-height:
|
|
1928
|
+
[data-dsh-mobile-settings-options] [data-slot="settings.general.item"] [class*="_selector"] { box-sizing:border-box !important; align-self:flex-start !important; justify-content:space-between !important; min-width:0 !important; min-height:48px !important; max-width:100% !important; }
|
|
1927
1929
|
[data-dsh-mobile-settings-options] :is(input,select,textarea,button) { max-width:100%; }
|
|
1928
1930
|
[data-dsh-mobile-settings-options] :is(input,select,textarea) { box-sizing:border-box; width:100%; min-width:0; }
|
|
1929
1931
|
[data-dsh-mobile-settings-options] [class*="_head"] { min-width:0; flex-wrap:wrap; }
|
|
@@ -2904,6 +2906,90 @@ window.__ModuleLoader__.load({
|
|
|
2904
2906
|
onClick: invoke
|
|
2905
2907
|
}, state === "busy" ? copy.busy : state === "error" ? copy.retry : copy.action));
|
|
2906
2908
|
}
|
|
2909
|
+
function mobileTaskNotificationCopy(locale) {
|
|
2910
|
+
if (locale === "zh") return {
|
|
2911
|
+
title: "任务通知",
|
|
2912
|
+
description: "在 Android 系统中开启任务完成和待确认提醒。",
|
|
2913
|
+
action: "通知设置",
|
|
2914
|
+
busy: "正在打开…",
|
|
2915
|
+
success: "请在系统提示或设置中确认任务通知权限。",
|
|
2916
|
+
reopen: "重新打开",
|
|
2917
|
+
error: "无法打开通知设置,请重试。",
|
|
2918
|
+
retry: "重试"
|
|
2919
|
+
};
|
|
2920
|
+
if (locale === "it") return {
|
|
2921
|
+
title: "Notifiche delle attività",
|
|
2922
|
+
description: "Attiva gli avvisi Android per le attività completate o in attesa di conferma.",
|
|
2923
|
+
action: "Impostazioni notifiche",
|
|
2924
|
+
busy: "Apertura…",
|
|
2925
|
+
success: "Conferma il permesso nella richiesta o nelle impostazioni di sistema.",
|
|
2926
|
+
reopen: "Riapri",
|
|
2927
|
+
error: "Impossibile aprire le impostazioni delle notifiche. Riprova.",
|
|
2928
|
+
retry: "Riprova"
|
|
2929
|
+
};
|
|
2930
|
+
return {
|
|
2931
|
+
title: "Task notifications",
|
|
2932
|
+
description: "Enable Android reminders for completed tasks and pending confirmation.",
|
|
2933
|
+
action: "Notification settings",
|
|
2934
|
+
busy: "Opening…",
|
|
2935
|
+
success: "Confirm notification access in the system prompt or settings.",
|
|
2936
|
+
reopen: "Open again",
|
|
2937
|
+
error: "Could not open notification settings. Try again.",
|
|
2938
|
+
retry: "Retry"
|
|
2939
|
+
};
|
|
2940
|
+
}
|
|
2941
|
+
/** Render the Android-only notification permission entry in DSH General settings. */
|
|
2942
|
+
function MobileTaskNotificationRow() {
|
|
2943
|
+
const [nativeReady, setNativeReady] = (0, react.useState)(false);
|
|
2944
|
+
const [state, setState] = (0, react.useState)("idle");
|
|
2945
|
+
(0, react.useEffect)(() => {
|
|
2946
|
+
let disposed = false;
|
|
2947
|
+
const checkNativeAction = () => {
|
|
2948
|
+
const bridge = window.__DSH_MOBILE_NATIVE__;
|
|
2949
|
+
if (bridge === void 0) {
|
|
2950
|
+
setNativeReady(false);
|
|
2951
|
+
return;
|
|
2952
|
+
}
|
|
2953
|
+
Promise.resolve().then(() => bridge.capabilities()).then((capabilities) => {
|
|
2954
|
+
if (!disposed) setNativeReady(capabilities.includes("notification.settings"));
|
|
2955
|
+
}, () => {
|
|
2956
|
+
if (!disposed) setNativeReady(false);
|
|
2957
|
+
});
|
|
2958
|
+
};
|
|
2959
|
+
checkNativeAction();
|
|
2960
|
+
window.addEventListener("dsh-mobile-native-ready", checkNativeAction);
|
|
2961
|
+
return () => {
|
|
2962
|
+
disposed = true;
|
|
2963
|
+
window.removeEventListener("dsh-mobile-native-ready", checkNativeAction);
|
|
2964
|
+
};
|
|
2965
|
+
}, []);
|
|
2966
|
+
if (!nativeReady) return null;
|
|
2967
|
+
const locale = selectedMobileControlLocale();
|
|
2968
|
+
const copy = mobileTaskNotificationCopy(locale);
|
|
2969
|
+
const invoke = () => {
|
|
2970
|
+
const bridge = window.__DSH_MOBILE_NATIVE__;
|
|
2971
|
+
if (bridge === void 0 || state === "busy") return;
|
|
2972
|
+
setState("busy");
|
|
2973
|
+
bridge.invoke("notification.settings", {}).then(() => {
|
|
2974
|
+
setState("success");
|
|
2975
|
+
}, () => {
|
|
2976
|
+
setState("error");
|
|
2977
|
+
});
|
|
2978
|
+
};
|
|
2979
|
+
return (0, react.createElement)("div", {
|
|
2980
|
+
className: "dsh-mobile-settings_row",
|
|
2981
|
+
lang: locale
|
|
2982
|
+
}, (0, react.createElement)("div", { className: "dsh-mobile-settings_rowText" }, (0, react.createElement)("div", { className: "dsh-mobile-settings_title" }, copy.title), (0, react.createElement)("div", {
|
|
2983
|
+
className: "dsh-mobile-settings_desc",
|
|
2984
|
+
"aria-live": "polite"
|
|
2985
|
+
}, state === "success" ? copy.success : state === "error" ? copy.error : copy.description)), (0, react.createElement)("button", {
|
|
2986
|
+
type: "button",
|
|
2987
|
+
className: "dsh-mobile-settings_selector",
|
|
2988
|
+
disabled: state === "busy",
|
|
2989
|
+
"aria-busy": state === "busy" ? "true" : void 0,
|
|
2990
|
+
onClick: invoke
|
|
2991
|
+
}, state === "busy" ? copy.busy : state === "success" ? copy.reopen : state === "error" ? copy.retry : copy.action));
|
|
2992
|
+
}
|
|
2907
2993
|
const CONTROL_REQUEST_TIMEOUT_MS = 15e3;
|
|
2908
2994
|
const LONG_CONTROL_REQUEST_TIMEOUT_MS = 21e4;
|
|
2909
2995
|
const GITHUB_RELEASES_URL = "https://github.com/saya-ch/dsh-mobile/releases";
|
|
@@ -7068,11 +7154,22 @@ window.__ModuleLoader__.load({
|
|
|
7068
7154
|
style.textContent = desktopAdmin ? CONTROL_STYLES : NATIVE_MOBILE_STYLES;
|
|
7069
7155
|
document.head.append(style);
|
|
7070
7156
|
if (!desktopAdmin) {
|
|
7071
|
-
const removeSettingsAction = ctx.slots.inject("settings.general.item", () =>
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
|
|
7157
|
+
const removeSettingsAction = ctx.slots.inject("settings.general.item", () => {
|
|
7158
|
+
const removeSwitchComputer = ctx.slots.register({
|
|
7159
|
+
name: "settings.general.item",
|
|
7160
|
+
id: "dsh-mobile-switch-computer",
|
|
7161
|
+
order: 100
|
|
7162
|
+
}, MobileSwitchComputerRow);
|
|
7163
|
+
const removeTaskNotifications = ctx.slots.register({
|
|
7164
|
+
name: "settings.general.item",
|
|
7165
|
+
id: "dsh-mobile-task-notifications",
|
|
7166
|
+
order: 110
|
|
7167
|
+
}, MobileTaskNotificationRow);
|
|
7168
|
+
return () => {
|
|
7169
|
+
removeTaskNotifications();
|
|
7170
|
+
removeSwitchComputer();
|
|
7171
|
+
};
|
|
7172
|
+
});
|
|
7076
7173
|
const removeCustom = installCustomAssets();
|
|
7077
7174
|
const removeSurface = installDshLanguageBoundSurface(installNativeMobileSurface);
|
|
7078
7175
|
return () => {
|