dsh-mobile 0.4.0 → 0.4.2

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 CHANGED
@@ -2,9 +2,41 @@
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
- ## Unreleased
6
-
7
- Future changes will be recorded here.
5
+ ## 0.4.2 - 2026-09-16
6
+
7
+ - Add an Own reverse proxy provider under Remote → Self-hosted for an existing user-managed HTTPS proxy. It provides a separate authenticated private HTTP origin (default 3444), strict private bind/source-CIDR validation, custom public HTTPS ports, local configuration and safe settings-only purge.
8
+ - Distinguish backend listening from unverified public HTTPS/certificate/WebSocket reachability, with localized setup, errors and diagnostics; retain the existing LAN gateway, remote pairing and Android protocol.
9
+ - Cover the HTTPS proxy → private HTTP origin → DSH path with real loopback pairing, authenticated HTTP and WebSocket tests, including Host/Origin/forwarded-header rejection and LAN independence.
10
+ - Permanently remove revoked devices from durable storage and the desktop list, terminate their active Sessions, and compact legacy `revokedAt` rows on startup. Deleted credentials are rejected as `authentication_failed`; no revocation tombstones are retained. An offline revoked device is therefore re-paired rather than shown as revoked when it reconnects.
11
+ - Load a bundled Iterator compatibility script before DSH boot on the dedicated mobile frontend, preventing `Iterator is not defined` on WebViews without Iterator helpers. The script is served locally behind the existing gateway authentication and uses feature detection to preserve or repair native helpers without weakening CSP or dropping script nonces.
12
+ - Add a cloudflared remote provider (Windows x64) in two modes. The pinned official client is downloaded from the official release page and SHA-256 verified only after confirmation, is launched with automatic updates disabled, and does not add a system service, startup item, registry entry, or PATH entry. **Quick mode** needs no account, token, or DNS record: cloudflared allocates a temporary `*.trycloudflare.com` address, which the gateway validates before adopting it — including rejecting the reserved control-plane hosts the banner can print first, such as `api.trycloudflare.com`, which would otherwise have put Cloudflare's API in the pairing QR code. **Named mode** uses a connector token, a public hostname, and a local forward port: the hostname stays the same across restarts, the token is stored only in the DSH Mobile private directory and handed to cloudflared through `TUNNEL_TOKEN` rather than the command line, and it is never returned to any client. Because Cloudflare routes the hostname to that exact port, the port is bound as configured and a taken port is reported as a hard failure instead of silently moving; readiness comes from the connector's own `Registered tunnel connection` line, since a named tunnel prints no banner. Switching back to a quick tunnel removes the stored token.
13
+ - Let the Android app classify `*.trycloudflare.com` as a supported remote tunnel host, so a scanned cloudflared pairing link selects remote access on its own instead of being rejected while no flow has been chosen yet. A named tunnel uses a domain the operator owns, which the app cannot classify by itself, so it is accepted only while the Remote flow is active. Both require an app build that includes this change; earlier builds still pair from the Remote access flow.
14
+ - Keep the mobile gateway usable when its broadcast discovery socket cannot bind the UDP port: Windows keeps separate TCP and UDP port-exclusion tables, so the port the operating system handed the TCP listener can be refused for UDP, and another process may already hold it. Discovery now degrades on its own while mDNS, HTTP and WebSocket service continue, instead of failing the whole listener.
15
+ - Make the remote provider chooser readable now that it offers three providers: the cards list one per row instead of leaving the third orphaned in half a row, the card description — the copy that decides the choice — is no longer the smallest text in the panel, and the destructive inline actions are no longer its smallest targets.
16
+ - Fix the on-demand component download, so the cloudflared component can actually install: a pinned GitHub release URL answers with a redirect, and the downloader refused every redirect, which failed the install in under a second with `TypeError: fetch failed` and no bytes transferred. One redirect hop is now followed after validating its scheme and a release-asset host, and a transport failure is retried once, because a 55 MB transfer through a TUN proxy can reset mid-stream.
17
+ - Keep a failed provider action readable: the status line now holds its message through the repaint that follows, instead of being replaced by the server snapshot within a frame, which made a failed download look like a click that did nothing.
18
+ - Replace the panel's hardcoded colours and its smallest type with DSH tokens and one 11px floor, and reduce interactive targets to two tiers (36px inline, 44px primary). An earlier attempt at the colour work referenced four DSH aliases that do not exist, so every var() fell back to its light literal and the dark theme never adapted; the panel now uses the real border-l1/l2/l3 and state tokens, and a guard test parses the stylesheet so a stray brace can no longer silently drop declarations.
19
+ - Show a failed provider request in the user's own language. A rejection carries its error code, and the panel stringified the whole error, so a rejected tunnel hostname or a failed component download printed the raw code in every locale while the translated sentence sat unreachable; provider failures now resolve through one code-to-copy table.
20
+ - Keep broadcast discovery observable when its UDP port cannot be bound: the failure is reported through the management status and logged once at startup, instead of vanishing while the phone simply cannot find the computer. A missing bundled mobile asset is reported the same way rather than appearing only as a failed subresource.
21
+ - Never let the compatibility bundle break the mobile frontend. An index without a script tag used to fail the whole page, markup inside an HTML comment was accepted as the injection point (so the fix silently never ran), and an already-injected bundle went unrecognised when the document used single quotes.
22
+ - Reserve DSH's own WebServer port (3080) for the reverse-proxy backend and keep that port in the unprivileged range, matching the LAN gateway reservation that already refused 3443.
23
+ - Export the cloudflared tunnel configuration surface so installation tooling can pre-seed a named tunnel through the same validated path instead of writing JSON by hand.
24
+ - Document that a named tunnel must be scanned from inside the app's Remote flow, and that device credentials are bound to their exact origin, so moving a remote channel to a fixed hostname requires pairing the phone again.
25
+ - Reorganize the shipped guides into a bilingual index and remove their broken relative links.
26
+
27
+ ## 0.4.1 - 2026-09-15
28
+
29
+ - Allow the desktop Mobile access admin API and sidebar control on RFC1918 and IPv4 link-local Host values when the TCP peer remains loopback, so headless Linux hosts and LAN reverse proxies can open DSH Web without a localhost-only browser. Public IPs, CGNAT, and arbitrary DNS names stay rejected. The dedicated Mobile HTTPS listener remains the phone surface (thanks @xingleiwu for PR #79).
30
+ - Let the proxied DSH GUI frame its own same-origin surfaces and embed external http(s) and blob: pages, so the right-sidebar browser tab, the HTML/diff previews, and the PDF preview work over LAN and remote access; gateway-owned login, pairing, and JSON responses keep refusing every frame (thanks @idoall for PR #77).
31
+ - Bound advisory operating-system route inspection so a slow Windows network query falls back to explicit network selection instead of holding the local setup page open.
32
+ - Keep Android CI and release setup limited to currently available SDK packages so APK verification is not blocked by the retired `tools` package.
33
+ - Require a same-origin `Origin` header on every mutating desktop management request, including clients that omit Fetch Metadata, so private-Host reverse proxies cannot bypass the browser CSRF check.
34
+ - Accept the DSH conversation scroll marker wherever it lives under the conversation skeleton, keeping the compatibility gate valid when upstream extracts the body into a separate component.
35
+ - Allow HTTP iframe sources for compatibility while showing a localized warning that unencrypted pages can be altered and should not be used for sensitive work.
36
+ - Fix mobile extension Host actions that sent JSON without an explicit content type, which caused every `api.host.invoke()` call to return `415 unsupported_media_type`.
37
+ - Accept callable Schemastery input schemas as well as existing `parse(value)` adapters for extension actions, so documented `api.schema.object(...)` inputs are validated and normalized before `run()`.
38
+ - Update the development peers and source compatibility gate for DeepSeek Harness `0.1.6-alpha.1`: the checker follows extracted conversation/composer markers and the named global transport hook without weakening the Host-trust or authenticated RPC checks.
39
+ - Correct bilingual navigation and historical issue links, clarify Android task-reminder versus browser-notification behavior, and add a complete English self-hosted FRP guide without changing runtime behavior.
8
40
 
9
41
  ## 0.4.0 - 2026-09-14
10
42
 
@@ -13,7 +45,7 @@ Future changes will be recorded here.
13
45
  - Use a bounded session-free native probe for list reachability checks, with a renewal fallback for older plugins so status refreshes cannot evict an active DSH session.
14
46
  - Preserve a local row after computer-side revocation, stop automatic retries for revoked credentials, and expose a short-lived undo action for local deletion without restoring a computer-side authorization.
15
47
  - Follow the DSH conversation's actual nested scroll container when showing or hiding the Android toolbar, keeping task-notification settings reachable on current DSH Web layouts.
16
- - Let cpolar choose its default route instead of forcing `cn`; explicit region settings remain available for advanced deployments.
48
+ - Let cpolar choose its default route instead of forcing `cn`: the plugin no longer passes `-region`, so cpolar selects its own tunnel server. No profile or environment setting exposes an explicit region.
17
49
  - Add Android task-completion and pending-input reminders through authenticated Host events and the exact-origin native bridge; notification permission is enabled explicitly from the foreground app menu, lock-screen text stays generic, and each completed turn keeps a separate reminder (thanks @qzyqmzn for PR #75).
18
50
  - Show and re-copy remote pairing links without invalidating the QR code's active one-time pairing window (thanks @qzyqmzn for PR #75).
19
51
  - Detect plugin-market installations that have not completed LAN setup, prevent the loopback-only `127.0.0.1` fallback from being presented as phone access, and provide a localized in-panel network picker that creates private TLS material and LAN-only Windows firewall rules after explicit confirmation. The configured gateway starts after one DSH restart (thanks @cangming99 for #72).
@@ -93,7 +125,7 @@ Future changes will be recorded here.
93
125
 
94
126
  ## 0.3.7 - 2026-09-02
95
127
 
96
- - Fix the Windows DSH Desktop startup failure reported in [#22#22](https://github.com/saya-ch/dsh-mobile/issues/22): capture subprocess output through the callback API instead of relying on `execFile` promisify metadata that a host wrapper may omit. Thanks @XChen446 for the precise 0.3.6 stack trace.
128
+ - Fix the Windows DSH Desktop startup failure reported in [#22](https://github.com/saya-ch/dsh-mobile/issues/22): capture subprocess output through the callback API instead of relying on `execFile` promisify metadata that a host wrapper may omit. Thanks @XChen446 for the precise 0.3.6 stack trace.
97
129
  - Apply the same output handling to Windows SID resolution, private-file ACLs, route selection, and firewall diagnostics/setup. Invalid SID output and ACL failures still reject the operation; a failed SID lookup can be retried and Windows permission commands have bounded execution time.
98
130
  - Retain the 0.3.6 removal of the exact DSH version allowlist. Existing 0.3.3-0.3.6 Android apps and paired devices remain compatible; the 0.3.7 APK updates version metadata only.
99
131
 
@@ -105,7 +137,7 @@ Future changes will be recorded here.
105
137
 
106
138
  ## 0.3.5 - 2026-09-01
107
139
 
108
- - Fix [#26#26](https://github.com/saya-ch/dsh-mobile/issues/26): prevent mobile startup from remaining on “Loading plugins” over LAN or remote access when DSH sends the WebSocket upgrade response and initial snapshot together. The gateway now preserves that snapshot without relaxing its 16 KiB response-header limit. Thanks @oliverwan97 for the detailed report.
140
+ - Fix [#26](https://github.com/saya-ch/dsh-mobile/issues/26): prevent mobile startup from remaining on “Loading plugins” over LAN or remote access when DSH sends the WebSocket upgrade response and initial snapshot together. The gateway now preserves that snapshot without relaxing its 16 KiB response-header limit. Thanks @oliverwan97 for the detailed report.
109
141
 
110
142
  ## 0.3.4 - 2026-08-31
111
143
 
@@ -128,7 +160,7 @@ Future changes will be recorded here.
128
160
 
129
161
  ## 0.3.2 - 2026-08-29
130
162
 
131
- - Special thanks to @JackRushante for [#16#16](https://github.com/saya-ch/dsh-mobile/pull/16): the secure Android media bridge, image attachments, localization foundation, bounded extension requests, and Funnel lifecycle hardening. This release retains all four original commits and their author metadata.
163
+ - Special thanks to @JackRushante for [#16](https://github.com/saya-ch/dsh-mobile/pull/16): the secure Android media bridge, image attachments, localization foundation, bounded extension requests, and Funnel lifecycle hardening. This release retains all four original commits and their author metadata.
132
164
  - Move image selection and camera capture into a dedicated top row of the composer command menu, without focusing the message editor.
133
165
  - Push extension and `/mobile` changes to authenticated phones immediately, while retaining bounded polling as a network-recovery fallback.
134
166
  - Bind each mobile UI to its matching Host, script, style, and asset generation; retain the previous Host through a bounded refresh window, fail closed on client activation errors, and tighten scoped requests against encoded path traversal.
@@ -138,7 +170,7 @@ Future changes will be recorded here.
138
170
 
139
171
  ## 0.3.1 - 2026-08-28
140
172
 
141
- - Credit @BlueandwhiteXD ([#15#15](https://github.com/saya-ch/dsh-mobile/pull/15)) for the Android keyboard inset report and fix incorporated into the 0.3 mobile layout.
173
+ - Credit @BlueandwhiteXD ([#15](https://github.com/saya-ch/dsh-mobile/pull/15)) for the Android keyboard inset report and fix incorporated into the 0.3 mobile layout.
142
174
 
143
175
  ## 0.3.0 - 2026-08-28
144
176
 
package/README.en.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <h1 align="center">DSH Mobile</h1>
6
6
 
7
- <p align="center">Secure, live LAN access to DeepSeek Harness from a phone.</p>
7
+ <p align="center">Secure, live access to DeepSeek Harness from a phone.</p>
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&amp;color=CB3837" alt="npm version"></a>
@@ -15,21 +15,32 @@
15
15
  <a href="https://github.com/awesome-dsh-plugin/awesome-dsh-plugin"><img src="https://awesome-dsh-plugin.com/badge.svg" alt="Awesome DSH Plugin"></a>
16
16
  </p>
17
17
 
18
- <p align="center"><a href="README.md">简体中文</a> · <a href="CHANGELOG.md">Changelog</a></p>
18
+ <p align="center">
19
+ <a href="#what-it-does">What it does</a> ·
20
+ <a href="#quick-start">Quick start</a> ·
21
+ <a href="#connection-guide">Connection guide</a> ·
22
+ <a href="#extend-and-customize">Extend and customize</a> ·
23
+ <a href="#device-management">Device management</a> ·
24
+ <a href="#third-party-plugin-compatibility">Third-party plugins</a> ·
25
+ <a href="#security">Security</a> ·
26
+ <a href="#compatibility">Compatibility</a> ·
27
+ <a href="CHANGELOG.md">Changelog</a> ·
28
+ <a href="README.md">简体中文</a>
29
+ </p>
19
30
 
20
31
  > DSH Mobile is a DeepSeek Harness community plugin; the native app supports Android only.
21
32
  >
22
- > **0.4.0 update**: adds multi-device management, computer-side revocation status sync, and session-free reachability checks; improves plugin-market LAN onboarding, task-state notifications, the immersive WebView layout, and three-language pairing pages. [Details](CHANGELOG.md).
33
+ > **0.4.2 update**: adds an own HTTPS reverse proxy and the cloudflared remote channel (quick and named tunnels) with on-demand component installation, plus one-command frps + Caddy deployment. The provider chooser and panel copy were re-measured at its real 380 px width, and the on-demand download no longer dies on a redirect or hides its failure message. [Details](CHANGELOG.md).
23
34
  >
24
- > **Upgrade reminder**: 0.4.0 requires the plugin and Android app to be updated together for the multi-device list. Older apps continue to use their existing single-device pairing. [Compatibility notes](#compatibility).
35
+ > **Upgrade reminder**: the 0.4.2 plugin continues to work with the 0.4.0 Android app and existing devices do not need re-pairing; a named tunnel must be scanned from inside the app's Remote flow. Install the 0.4.2 app as well if you want this Android build. [Compatibility notes](#compatibility).
25
36
 
26
37
  <p align="center">
27
- <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.0/dsh-mobile-android-v0.4.0.apk"><img src="assets/brand/app-icon-rounded.svg" alt="DSH Mobile Android app icon" width="72" height="72"></a><br>
28
- <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.0/dsh-mobile-android-v0.4.0.apk"><strong>Download Android app 0.4.0</strong></a><br>
29
- <sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.4.0">Release notes and checksums</a></sub>
38
+ <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.2/dsh-mobile-android-v0.4.2.apk"><img src="assets/brand/app-icon-rounded.svg" alt="DSH Mobile Android app icon" width="72" height="72"></a><br>
39
+ <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.2/dsh-mobile-android-v0.4.2.apk"><strong>Download Android app 0.4.2</strong></a><br>
40
+ <sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.4.2">Release notes and checksums</a></sub>
30
41
  </p>
31
42
 
32
- 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, or self-hosted FRP 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.
43
+ 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.
33
44
 
34
45
  Mobile access runs on its own HTTPS origin with pinned certificates; only paired devices pass validation.
35
46
 
@@ -80,7 +91,7 @@ Restart DSH, then search for **dsh-mobile** under **Settings → Plugin Market**
80
91
 
81
92
  After installation, start DSH and use the connection guide below to choose LAN or remote access.
82
93
 
83
- Registry-installed plugins check for updates when the desktop UI loads and show “Update plugin” beside the access-panel title when a newer release is available. Restart DSH after installation. The app download entry shows the latest version; local development packages are not overwritten, and Android does not send update notifications.
94
+ Registry-installed plugins check for updates when the desktop UI loads and show “Update plugin” beside the access-panel title when a newer release is available. Restart DSH after installation. The app download entry shows the latest version; local development packages are not overwritten, and Android does not check for or push app-version updates.
84
95
 
85
96
  ## Connection guide
86
97
 
@@ -101,15 +112,17 @@ Use this when the phone and computer share Wi-Fi, Ethernet, or a phone hotspot.
101
112
 
102
113
  Port note: `dsh web --port` changes the DSH Web upstream port (3080 by default), which the plugin follows automatically. `dsh-mobile setup --port` changes the Mobile HTTPS listener (3443 by default), and the pairing QR code includes the selected port.
103
114
 
115
+ Headless Linux hosts, and browsers that open DSH Web through a LAN IP or reverse proxy, can use the same **Mobile access** control in the lower-left corner. The admin API still requires a loopback TCP peer (for example a local `socat` or reverse proxy to `127.0.0.1`) so the plugin itself is not LAN-exposed. The browser Host may be `localhost`, RFC1918, or an IPv4 link-local address; public IPs and arbitrary DNS names still return 403. The reverse proxy must be limited to trusted local or LAN callers and must not publicly forward `/api/mobile-access`. The dedicated Mobile HTTPS listener (3443 by default) remains the phone surface and does not become the desktop admin panel.
116
+
104
117
  The app is optional: select **Copy pairing link** and open it in a mobile browser. The browser must manually trust the plugin certificate on the first visit.
105
118
 
106
119
  Browser pairing and reauthentication pages use the browser's `Accept-Language` to show Simplified Chinese, English, or Italian. Android screens follow the system language, while the DSH plugin control panel follows the language selected by DSH.
107
120
 
108
121
  ### Remote access
109
122
 
110
- Use this after the phone leaves the computer's network. Remote access is disabled by default, and the phone needs no separate Tailscale, cpolar, or FRP app.
123
+ Use this after the phone leaves the computer's network. Remote access is disabled by default, and the phone needs no separate Tailscale, cpolar, cloudflared, or FRP app.
111
124
 
112
- Remote providers may impose bandwidth and connection limits: the [cpolar Free plan](https://svip.cpolar.com/pricing) currently lists 1 Mbps, while [Tailscale Funnel](https://tailscale.com/docs/features/tailscale-funnel#requirements-and-limitations) has non-configurable bandwidth limits. DSH Mobile reduces transfer and waiting with 10-message pages, load-on-scroll history, gzip, and a persistent WebSocket, but it cannot raise provider quotas.
125
+ Remote providers may impose bandwidth and connection limits: the [cpolar Free plan](https://svip.cpolar.com/pricing) currently lists 1 Mbps, while [Tailscale Funnel](https://tailscale.com/docs/features/tailscale-funnel#requirements-and-limitations) has non-configurable bandwidth limits, and a cloudflared quick tunnel is a free Cloudflare address with randomized hostnames and rate limiting. DSH Mobile reduces transfer and waiting with 10-message pages, load-on-scroll history, gzip, and a persistent WebSocket, but it cannot raise provider quotas.
113
126
 
114
127
  <p align="center">
115
128
  <img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/remote-access-en.png" width="82%" alt="DSH Mobile remote access and provider selection">
@@ -118,18 +131,22 @@ Remote providers may impose bandwidth and connection limits: the [cpolar Free pl
118
131
  1. Open **Mobile Access → Remote** in the lower-left corner of DeepSeek Harness and choose a provider:
119
132
  - **Tailscale Funnel**: select **Enable remote access**, complete the one-time Tailscale sign-in on the official page, follow the panel prompt to allow Funnel, then return to DSH and wait until the connection is ready.
120
133
  - **cpolar**: select **Install official component**, sign in to the cpolar dashboard and obtain an Authtoken, paste it, then select **Save and connect**. The component is downloaded into the plugin's private directory only after confirmation; free temporary addresses may change after DSH or cpolar restarts.
121
- - **Self-hosted FRP (advanced)**: expand **Self-hosted connection**, enter the VPS, frps port, shared token, and public HTTPS origin. The origin may use your domain or the VPS public IPv4 address (for example, `https://203.0.113.10` — substitute your own real address; documentation ranges are rejected). Apply the restricted template manually or enter an SSH user, port, and local private-key path for automatic deployment. Automatic deployment supports Ubuntu/Debian with systemd, uses OpenSSH keys or an agent, refuses password auth, and does not overwrite Caddy configuration it does not manage; both deployment and server cleanup display the SSH host keys for verification against the VPS console before continuing. IPv4 mode obtains a roughly six-day Let's Encrypt IP certificate and installs daily automatic renewal. Install the official `frpc` on demand and verify the path afterward. A reviewable uninstall script or one-click server cleanup removes only DSH Mobile-owned services and configs. This requires Android app 0.3.3 or later.
122
- 2. When the panel reports that remote access is ready, select **Create remote pairing QR code**.
134
+ - **Self-hosted FRP (advanced)**: expand **Self-hosted connection**, enter the VPS, frps port, shared token, and public HTTPS origin. The origin may use your domain or the VPS public IPv4 address (for example, `https://203.0.113.10` — substitute your own real address; documentation ranges are rejected). Apply the restricted template manually or enter an SSH user, port, and local private-key path for automatic deployment. Automatic deployment supports Ubuntu/Debian with systemd, uses OpenSSH keys or an agent, refuses password auth, and does not overwrite Caddy configuration it does not manage; both deployment and server cleanup display the SSH host keys for verification against the VPS console before continuing. IPv4 mode obtains a roughly six-day Let's Encrypt IP certificate and installs daily automatic renewal. Install the official `frpc` on demand and verify the path afterward. A reviewable uninstall script or one-click server cleanup removes only DSH Mobile-owned services and configs. This requires Android app 0.3.3 or later. See the [English self-hosted FRP guide](docs/SELF_HOSTED_FRP.en.md) for the complete procedure.
135
+ - **Own reverse proxy**: open **Self-hosted connection → Own reverse proxy**, enter the public HTTPS origin (custom ports supported), private listen IPv4, separate HTTP backend port (default 3444), and allowed proxy source CIDRs, then select **Save and start backend**. This uses your existing Lucky/Nginx/Caddy without a tunnel component and requires Android app 0.4.0 or later. See the [own reverse proxy guide](docs/SELF_HOSTED_ORIGIN.en.md).
136
+ - **cloudflared**: select **Install official component**. After you confirm, the plugin downloads a pinned build from the official release page into its private directory and requests a temporary public address (a quick tunnel) with **no sign-up or sign-in**. Choose this when you would rather not create an account. The quick-tunnel hostname changes on every reconnect, and Cloudflare positions quick tunnels for testing: they are rate-limited and carry no uptime guarantee, so do not rely on one for production access that must stay reachable. With a Cloudflare account and domain, switch **Tunnel type** to **Named**, then supply a connector token, a public hostname and a local forward port to get an address that survives restarts. The token is stored only in the private directory and reaches cloudflared through the environment rather than the command line; see [Cloudflare named tunnel](docs/CLOUDFLARE_TUNNEL.en.md).
137
+ 2. When the panel reports that remote access is ready, select **Create remote pairing QR code**. Own reverse proxy reports only **Backend listening**: public HTTPS, its certificate and WebSocket still require verification from your phone.
123
138
  3. In the Android app, open **Remote access** and scan the QR code to create its separate pairing.
124
139
  4. The app saves the current address and device credential for automatic reconnection. If a free cpolar address changes, scan the computer's current remote QR code to verify the connection again; clearing app data is unnecessary. A stored device token is sent only to its exact saved Origin, never to a new QR-code domain.
125
140
 
126
- > **Remote notifications**: browser notification permission is granted per origin, so allow it separately for the remote address; OS-level toasts only appear on the computer, never on the phone. To push task completion to the phone, use a third-party channel (e.g. dsh-messager's Feishu/WeCom/Telegram push), which is delivered server-side and unaffected by the gateway.
141
+ > **Remote notifications**: browser `Notification` permission is granted per Origin, and a web-page system toast appears only on the device running that page. Android task reminders are a separate 0.4.0 feature: enable them from the app's foreground menu, and keep the WebView page alive; they are not a general background push service. For reliable background delivery, use a server-side webhook or bot channel you have configured.
127
142
 
128
- Tailscale Funnel has broad reach but may be unreliable from mainland China. Its runtime ties the public listener to the parent process and a bounded control channel; parent exit, channel closure, or an explicit stop ends the current generation and cleans up its resources. cpolar is better suited to mainland networks, while self-hosted FRP fits users who already have a VPS and want to avoid public-provider bandwidth quotas. An unregistered domain on a mainland-China VPS may be intercepted by the cloud provider; public IPv4 mode avoids that dependency. The plugin validates pinned on-demand components, stores their configuration and programs entirely under `$DSH_HOME/mobile-access/`, and can remove them completely from the panel.
143
+ Tailscale Funnel has broad reach but may be unreliable from mainland China. Its runtime ties the public listener to the parent process and a bounded control channel; parent exit, channel closure, or an explicit stop ends the current generation and cleans up its resources. cpolar is better suited to mainland networks, while self-hosted FRP fits users who already have a VPS and want to avoid public-provider bandwidth quotas. cloudflared runs in two modes: a quick tunnel needs no account or sign-in, but its hostname is random, changes on every reconnect, and is positioned by Cloudflare for testing with no uptime guarantee, so it suits temporary or verification use rather than a permanent channel; a named tunnel uses a Cloudflare account token and keeps one fixed public hostname across restarts. An unregistered domain on a mainland-China VPS may be intercepted by the cloud provider; public IPv4 mode avoids that dependency. The plugin validates pinned on-demand components, stores their configuration and programs entirely under `$DSH_HOME/mobile-access/`, and can remove them completely from the panel.
129
144
 
130
145
  Self-hosted FRP generates only one HTTP vhost to the DSH loopback gateway. It exposes no arbitrary FRP configuration, TCP/UDP proxy, or FRP plugin. The VPS plaintext vhost must bind to `127.0.0.1`, with Caddy providing public HTTPS; the plugin rejects a publicly reachable plaintext port and reports readiness only after public discovery identifies the current computer.
131
146
 
132
- The public remote origin still requires DSH device pairing. The bundled Funnel and managed cpolar components currently support Windows x64; on-demand FRP 0.70.1 supports Windows, Linux, and macOS on x64 and arm64.
147
+ The own-proxy HTTP backend must remain on a trusted private network: **never port-forward it publicly or bypass it by proxying to DSH or the existing LAN 3443 gateway**. CIDRs match the proxy's direct TCP peer, not forwarded headers. Preserve the external Host (including port), Origin, cookies and WebSocket. Clearing proxy settings keeps paired remote devices.
148
+
149
+ The public remote origin still requires DSH device pairing. The bundled Funnel and the managed cpolar and cloudflared components currently support Windows x64; on-demand FRP 0.70.1 supports Windows, Linux, and macOS on x64 and arm64.
133
150
 
134
151
  ## Extend and customize
135
152
 
@@ -149,6 +166,8 @@ Two kinds of changes are supported: the phone UI itself (theme, layout, buttons)
149
166
 
150
167
  Extension manifests, scripts, styles, and assets are revisioned. When the plugin observes a `/mobile` or extension-file change, it notifies authenticated phones to refresh immediately; 45-second visible and 5-minute hidden checks remain only as recovery fallbacks. A failed Host staging pass keeps the current version; if the Host has changed but the new phone UI cannot activate, that extension closes and retries instead of mixing generations.
151
168
 
169
+ An extension action's `input` may use a callable Schemastery schema such as `api.schema.object(...)` or an adapter with `parse(value)`; mobile `api.host.invoke()` sends JSON explicitly, and the input is validated and normalized before the computer-side action runs.
170
+
152
171
  <sub>You can even use an extension to connect to SillyTavern running on the same computer, give it a lightweight mobile frontend, and open it from the same app.</sub>
153
172
 
154
173
  > `host.mjs` has the same privileges as a local program. Create and run only computer-side extensions that you understand and trust.
@@ -164,14 +183,16 @@ The examples above, applied:
164
183
 
165
184
  ### Device management
166
185
 
167
- The Android app keeps LAN, cpolar, Tailscale Funnel, and self-hosted FRP pairings in one **Paired computers** list. The first upgrade migrates the legacy LAN and remote credentials without requiring another pairing; when an address changes, the app merges the row by the DSH installation's stable `instanceId` and keeps its custom name. Device tokens and LAN CAs remain encrypted by Android Keystore and never appear in the list or QR code.
186
+ The Android app keeps LAN, cpolar, cloudflared, Tailscale Funnel, and self-hosted FRP pairings in one **Paired computers** list. The first upgrade migrates the legacy LAN and remote credentials without requiring another pairing; when an address changes, the app merges the row by the DSH installation's stable `instanceId` and keeps its custom name. Device tokens and LAN CAs remain encrypted by Android Keystore and never appear in the list or QR code.
168
187
 
169
188
  Each row shows its custom name, transport, Origin, live reachability, and last connection time. A green dot means **Reachable**; a gray dot means **Checking**, **Temporarily unreachable**, **Pairing expired**, or **Removed on computer**. The check validates the DSH Gateway over HTTPS instead of using ICMP, so a temporary network outage is not mistaken for computer-side revocation.
170
189
 
171
190
  - **Startup behavior → Open DSH directly** (default): one device connects directly; with multiple devices, the app tries the last-used device first, then the still-valid device with the most recent connection. A bounded connection budget returns to the list instead of spinning forever.
172
191
  - **Startup behavior → Show device list**: choose a computer on every launch, which is useful when switching between several machines. The option is in the list's top-right settings button and is saved immediately.
173
192
  - Tap a row to connect. The overflow button and long press open the same action sheet for rename, check now, pair again, or delete the local record. Deletion has a second confirmation and a short undo window; undo restores only the local row and never restores a computer-side revocation.
174
- - Open DSH **Settings → General** in the WebView and select **Switch computer** to return to the paired-device list; this action appears only in the Android app. If the computer revokes a device, the app keeps its row as **Removed on computer**, stops automatic reconnection, and offers **Pair again** or **Delete device**.
193
+ - Open DSH **Settings → General** in the WebView and select **Switch computer** to return to the paired-device list; this action appears only in the Android app. When an online device receives the computer's revocation notification, the app keeps its row as **Removed on computer**, stops automatic reconnection, and offers **Pair again** or **Delete device**.
194
+
195
+ Revoking a device permanently deletes its durable record and token digest instead of retaining a `revokedAt` tombstone. Startup also removes legacy revoked rows. A deleted token receives `401 authentication_failed`, just like an unknown token. Existing apps checking a device that was revoked while offline may therefore show **Pairing expired** and require pairing again; online Sessions still receive the revocation notification and disconnect immediately.
175
196
 
176
197
  <table>
177
198
  <tr>
@@ -192,7 +213,9 @@ The mobile adaptation keeps DSH's existing Workspace, task-management, terminal,
192
213
 
193
214
  Compatibility and WebSocket rules:
194
215
 
195
- - This release is contract-checked against DSH `0.1.5-rc.2` (renderer-v2) and retains the `0.1.5-rc.1` LAN verification. 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.
216
+ 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
+ - The released 0.4.0 is contract-checked against DSH `0.1.5-rc.2` (renderer-v2) and retains the `0.1.5-rc.1` LAN verification. 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.
196
219
  - 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.
197
220
  - 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.
198
221
  - 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.
@@ -219,6 +242,8 @@ Compatibility and WebSocket rules:
219
242
 
220
243
  The Android app is a thin Kotlin WebView shell and contains no frontend copy; mobile browsers load the same page. For compatibility diagnosis, append `?frontend=stock` to the browser URL to temporarily use the previous desktop-page adaptation.
221
244
 
245
+ The dedicated mobile page synchronously loads the authenticated, same-origin `/mobile-access/compat.js` before the first DSH boot script. This bundled core-js compatibility layer supplies `Iterator` / Iterator helpers to WebViews that lack them, preventing the startup error `Iterator is not defined`. It uses feature detection to preserve or repair native helpers, needs no CDN, and does not weaken CSP. It does not change the Android APK, desktop page, or `?frontend=stock` page. This is not a promise to support every old engine: the frontend still targets ES2022. Update Android System WebView / Chrome first if other compatibility errors remain.
246
+
222
247
  > **Community client (unofficial)**: [WeChat Mini-Program client](https://github.com/StrawberryAO/dsh-mobile-minapp)
223
248
  > A native WeChat Mini-Program that reuses the Mobile Access pairing and Remote stream protocol (requires dsh-mobile ≥ 0.3.8).
224
249
  > Because WeChat release builds enforce a domain allow-list (ICP-registered HTTPS origins only), it currently works via WeChat DevTools / real-device debugging; see its README.
@@ -241,6 +266,7 @@ Three layers: the Host face for discovery, pairing, HTTPS, loopback proxying, an
241
266
  - Use the LAN listener only on a trusted home, office, or hotspot network; do not add your own port forwarding.
242
267
  - A remote origin is publicly reachable, but unpaired requests cannot enter DSH; turn the remote switch off when it is not needed.
243
268
  - cpolar downloads a pinned official build only after confirmation and verifies its size and SHA-256. It installs no system service, PATH entry, or startup task, and plugin cleanup removes its managed files.
269
+ - cloudflared likewise downloads a pinned build from the official GitHub Release only after confirmation and verifies the exact size and SHA-256, and it launches the client with automatic updates disabled so the running binary is always the verified one. It needs no account, token, or DNS record, stores no credential, and cleanup deletes every file it manages.
244
270
  - Self-hosted FRP downloads pinned official `frpc` only after confirmation and verifies the origin, exact size, SHA-256, archive paths, and executable version. The shared token never appears in status, diagnostics, or logs. Copying the server template places it on the system clipboard, so clear the clipboard after use; local cleanup removes only plugin-managed files, while the VPS is cleaned separately with the uninstall script or one-click server cleanup. Automatic deployment and server cleanup both display the SSH host keys, which must be verified against the VPS console before continuing.
245
271
  - A paired device is a fully trusted DeepSeek Harness operator and can run tools on the computer; revoke lost devices from the computer.
246
272
  - The LAN gateway listens only while Mobile Access is enabled; with it off, DSH keeps running normally on the computer.
@@ -253,6 +279,8 @@ The table below lists, for each plugin version, the DeepSeek Harness version it
253
279
 
254
280
  | DSH Mobile plugin | Verified DeepSeek Harness version |
255
281
  | --- | --- |
282
+ | `0.4.2` | `0.1.6-alpha.1` (local source and renderer-v2 contract check) |
283
+ | `0.4.1` | `0.1.6-alpha.1` (local source and renderer-v2 contract check) |
256
284
  | `0.3.15`, `0.3.16`, `0.4.0` | `0.1.5-rc.2` (contract check); `0.1.5-rc.1` (@idoall LAN verification) |
257
285
  | `0.3.14` | `0.1.3-alpha.2` |
258
286
  | `0.3.9`-`0.3.12` | `0.1.3-alpha.1` |
@@ -285,4 +313,4 @@ npm ci
285
313
  npm run verify
286
314
  ```
287
315
 
288
- See the [Android guide](apps/mobile/README.md). Licensed under [Apache-2.0](LICENSE).
316
+ See the [Android guide](https://github.com/saya-ch/dsh-mobile/blob/main/apps/mobile/README.md). Licensed under [Apache-2.0](LICENSE).
package/README.md CHANGED
@@ -20,23 +20,27 @@
20
20
  <a href="#快速开始">快速开始</a> ·
21
21
  <a href="#连接教程">连接教程</a> ·
22
22
  <a href="#扩展与自定义">扩展与自定义</a> ·
23
+ <a href="#设备管理">设备管理</a> ·
24
+ <a href="#第三方插件适配">第三方插件适配</a> ·
25
+ <a href="#安全">安全</a> ·
26
+ <a href="#兼容性">兼容性</a> ·
23
27
  <a href="CHANGELOG.md">更新记录</a> ·
24
28
  <a href="README.en.md">English</a>
25
29
  </p>
26
30
 
27
31
  > DSH Mobile 是 DeepSeek Harness 社区插件,原生 App 仅支持 Android。
28
32
  >
29
- > **0.4.0 更新**:加入多设备管理、电脑端撤销状态同步、无 Session 可达性检查;完善插件市场局域网引导、任务状态通知、原生 WebView 沉浸布局和三语配对页。[详细记录](CHANGELOG.md)。
33
+ > **0.4.2 更新**:新增自有 HTTPS 反向代理与 cloudflared 远程通道(快速隧道 + 命名隧道),cloudflared 组件改为按需安装;新增一键部署 frps + Caddy。远程提供方选择器与面板文字在真实 380px 宽度下重新校对,并修好了组件下载被重定向卡死、失败提示被刷新覆盖等问题。[详细记录](CHANGELOG.md)。
30
34
  >
31
- > **升级提醒**:0.4.0 需要同步更新插件与 Android App 才能使用多设备列表;旧 App 仍可使用原有单设备配对。[兼容说明](#兼容性)。
35
+ > **升级提醒**:0.4.2 插件可继续使用 0.4.0 Android App,已有设备无需重新配对;命名隧道要求 App 侧处于「远程」流程内扫码。若要使用本次 Android 构建,请同时安装 0.4.2 App。[兼容说明](#兼容性)。
32
36
 
33
37
  <p align="center">
34
- <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.0/dsh-mobile-android-v0.4.0.apk"><img src="assets/brand/app-icon-rounded.svg" alt="DSH Mobile 安卓应用图标" width="72" height="72"></a><br>
35
- <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.0/dsh-mobile-android-v0.4.0.apk"><strong>下载 Android App 0.4.0</strong></a><br>
36
- <sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.4.0">版本说明与校验文件</a></sub>
38
+ <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.2/dsh-mobile-android-v0.4.2.apk"><img src="assets/brand/app-icon-rounded.svg" alt="DSH Mobile 安卓应用图标" width="72" height="72"></a><br>
39
+ <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.4.2/dsh-mobile-android-v0.4.2.apk"><strong>下载 Android App 0.4.2</strong></a><br>
40
+ <sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.4.2">版本说明与校验文件</a></sub>
37
41
  </p>
38
42
 
39
- DSH Mobile 是一个 DeepSeek Harness 插件,让手机浏览器或 Android App 通过局域网,或可选的 Tailscale Funnel、cpolar、自建 FRP 远程通道连接电脑,继续使用同一份会话、工作区、消息和工具。局域网与远程访问分别启停、分别管理设备,且都不修改 DeepSeek Harness 源码。
43
+ DSH Mobile 是一个 DeepSeek Harness 插件,让手机浏览器或 Android App 通过局域网,或可选的 Tailscale Funnel、cpolar、cloudflared、自建 FRP 或自有反向代理远程通道连接电脑,继续使用同一份会话、工作区、消息和工具。局域网与远程访问分别启停、分别管理设备,且都不修改 DeepSeek Harness 源码。
40
44
 
41
45
  移动访问使用独立的 HTTPS 与证书固定,只有配对过的设备能通过校验接入。
42
46
 
@@ -87,7 +91,7 @@ dsh plugin --profile web add dshmarket
87
91
 
88
92
  安装并启动 DSH 后,按照下一节选择局域网或远程连接。
89
93
 
90
- 通过 npm 安装的插件会在桌面界面加载时检查新版本,有更新时在访问面板标题右侧显示“更新插件”,安装后需重启 DSH。App 下载入口展示最新版本;本地开发包不会被自动覆盖,Android App 暂无主动更新通知。
94
+ 通过 npm 安装的插件会在桌面界面加载时检查新版本,有更新时在访问面板标题右侧显示“更新插件”,安装后需重启 DSH。App 下载入口展示最新版本;本地开发包不会被自动覆盖,Android App 不会主动检查或推送版本更新。
91
95
 
92
96
  ## 连接教程
93
97
 
@@ -108,15 +112,17 @@ dsh plugin --profile web add dshmarket
108
112
 
109
113
  端口说明:`dsh web --port` 修改 DSH Web 上游端口(默认 3080),插件会自动跟随;`dsh-mobile setup --port` 修改 Mobile HTTPS 监听端口(默认 3443),配对二维码会包含实际端口。
110
114
 
115
+ 无图形界面的 Linux 主机,或通过局域网 IP / 反向代理打开 DSH Web 时,左下角 **移动访问** 管理口同样可用。管理 API 仍要求 TCP 对端是本机回环(例如本机 `socat` / 反向代理连到 `127.0.0.1`),不会把插件自己暴露到公网;浏览器 Host 可以是 `localhost`、RFC1918 或 IPv4 链路本地地址,公网 IP 和任意域名仍会返回 403。反向代理必须只对可信的本机或内网请求开放,不能把 `/api/mobile-access` 管理路径公开转发到互联网。手机走的独立 HTTPS 入口(默认 3443)仍是移动端界面,不会变成桌面管理口。
116
+
111
117
  不安装 App 也可以访问:点击 **复制配对链接**,在手机浏览器中打开;首次访问需要按浏览器提示手动信任插件证书。
112
118
 
113
119
  手机浏览器中的配对和重新连接页面会按浏览器的 `Accept-Language` 显示简体中文、英文或意大利文;Android App 则跟随系统语言。DSH 内的插件控制面板继续跟随 DSH 当前语言。
114
120
 
115
121
  ### 远程访问
116
122
 
117
- 适合手机离开电脑所在网络后使用。远程访问默认关闭,手机不需要另外安装 Tailscale、cpolar 或 FRP。
123
+ 适合手机离开电脑所在网络后使用。远程访问默认关闭,手机不需要另外安装 Tailscale、cpolar、cloudflared 或 FRP。
118
124
 
119
- 远程服务可能受带宽和连接限额影响:[cpolar 免费方案](https://svip.cpolar.com/pricing) 当前为 1 Mbps,[Tailscale Funnel](https://tailscale.com/docs/features/tailscale-funnel#requirements-and-limitations) 也存在不可配置的带宽限制。DSH Mobile 通过 10 条分页、顶部按需加载、gzip 和 WebSocket 长连接减少流量与等待,但无法突破服务商限额。
125
+ 远程服务可能受带宽和连接限额影响:[cpolar 免费方案](https://svip.cpolar.com/pricing) 当前为 1 Mbps,[Tailscale Funnel](https://tailscale.com/docs/features/tailscale-funnel#requirements-and-limitations) 也存在不可配置的带宽限制,cloudflared 的 quick tunnel 由 Cloudflare 免费提供、地址随机且有限流。DSH Mobile 通过 10 条分页、顶部按需加载、gzip 和 WebSocket 长连接减少流量与等待,但无法突破服务商限额。
120
126
 
121
127
  <p align="center">
122
128
  <img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/remote-access.png" width="82%" alt="DSH Mobile 远程访问与通道选择">
@@ -125,18 +131,22 @@ dsh plugin --profile web add dshmarket
125
131
  1. 在 DeepSeek Harness 左下角打开 **移动访问 → 远程**,选择一种连接方式:
126
132
  - **Tailscale Funnel**:点击 **启用远程访问**,在打开的官方页面完成一次 Tailscale 登录;按面板提示继续允许 Funnel,然后返回 DSH 等待连接就绪。
127
133
  - **cpolar**:点击 **安装官方组件**,登录 cpolar 控制台取得 Authtoken,粘贴后点击 **保存并连接**。组件只会在确认后下载到插件私有目录;免费临时地址可能在 DSH 或 cpolar 重启后变化。
128
- - **自建 FRP(高级)**:展开 **自建连接**,填写 VPS、frps 端口、共享 Token 和公开 HTTPS 地址;公开地址可以是自己的域名,也可以直接是 VPS 公网 IPv4(例如 `https://203.0.113.10`,请换成你自己的真实地址,文档示例网段会被拒绝)。可以复制受限模板手动部署,也可以填写 SSH 用户、SSH 端口和本机私钥路径,点击 **部署 frps + Caddy** 自动部署。自动部署支持 Ubuntu/Debian + systemd,使用 OpenSSH 密钥或 ssh-agent,不接受密码,也不会覆盖非 DSH Mobile 管理的 Caddyfile;部署与清理前都会展示服务器主机指纹,需到 VPS 控制台核对后才能继续。公网 IP 模式会申请约 6 天有效的 Let’s Encrypt IP 证书并配置每日自动续期。部署完成后再安装官方 `frpc` 并验证连接。不再需要服务器时可用“复制 VPS 卸载脚本”或一键清理,只删除 DSH Mobile 自己的服务与配置。需要 Android App 0.3.3 或更高版本。详见[自建 FRP 使用指南](docs/SELF_HOSTED_FRP.md)。
129
- 2. 状态变为“远程访问已就绪”后,点击 **生成远程配对二维码**。
134
+ - **自建 FRP(高级)**:展开 **自建连接**,填写 VPS、frps 端口、共享 Token 和公开 HTTPS 地址;公开地址可以是自己的域名,也可以直接是 VPS 公网 IPv4(例如 `https://203.0.113.10`,请换成你自己的真实地址,文档示例网段会被拒绝)。可以复制受限模板手动部署,也可以填写 SSH 用户、SSH 端口和本机私钥路径,点击 **部署 frps + Caddy** 自动部署。自动部署支持 Ubuntu/Debian + systemd,使用 OpenSSH 密钥或 ssh-agent,不接受密码,也不会覆盖非 DSH Mobile 管理的 Caddyfile;部署与清理前都会展示服务器主机指纹,需到 VPS 控制台核对后才能继续。公网 IP 模式会申请约 6 天有效的 Let’s Encrypt IP 证书并配置每日自动续期。部署完成后再安装官方 `frpc` 并验证连接。不再需要服务器时可用“复制 VPS 卸载脚本”或一键清理,只删除 DSH Mobile 自己的服务与配置。需要 Android App 0.3.3 或更高版本。详见 [自建 FRP 使用指南](docs/SELF_HOSTED_FRP.md)。
135
+ - **自有反向代理**:展开 **自建连接 → 自有反向代理**,填写公网 HTTPS 地址(支持自定义端口)、私有监听 IPv4、独立 HTTP 后端端口(默认 3444)和代理来源 CIDR,再点击 **保存并启动后端**。适合已有 Lucky/Nginx/Caddy 的用户,无需隧道组件;需要 Android App 0.4.0 或更高版本。详见 [自有反向代理指南](docs/SELF_HOSTED_ORIGIN.md)。
136
+ - **cloudflared**:点击 **安装官方组件**,插件在确认后从官方发布页下载固定版本到插件私有目录,随后自动申请一个临时公网地址(quick tunnel),**无需注册或登录**。适合不想注册账号的用户;quick tunnel 地址每次重连都会变化,官方定位为测试用途、有限流且无可用性保证,请勿用于必须长期可达的生产访问。已有 Cloudflare 账号和域名时,可把隧道类型切到 **命名隧道**,填入连接器令牌、公网域名与本机转发端口,即可获得重启后不变的固定地址(令牌只存私有目录、只经环境变量传给 cloudflared)。步骤见 [Cloudflare 命名隧道](docs/CLOUDFLARE_TUNNEL.md)。
137
+ 2. 状态变为“远程访问已就绪”后,点击 **生成远程配对二维码**。自有反向代理仅显示“后端已监听”:它不验证公网连通性,仍需检查代理 HTTPS、证书与 WebSocket 并用手机验收。
130
138
  3. 在 Android App 中进入 **远程访问**,扫描二维码完成独立配对。
131
139
  4. 此后 App 会保存当前地址和设备凭据并自动重连。若 cpolar 免费临时地址发生变化,请扫描电脑端当前远程二维码重新验证连接;无需清除 App 数据。旧设备 token 只会发送到原先保存的精确 Origin,不会发送给二维码中的新域名。
132
140
 
133
- > **远程通知说明**:浏览器通知权限按地址分别授权,远程地址需在浏览器中单独允许;系统级弹窗只出现在电脑上,手机收不到。任务完成要推送到手机时,请使用第三方通道(如 dsh-messager 的飞书/企业微信/Telegram 推送),它们走服务端下发,不受网关影响。
141
+ > **远程通知说明**:浏览器的 `Notification` 权限按 Origin 分别授权,网页系统通知只显示在运行该网页的设备上。Android App 的任务提醒是独立的 0.4.0 功能,需要在 App 前台菜单中主动开启,且依赖 WebView 页面仍存活;它不是通用的后台推送。需要可靠的后台推送时,请使用你已配置的服务端 webhook 或机器人通道。
134
142
 
135
- Tailscale Funnel 覆盖范围广,但在中国大陆网络下可能不稳定。其运行组件把公开监听生命周期绑定到父进程和受限控制通道;父进程退出、控制通道关闭或显式停止时会结束当前代次并清理资源。cpolar 更适合国内网络;自建 FRP 适合已有 VPS、希望避开公共服务带宽限制的用户。中国大陆 VPS 上的未备案域名可能被云厂商拦截,此时可使用公网 IPv4 模式。插件会校验按需下载的固定版本组件,配置与程序均保存在 `$DSH_HOME/mobile-access/`,可随时在面板中彻底清除。
143
+ Tailscale Funnel 覆盖范围广,但在中国大陆网络下可能不稳定。其运行组件把公开监听生命周期绑定到父进程和受限控制通道;父进程退出、控制通道关闭或显式停止时会结束当前代次并清理资源。cpolar 更适合国内网络;自建 FRP 适合已有 VPS、希望避开公共服务带宽限制的用户。cloudflared 有两种模式:快速隧道不需要账号或登录,但地址随机、每次重连都会变化,官方定位为测试用途且无可用性保证,因此只适合临时或验证场景;命名隧道使用 Cloudflare 账号令牌,能保留重启后不变的固定公网域名。中国大陆 VPS 上的未备案域名可能被云厂商拦截,此时可使用公网 IPv4 模式。插件会校验按需下载的固定版本组件,配置与程序均保存在 `$DSH_HOME/mobile-access/`,可随时在面板中彻底清除。
136
144
 
137
145
  自建 FRP 只生成一个指向 DSH 回环网关的 HTTP vhost,不提供任意 FRP 配置、TCP/UDP 代理或 FRP 插件。VPS 的明文 vhost 必须只监听 `127.0.0.1`,由 Caddy 提供公网 HTTPS;插件会拒绝可从公网访问的明文端口,并在公开发现接口确认连接到当前电脑后才显示“已就绪”。
138
146
 
139
- 远程公开地址仍受 DSH 设备配对保护。内置 Funnel 与托管 cpolar 当前支持 Windows x64;按需安装的 FRP 0.70.1 支持 Windows、Linux、macOS 的 x64 与 arm64。
147
+ 自有反向代理的 HTTP 后端只允许留在可信私网;**不要把它映射到公网,也不要绕过它直连 DSH 或现有 LAN 3443**。来源 CIDR 匹配代理的直接 TCP 来源,不信任转发头;反代须保留外部 Host(含端口)、Origin、Cookie 和 WebSocket。清除代理配置不会删除已配对远程设备。
148
+
149
+ 远程公开地址仍受 DSH 设备配对保护。内置 Funnel 与托管 cpolar、cloudflared 当前支持 Windows x64;按需安装的 FRP 0.70.1 支持 Windows、Linux、macOS 的 x64 与 arm64。
140
150
 
141
151
  ## 扩展与自定义
142
152
 
@@ -156,7 +166,9 @@ Tailscale Funnel 覆盖范围广,但在中国大陆网络下可能不稳定。
156
166
 
157
167
  扩展清单及其脚本、样式和资源按版本标识刷新。插件监听到 `/mobile` 或扩展文件变化后,会通过已认证连接通知手机立即更新;页面可见时每 45 秒、隐藏时每 5 分钟的检查只作为断线兜底。Host 暂存失败会继续使用现有版本;若 Host 已更新但新手机界面激活失败,则关闭该扩展并自动重试,避免混用新旧能力。
158
168
 
159
- <sub>你甚至可以通过扩展连接电脑上运行的酒馆(</sub>
169
+ 扩展动作的 `input` 可以直接使用 `api.schema.object(...)` 等 Schemastery schema,也可以使用带 `parse(value)` 的适配器;手机端 `api.host.invoke()` 会按 JSON 请求发送,输入会在电脑端动作执行前完成校验和规范化。
170
+
171
+ <sub>你甚至可以通过扩展连接电脑上运行的酒馆,并从同一 App 打开它的简易移动前端。</sub>
160
172
 
161
173
  > `host.mjs` 与本机程序拥有相同权限;仅创建和运行你理解并信任的电脑端扩展。
162
174
 
@@ -171,14 +183,16 @@ Tailscale Funnel 覆盖范围广,但在中国大陆网络下可能不稳定。
171
183
 
172
184
  ### 设备管理
173
185
 
174
- Android App 将局域网、cpolar、Tailscale Funnel 和自建 FRP 统一整理到“已配对设备”列表。首次升级会自动迁移旧版局域网与远程凭据,不要求重新配对;地址变化时按 DSH 安装的稳定 `instanceId` 合并原记录,保留自定义名称。设备 Token 和局域网 CA 继续由 Android Keystore 加密保存,不会显示在列表或二维码中。
186
+ Android App 将局域网、cpolar、cloudflared、Tailscale Funnel 和自建 FRP 统一整理到“已配对设备”列表。首次升级会自动迁移旧版局域网与远程凭据,不要求重新配对;地址变化时按 DSH 安装的稳定 `instanceId` 合并原记录,保留自定义名称。设备 Token 和局域网 CA 继续由 Android Keystore 加密保存,不会显示在列表或二维码中。
175
187
 
176
188
  每条记录显示自定义名称、连接方式、Origin、实时可达状态和最近连接时间。绿色状态点表示“可达”,灰色状态点表示“检测中”“暂不可达”“配对已过期”或“电脑端已移除”;可达性检查直接验证 DSH Gateway,不依赖 ICMP,也不会把暂时断网误判成电脑端撤销。
177
189
 
178
190
  - **启动时打开 → 直接进入 DSH**(默认):单设备直接连接;多设备优先连接上次使用的设备,其次按最近连接时间选择仍有效的设备。连接超过有限重试预算后自动回到列表,不会无限转圈。
179
191
  - **启动时打开 → 显示设备列表**:每次启动先选择电脑,适合经常在多台设备之间切换。该选项位于列表右上角的设置按钮中,修改后立即保存。
180
192
  - 点按设备行可连接;右侧“…”和长按提供相同的操作面板,可编辑名称、立即检测、重新配对或删除本地记录。删除前会二次确认,并提供短暂撤销;撤销只恢复本机记录,不恢复电脑端已经撤销的授权。
181
- - 在 WebView 页面打开 DSH **设置 → 通用**,选择 **切换电脑** 可回到已配对设备列表;该动作仅在 Android App 中显示。电脑端撤销设备后,App 保留该条目并显示“电脑端已移除”,停止自动重连,同时提供 **重新配对** 和 **删除本地记录**。
193
+ - 在 WebView 页面打开 DSH **设置 → 通用**,选择 **切换电脑** 可回到已配对设备列表;该动作仅在 Android App 中显示。在线收到电脑端撤销通知后,App 保留该条目并显示“电脑端已移除”,停止自动重连,同时提供 **重新配对** 和 **删除本地记录**。
194
+
195
+ 电脑端撤销会永久删除持久化存储中的设备记录及令牌摘要,而不是保留 `revokedAt` 标记;启动时也会清理旧版留下的已撤销记录。删除后,旧令牌与未知令牌一样返回 `401 authentication_failed`。现有 App 若在离线期间被撤销,再次检测时可能显示“配对已过期”,需要重新配对;在线会话仍会收到撤销通知并立即断开。
182
196
 
183
197
  <table>
184
198
  <tr>
@@ -199,8 +213,10 @@ Android App 将局域网、cpolar、Tailscale Funnel 和自建 FRP 统一整理
199
213
 
200
214
  兼容条件与 WebSocket 放行规则:
201
215
 
202
- - 本版本已按 DSH `0.1.5-rc.2` 的 renderer-v2 合同检查,并保留对 `0.1.5-rc.1` 局域网路径的验证。DSH 页面需要提供标准的会话、`main`/`panelInfo` 和 `rightbar` 插槽;插件自身需要通过 DSH 的标准面板或侧边栏入口注册内容。
203
- - 网关默认只允许 DSH 内置的第一方 WebSocket 路径。社区侧边栏插件使用的其他路径默认拦截,通常会在诊断页显示为待处理项目;截图中的`/sidebar/ws/agent-terminals` 就属于这类需要按实际插件确认的路径。
216
+ 代理页面为兼容部分社区插件允许嵌入 HTTP 页面;这类内容未加密,可能被篡改,浏览器也可能因混合内容策略拦截。处理敏感内容时请使用 HTTPS。通过 HTTPS 管理入口打开远程面板时,页面顶部会显示相同提醒。
217
+
218
+ - 已发布的 0.4.0 按 DSH `0.1.5-rc.2` 做过 renderer-v2 合同检查,并保留对 `0.1.5-rc.1` 局域网路径的验证。DSH 页面需要提供标准的会话、`main`/`panelInfo` 和 `rightbar` 插槽;插件自身需要通过 DSH 的标准面板或侧边栏入口注册内容。
219
+ - 网关默认只允许 DSH 内置的第一方 WebSocket 路径。社区侧边栏插件使用的其他路径默认拦截,通常会在诊断页显示为待处理项目;截图中的`/sidebar/ws/agent-opens` 和`/sidebar/ws/agent-terminals` 就属于这类需要按实际插件确认的路径。
204
220
  - 在 **连接诊断 → 第三方 WebSocket 路径** 中,只对确认过的精确路径点击 **允许**。系统不接受带查询字符串或模糊前缀的路径;不建议使用“全部允许”。已允许的路径可以随时移除,局域网和远程连接使用同一套规则。
205
221
  - 放行只代表该路径可以通过已认证、同源的 DSH Mobile 网关,不会开放任意 TCP/UDP 端口,也不会绕过设备配对。若社区插件仍然连接失败,先看诊断页的实际拦截路径,再按一条路径放行。
206
222
 
@@ -227,6 +243,8 @@ Android App 将局域网、cpolar、Tailscale Funnel 和自建 FRP 统一整理
227
243
 
228
244
  Android App 只是 Kotlin WebView 薄壳,不内置另一份网页;手机浏览器访问的是同一页面。需要排查兼容性时,可在浏览器地址后追加 `?frontend=stock`,临时回到旧的桌面页面适配模式。
229
245
 
246
+ 专用移动页面会在 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。
247
+
230
248
  > **社区客户端(非官方)**:[微信小程序客户端](https://github.com/StrawberryAO/dsh-mobile-minapp)
231
249
  > 原生微信小程序实现,复用「移动访问」的配对与 Remote 流协议(需 dsh-mobile ≥ 0.3.8)。
232
250
  > 因微信正式版强制「合法域名」(需 ICP 备案的自有 HTTPS 域名),目前需通过微信开发者工具 / 真机调试使用,详见其 README。
@@ -250,6 +268,7 @@ flowchart LR
250
268
  - 局域网监听只用于可信家庭、办公网络或可信热点;不要自行做端口转发。
251
269
  - 远程地址可从公网到达,但未配对请求无法进入 DSH;不使用时应关闭远程开关。
252
270
  - cpolar 仅在用户确认后下载固定官方版本并校验大小和 SHA-256;不会安装系统服务、写入 PATH 或设置开机启动,插件清理会删除其托管文件。
271
+ - cloudflared 同样仅在用户确认后从官方 GitHub Release 下载固定版本并校验精确大小和 SHA-256,且启动时关闭自动更新,以保证运行的始终是已校验的那份二进制;不需要账号、Token 或 DNS 记录,不写入任何凭据,清理时删除插件托管的全部文件。
253
272
  - 自建 FRP 仅在用户确认后从官方 Release 下载固定版本 `frpc`,校验来源、精确大小、SHA-256、压缩包路径和可执行文件版本;共享 Token 不会出现在状态、诊断或日志中。复制服务器模板时 Token 会进入系统剪贴板,请粘贴后及时清除;本机清理只删除插件管理的文件,VPS 需要用面板提供的卸载脚本或一键清理单独清除。自动部署与一键清理前都会展示 SSH 主机指纹,必须到 VPS 控制台核对后才能继续。
254
273
  - 配对设备拥有控制电脑端 DeepSeek Harness 的能力,应视为完全可信设备;丢失手机后应在电脑端撤销设备。
255
274
  - 移动网关开启时才监听局域网;关闭后 DeepSeek Harness 仍正常在电脑本机运行。
@@ -263,6 +282,8 @@ flowchart LR
263
282
 
264
283
  | DSH Mobile 插件 | 验证支持的 DeepSeek Harness 版本 |
265
284
  | ----------------------------------------- | -------------------------------------------------------------- |
285
+ | `0.4.2` | `0.1.6-alpha.1`(本机源码与 renderer-v2 契约检查) |
286
+ | `0.4.1` | `0.1.6-alpha.1`(本机源码与 renderer-v2 契约检查) |
266
287
  | `0.3.15`、`0.3.16`、`0.4.0` | `0.1.5-rc.2`(契约检查);`0.1.5-rc.1`(@idoall 局域网实测) |
267
288
  | `0.3.14` | `0.1.3-alpha.2` |
268
289
  | `0.3.9`-`0.3.12` | `0.1.3-alpha.1` |
@@ -295,6 +316,6 @@ npm ci
295
316
  npm run verify
296
317
  ```
297
318
 
298
- Android 构建见 [App 文档](apps/mobile/README.zh-CN.md)。
319
+ Android 构建见 [App 文档](https://github.com/saya-ch/dsh-mobile/blob/main/apps/mobile/README.zh-CN.md)。
299
320
 
300
321
  Apache-2.0,详见 [LICENSE](LICENSE)。
package/SECURITY.md CHANGED
@@ -16,6 +16,7 @@ The maintainer will acknowledge a complete report within seven days. Publication
16
16
 
17
17
  - Keep the ordinary DSH Web listener on loopback.
18
18
  - Expose only the plugin-owned HTTPS listener to the LAN.
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.
19
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.
20
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.
21
22
  - Public remote origins provided by Funnel, cpolar, or self-hosted FRP 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.
@@ -33,6 +34,7 @@ The maintainer will acknowledge a complete report within seven days. Publication
33
34
  - 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.
34
35
  - 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.
35
36
  - 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.
37
+ - 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.
36
38
 
37
39
  ## Known limitation
38
40
 
@@ -6,6 +6,35 @@ The optional cpolar component is not included in the npm package or Android App.
6
6
 
7
7
  The optional [FRP](https://github.com/fatedier/frp) client is not included in the npm package or Android App. When a user explicitly chooses self-hosted FRP installation, DSH Mobile downloads the pinned official `frpc` 0.70.1 archive, verifies its origin, exact size, SHA-256 digest, archive paths, and executable version, then stores only `frpc` under the user's DSH Mobile data directory. FRP is distributed under the Apache License 2.0.
8
8
 
9
+ 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 `cloudflared-windows-amd64.exe` release asset (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 and no account credential is requested or stored. 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
+
9
11
  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`, and [selfsigned](https://github.com/jfromaniello/selfsigned) `^5.5.0`. These packages are distributed under the MIT License; the release checkout's exact transitive versions appear in the attached CycloneDX npm SBOM.
10
12
 
11
13
  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.12.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
+
15
+ ## core-js browser compatibility bundle
16
+
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:
18
+
19
+ ```text
20
+ Copyright (c) 2013–2025 Denis Pushkarev (zloirock.ru)
21
+ Copyright (c) 2025–2026 CoreJS Company (core-js.io)
22
+
23
+ Permission is hereby granted, free of charge, to any person obtaining a copy
24
+ of this software and associated documentation files (the "Software"), to deal
25
+ in the Software without restriction, including without limitation the rights
26
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27
+ copies of the Software, and to permit persons to whom the Software is
28
+ furnished to do so, subject to the following conditions:
29
+
30
+ The above copyright notice and this permission notice shall be included in
31
+ all copies or substantial portions of the Software.
32
+
33
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39
+ THE SOFTWARE.
40
+ ```