dsh-mobile 0.3.15 → 0.4.0
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 +30 -0
- package/README.en.md +62 -10
- package/README.md +72 -19
- package/lib/cli.js +96 -81
- package/lib/cli.js.map +1 -1
- package/lib/client.js +736 -89
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +86 -2
- package/lib/index.mjs +653 -118
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-layout.js +89 -34
- package/lib/mobile-layout.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
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.
|
|
8
|
+
|
|
9
|
+
## 0.4.0 - 2026-09-14
|
|
10
|
+
|
|
11
|
+
- Add multi-device management to the Android app: migrate existing LAN and remote credentials, show each paired computer with its Origin and HTTPS reachability state, connect the most recently used valid device on direct startup, and provide list, rename, re-pair, delete, and launch-behavior controls.
|
|
12
|
+
- Complete plugin-side localization for Chinese, English, and Italian, including diagnostic report copy, browser pairing pages, and reauthentication guidance selected from `Accept-Language`; Android resources remain key-complete across all three supported locales.
|
|
13
|
+
- 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
|
+
- 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
|
+
- 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.
|
|
17
|
+
- 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
|
+
- Show and re-copy remote pairing links without invalidating the QR code's active one-time pairing window (thanks @qzyqmzn for PR #75).
|
|
19
|
+
- 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).
|
|
20
|
+
- Keep native DSH file selection in the composer Add group, add only camera capture there, and reuse the native menu-row styling for the mobile action.
|
|
21
|
+
- Add a native-app-only Switch computer action to the WebView's DSH General settings page, keeping the Android device-list settings and DSH content toolbar focused on their own controls.
|
|
22
|
+
- Hide the Android shell toolbar above the WebView so DSH occupies the full safe viewport; the system status-bar color continues to follow the page background.
|
|
23
|
+
|
|
24
|
+
## 0.3.16 - 2026-09-12
|
|
25
|
+
|
|
26
|
+
- Provide legacy dsh-web community pane markers and a collapsed-rail marker on the dedicated layout so DOM-mounting plugins such as `@linxin666/dsh-client-ui-task-board` can inject their sidebar entry and panel (thanks @idoall for PR #67).
|
|
27
|
+
- Preserve native rightbar track/fullscreen requests while shrinking or overlaying the panel whenever docking would leave less than 400 px for the conversation. The dedicated layout no longer consumes the retired Better Sidebar width variable (thanks @idoall for PR #66).
|
|
28
|
+
- Follow the active DSH WebServer port for LAN and remote proxy authentication, including managed and legacy setup files that saved an older 3080 upstream. The independently configurable Mobile HTTPS listener remains unchanged (thanks @CharlesLueng for #68).
|
|
29
|
+
- Limit the full-width question and plan-review adaptations to screens at most 600 px wide so desktop remote browsers retain DSH's native card width (thanks @idoall for #64).
|
|
30
|
+
|
|
31
|
+
### Contributors
|
|
32
|
+
|
|
33
|
+
- @idoall contributed PRs #66 and #67 and reported #64: responsive rightbar docking, legacy dsh-web community pane compatibility, and the question-card reproduction.
|
|
34
|
+
- @CharlesLueng reported #68 and identified the custom DSH Web port path that still targeted the saved default upstream.
|
|
5
35
|
|
|
6
36
|
## 0.3.15 - 2026-09-11
|
|
7
37
|
|
package/README.en.md
CHANGED
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
|
|
20
20
|
> DSH Mobile is a DeepSeek Harness community plugin; the native app supports Android only.
|
|
21
21
|
>
|
|
22
|
-
> **0.
|
|
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).
|
|
23
23
|
>
|
|
24
|
-
> **Upgrade reminder**:
|
|
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).
|
|
25
25
|
|
|
26
26
|
<p align="center">
|
|
27
|
-
<a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.
|
|
28
|
-
<a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.
|
|
29
|
-
<sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.
|
|
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>
|
|
30
30
|
</p>
|
|
31
31
|
|
|
32
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.
|
|
@@ -40,7 +40,7 @@ It also lets you customize the phone from a DSH conversation: `/mobile <what you
|
|
|
40
40
|
- **Continue DSH work from a phone**: the same sessions, Workspaces, messages, and tools, in real time.
|
|
41
41
|
- **Customize the phone UI by talking to DSH**: change the mobile layout, interactions, and features from a conversation; open pages refresh within seconds.
|
|
42
42
|
- **A dedicated touch layout**: session drawer, tool details, settings, question cards, and composer reorganized for phones. Native app screens follow the Android system locale in Simplified Chinese, English, or Italian. Plugin-owned Web UI follows DSH's selected locale; Italian resources are ready for a future DSH Italian locale.
|
|
43
|
-
- **Image attachments**:
|
|
43
|
+
- **Image attachments**: file selection uses DSH's native **Add** group; DSH Mobile only adds **Take photo** to that group, and captured images follow DSH's native attachment flow.
|
|
44
44
|
- **Auto-discovery, no re-pairing**: Wi-Fi, hotspot, or IP changes normally recover automatically.
|
|
45
45
|
- **One-click connection diagnostics**: check versions, gateway, network interface, firewall, and the remote path; stable reason codes are localized in the UI, and the copied report excludes credentials and complete addresses.
|
|
46
46
|
- **One-click approval for third-party plugin WebSockets**: the diagnostics view groups blocked plugin connections by directory (with attempt counts); allowing a path unblocks that exact path while everything unapproved stays blocked, and a red badge marks the sidebar entry until reviewed (#47). If a plugin keeps failing to connect (for example a terminal reporting 1006), first look at the diagnostics view for blocked connections and approve them in one click — manual configuration is usually unnecessary.
|
|
@@ -74,7 +74,7 @@ Or via the plugin market (optional):
|
|
|
74
74
|
dsh plugin --profile web add dshmarket
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
Restart DSH, then search for **dsh-mobile** under **Settings → Plugin Market** and install it
|
|
77
|
+
Restart DSH, then search for **dsh-mobile** under **Settings → Plugin Market** and install it. On first opening Mobile access, the Local network page lists this computer's current networks; confirm one to create private certificates and LAN configuration, then restart DSH once as prompted. No terminal `setup` command is required for this path.
|
|
78
78
|
|
|
79
79
|
`setup` automatically selects and remembers the current LAN; Wi-Fi, hotspot, and IP changes normally recover without re-pairing. Use `--address 192.168.x.x` only when automatic selection fails. Settings, certificates, devices, and customization files live under `$DSH_HOME/mobile-access/`.
|
|
80
80
|
|
|
@@ -99,8 +99,12 @@ Use this when the phone and computer share Wi-Fi, Ethernet, or a phone hotspot.
|
|
|
99
99
|
3. In the Android app, open **Local network**, scan for computers, select the device, then scan the QR code or paste the pairing key.
|
|
100
100
|
4. Pairing creates persistent device trust. Later app launches discover and connect automatically; Wi-Fi, hotspot, and DHCP address changes normally do not require pairing again.
|
|
101
101
|
|
|
102
|
+
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
|
+
|
|
102
104
|
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.
|
|
103
105
|
|
|
106
|
+
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
|
+
|
|
104
108
|
### Remote access
|
|
105
109
|
|
|
106
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.
|
|
@@ -158,6 +162,54 @@ The examples above, applied:
|
|
|
158
162
|
<img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/cyberpunk-monitor-1.png" width="22%" style="margin-left:8px" alt="Mobile UI customized into a cyberpunk computer monitor">
|
|
159
163
|
</p>
|
|
160
164
|
|
|
165
|
+
### Device management
|
|
166
|
+
|
|
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.
|
|
168
|
+
|
|
169
|
+
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
|
+
|
|
171
|
+
- **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
|
+
- **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
|
+
- 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**.
|
|
175
|
+
|
|
176
|
+
<table>
|
|
177
|
+
<tr>
|
|
178
|
+
<td align="center" valign="top" width="50%">
|
|
179
|
+
<img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/device-management.jpg" width="44%" alt="Paired computer list in the Android app"><br>
|
|
180
|
+
<sub>Device management: paired computers, transport, and reachability</sub>
|
|
181
|
+
</td>
|
|
182
|
+
<td align="center" valign="top" width="50%">
|
|
183
|
+
<img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/startup-behavior.jpg" width="44%" alt="Startup behavior settings in the Android app"><br>
|
|
184
|
+
<sub>Startup behavior: open DSH directly or show the device list</sub>
|
|
185
|
+
</td>
|
|
186
|
+
</tr>
|
|
187
|
+
</table>
|
|
188
|
+
|
|
189
|
+
### Third-party plugin compatibility
|
|
190
|
+
|
|
191
|
+
The mobile adaptation keeps DSH's existing Workspace, task-management, terminal, and file-panel entry points instead of isolating third-party plugin content in a separate page. The wide-layout screenshot below shows the Android app in a wide viewport. The app adapts to the available width: phones use drawers and overlays, while wide screens use side-by-side panels; both layouts expose the same features and connection methods. DSH still loads third-party plugins itself—the mobile layer only adapts layout and access, without modifying DeepSeek Harness source.
|
|
192
|
+
|
|
193
|
+
Compatibility and WebSocket rules:
|
|
194
|
+
|
|
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.
|
|
196
|
+
- 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
|
+
- 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
|
+
- 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.
|
|
199
|
+
|
|
200
|
+
<table>
|
|
201
|
+
<tr>
|
|
202
|
+
<td align="center" valign="top" width="50%">
|
|
203
|
+
<img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/third-party-plugin-adaptation.png" width="96%" alt="Android app wide layout with a community sidebar plugin adapted"><br>
|
|
204
|
+
<sub>Community sidebar plugin compatibility in the Android app</sub>
|
|
205
|
+
</td>
|
|
206
|
+
<td align="center" valign="top" width="50%">
|
|
207
|
+
<img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/websocket-diagnostics.png" width="96%" alt="Third-party WebSocket path diagnostics in the Android app"><br>
|
|
208
|
+
<sub>Connection diagnostics: review and allow third-party WebSocket paths</sub>
|
|
209
|
+
</td>
|
|
210
|
+
</tr>
|
|
211
|
+
</table>
|
|
212
|
+
|
|
161
213
|
## App or mobile browser
|
|
162
214
|
|
|
163
215
|
| Client | Best for | Notes |
|
|
@@ -182,7 +234,7 @@ flowchart LR
|
|
|
182
234
|
Gateway -->|"loopback proxy"| DSH["Stock DSH Web and Host"]
|
|
183
235
|
```
|
|
184
236
|
|
|
185
|
-
Three layers: the Host face for discovery, pairing, HTTPS, loopback proxying, and extension registration; the Client face for the dedicated mobile layout and extension SDK; and the Android app for a narrow native bridge. The bridge uses `androidx.webkit` WebMessage, verifies the exact top-level origin and main frame on every bounded message, and never uses `addJavascriptInterface`. Neither the DeepSeek Harness source nor its
|
|
237
|
+
Three layers: the Host face for discovery, pairing, HTTPS, loopback proxying, and extension registration; the Client face for the dedicated mobile layout and extension SDK; and the Android app for a narrow native bridge. The bridge uses `androidx.webkit` WebMessage, verifies the exact top-level origin and main frame on every bounded message, and never uses `addJavascriptInterface`. Neither the DeepSeek Harness source nor its active Web page (port 3080 by default, configurable through `dsh web --port`) is modified.
|
|
186
238
|
|
|
187
239
|
## Security
|
|
188
240
|
|
|
@@ -201,7 +253,7 @@ The table below lists, for each plugin version, the DeepSeek Harness version it
|
|
|
201
253
|
|
|
202
254
|
| DSH Mobile plugin | Verified DeepSeek Harness version |
|
|
203
255
|
| --- | --- |
|
|
204
|
-
| `0.3.15` | `0.1.5-rc.2` (contract check); `0.1.5-rc.1` (@idoall LAN verification) |
|
|
256
|
+
| `0.3.15`, `0.3.16`, `0.4.0` | `0.1.5-rc.2` (contract check); `0.1.5-rc.1` (@idoall LAN verification) |
|
|
205
257
|
| `0.3.14` | `0.1.3-alpha.2` |
|
|
206
258
|
| `0.3.9`-`0.3.12` | `0.1.3-alpha.1` |
|
|
207
259
|
| `0.3.6`-`0.3.8` | `0.1.2-rc.1` |
|
|
@@ -209,7 +261,7 @@ The table below lists, for each plugin version, the DeepSeek Harness version it
|
|
|
209
261
|
| `0.3.0`-`0.3.3` | `0.1.2-alpha.1` |
|
|
210
262
|
| `0.1.4`, `0.2.x` | `0.1.1-rc.2` |
|
|
211
263
|
|
|
212
|
-
Existing 0.3.3–0.
|
|
264
|
+
Existing 0.3.3–0.4.0 apps do not need re-pairing. cpolar users should use app 0.3.15 or later because earlier apps may time out before a slow first load over the free route finishes; earlier apps also use a different status-bar strategy. The 0.4.0 app adds the multi-device list, startup behavior, and computer-side revocation status; older apps continue to connect to their saved single device. App 0.1.3 or earlier requires reinstalling and pairing again.
|
|
213
265
|
|
|
214
266
|
## Uninstall
|
|
215
267
|
|
package/README.md
CHANGED
|
@@ -26,14 +26,14 @@
|
|
|
26
26
|
|
|
27
27
|
> DSH Mobile 是 DeepSeek Harness 社区插件,原生 App 仅支持 Android。
|
|
28
28
|
>
|
|
29
|
-
> **0.
|
|
29
|
+
> **0.4.0 更新**:加入多设备管理、电脑端撤销状态同步、无 Session 可达性检查;完善插件市场局域网引导、任务状态通知、原生 WebView 沉浸布局和三语配对页。[详细记录](CHANGELOG.md)。
|
|
30
30
|
>
|
|
31
|
-
> **升级提醒**:
|
|
31
|
+
> **升级提醒**:0.4.0 需要同步更新插件与 Android App 才能使用多设备列表;旧 App 仍可使用原有单设备配对。[兼容说明](#兼容性)。
|
|
32
32
|
|
|
33
33
|
<p align="center">
|
|
34
|
-
<a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.
|
|
35
|
-
<a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.
|
|
36
|
-
<sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.
|
|
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>
|
|
37
37
|
</p>
|
|
38
38
|
|
|
39
39
|
DSH Mobile 是一个 DeepSeek Harness 插件,让手机浏览器或 Android App 通过局域网,或可选的 Tailscale Funnel、cpolar、自建 FRP 远程通道连接电脑,继续使用同一份会话、工作区、消息和工具。局域网与远程访问分别启停、分别管理设备,且都不修改 DeepSeek Harness 源码。
|
|
@@ -47,7 +47,7 @@ DSH Mobile 是一个 DeepSeek Harness 插件,让手机浏览器或 Android App
|
|
|
47
47
|
- **在手机上继续电脑端的工作**:同一份会话、工作区、消息和工具,实时同步。
|
|
48
48
|
- **用对话定制手机端**:直接在 DSH 对话里改手机页面的布局、交互和功能,几秒内刷新。
|
|
49
49
|
- **专属触屏布局**:会话抽屉、工具详情、设置、提问卡片和输入栏都按手机重新组织。App 原生页面跟随系统显示简体中文、英文或意大利文;插件界面跟随 DSH 的语言设置,意大利语资源已为 DSH 后续支持预留。
|
|
50
|
-
-
|
|
50
|
+
- **图片附件**:文件选择使用 DSH 原生“添加”组;DSH Mobile 只在该组补充“拍照”,拍摄结果按 DSH 原生附件流程发送。
|
|
51
51
|
- **自动发现、无需重新配对**:切换 Wi-Fi、热点或 IP 后通常自动恢复。
|
|
52
52
|
- **一键连接诊断**:检查版本、网关、网卡、防火墙和远程通道;稳定的原因码在界面中本地化,并生成不含凭据与完整地址的脱敏报告。
|
|
53
53
|
- **第三方插件 WebSocket 一键放行**:诊断页按目录分组记录被拦截的插件连接(含次数),点允许即放行确切路径,未批准的一律拦截;有新拦截时侧栏红点提醒(#47)。若某插件的连接一直失败(如终端报 1006),先到诊断页看看有没有被拦的连接,一键放行即可,通常无需手动配置。
|
|
@@ -81,7 +81,7 @@ pnpm dsh --profile web
|
|
|
81
81
|
dsh plugin --profile web add dshmarket
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
重启 DSH 后,在 **设置 → 插件市场** 里搜索 dsh-mobile
|
|
84
|
+
重启 DSH 后,在 **设置 → 插件市场** 里搜索 dsh-mobile 并安装。首次打开“移动访问”时,局域网页会列出电脑当前网络;确认网卡后由插件生成私有证书并配置局域网,按提示重启一次 DSH 即可使用,无需再打开终端运行 `setup`。
|
|
85
85
|
|
|
86
86
|
`setup` 会自动选择并记住当前局域网,切换 Wi-Fi、热点或 IP 后通常自动恢复;仅在自动选择失败时使用 `--address 192.168.x.x`。设置、证书、设备和自定义文件保存在 `$DSH_HOME/mobile-access/`。
|
|
87
87
|
|
|
@@ -106,8 +106,12 @@ dsh plugin --profile web add dshmarket
|
|
|
106
106
|
3. 在 Android App 中进入 **局域网访问**,扫描发现电脑并点击设备,再扫描二维码或粘贴配对密钥。
|
|
107
107
|
4. 配对完成后会建立持久设备信任。以后打开 App 会自动发现并连接,切换 Wi-Fi、热点或 DHCP 地址通常不需要重新配对。
|
|
108
108
|
|
|
109
|
+
端口说明:`dsh web --port` 修改 DSH Web 上游端口(默认 3080),插件会自动跟随;`dsh-mobile setup --port` 修改 Mobile HTTPS 监听端口(默认 3443),配对二维码会包含实际端口。
|
|
110
|
+
|
|
109
111
|
不安装 App 也可以访问:点击 **复制配对链接**,在手机浏览器中打开;首次访问需要按浏览器提示手动信任插件证书。
|
|
110
112
|
|
|
113
|
+
手机浏览器中的配对和重新连接页面会按浏览器的 `Accept-Language` 显示简体中文、英文或意大利文;Android App 则跟随系统语言。DSH 内的插件控制面板继续跟随 DSH 当前语言。
|
|
114
|
+
|
|
111
115
|
### 远程访问
|
|
112
116
|
|
|
113
117
|
适合手机离开电脑所在网络后使用。远程访问默认关闭,手机不需要另外安装 Tailscale、cpolar 或 FRP。
|
|
@@ -165,6 +169,54 @@ Tailscale Funnel 覆盖范围广,但在中国大陆网络下可能不稳定。
|
|
|
165
169
|
<img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/cyberpunk-monitor-1.png" width="22%" style="margin-left:8px" alt="/mobile 定制为赛博朋克监控面板">
|
|
166
170
|
</p>
|
|
167
171
|
|
|
172
|
+
### 设备管理
|
|
173
|
+
|
|
174
|
+
Android App 将局域网、cpolar、Tailscale Funnel 和自建 FRP 统一整理到“已配对设备”列表。首次升级会自动迁移旧版局域网与远程凭据,不要求重新配对;地址变化时按 DSH 安装的稳定 `instanceId` 合并原记录,保留自定义名称。设备 Token 和局域网 CA 继续由 Android Keystore 加密保存,不会显示在列表或二维码中。
|
|
175
|
+
|
|
176
|
+
每条记录显示自定义名称、连接方式、Origin、实时可达状态和最近连接时间。绿色状态点表示“可达”,灰色状态点表示“检测中”“暂不可达”“配对已过期”或“电脑端已移除”;可达性检查直接验证 DSH Gateway,不依赖 ICMP,也不会把暂时断网误判成电脑端撤销。
|
|
177
|
+
|
|
178
|
+
- **启动时打开 → 直接进入 DSH**(默认):单设备直接连接;多设备优先连接上次使用的设备,其次按最近连接时间选择仍有效的设备。连接超过有限重试预算后自动回到列表,不会无限转圈。
|
|
179
|
+
- **启动时打开 → 显示设备列表**:每次启动先选择电脑,适合经常在多台设备之间切换。该选项位于列表右上角的设置按钮中,修改后立即保存。
|
|
180
|
+
- 点按设备行可连接;右侧“…”和长按提供相同的操作面板,可编辑名称、立即检测、重新配对或删除本地记录。删除前会二次确认,并提供短暂撤销;撤销只恢复本机记录,不恢复电脑端已经撤销的授权。
|
|
181
|
+
- 在 WebView 页面打开 DSH **设置 → 通用**,选择 **切换电脑** 可回到已配对设备列表;该动作仅在 Android App 中显示。电脑端撤销设备后,App 保留该条目并显示“电脑端已移除”,停止自动重连,同时提供 **重新配对** 和 **删除本地记录**。
|
|
182
|
+
|
|
183
|
+
<table>
|
|
184
|
+
<tr>
|
|
185
|
+
<td align="center" valign="top" width="50%">
|
|
186
|
+
<img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/device-management.jpg" width="44%" alt="Android App 已配对设备列表"><br>
|
|
187
|
+
<sub>设备管理列表:查看已配对电脑、连接方式和可达状态</sub>
|
|
188
|
+
</td>
|
|
189
|
+
<td align="center" valign="top" width="50%">
|
|
190
|
+
<img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/startup-behavior.jpg" width="44%" alt="Android App 启动行为设置"><br>
|
|
191
|
+
<sub>启动行为设置:选择直接进入 DSH 或显示设备列表</sub>
|
|
192
|
+
</td>
|
|
193
|
+
</tr>
|
|
194
|
+
</table>
|
|
195
|
+
|
|
196
|
+
### 第三方插件适配
|
|
197
|
+
|
|
198
|
+
移动适配保持 DSH 原有的工作区、任务管理、终端和文件面板入口,不会把第三方插件内容隔离成另一套页面。下面的宽屏截图展示 Android App 在宽屏下的布局。App 会根据屏幕宽度自适应:手机使用抽屉和浮层,宽屏使用并排面板;两种布局共享相同的功能和连接方式。第三方插件仍由 DSH 自己加载,移动层负责适配布局与连接,不修改 DeepSeek Harness 源码。
|
|
199
|
+
|
|
200
|
+
兼容条件与 WebSocket 放行规则:
|
|
201
|
+
|
|
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` 就属于这类需要按实际插件确认的路径。
|
|
204
|
+
- 在 **连接诊断 → 第三方 WebSocket 路径** 中,只对确认过的精确路径点击 **允许**。系统不接受带查询字符串或模糊前缀的路径;不建议使用“全部允许”。已允许的路径可以随时移除,局域网和远程连接使用同一套规则。
|
|
205
|
+
- 放行只代表该路径可以通过已认证、同源的 DSH Mobile 网关,不会开放任意 TCP/UDP 端口,也不会绕过设备配对。若社区插件仍然连接失败,先看诊断页的实际拦截路径,再按一条路径放行。
|
|
206
|
+
|
|
207
|
+
<table>
|
|
208
|
+
<tr>
|
|
209
|
+
<td align="center" valign="top" width="50%">
|
|
210
|
+
<img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/third-party-plugin-adaptation.png" width="96%" alt="Android App 宽屏布局与社区侧边栏插件适配"><br>
|
|
211
|
+
<sub>Android App 下社区侧边栏插件的兼容</sub>
|
|
212
|
+
</td>
|
|
213
|
+
<td align="center" valign="top" width="50%">
|
|
214
|
+
<img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/websocket-diagnostics.png" width="96%" alt="Android App 第三方 WebSocket 路径诊断"><br>
|
|
215
|
+
<sub>连接诊断:检查第三方 WebSocket 路径并按需放行</sub>
|
|
216
|
+
</td>
|
|
217
|
+
</tr>
|
|
218
|
+
</table>
|
|
219
|
+
|
|
168
220
|
## App 与手机浏览器
|
|
169
221
|
|
|
170
222
|
|
|
@@ -191,7 +243,7 @@ flowchart LR
|
|
|
191
243
|
DSH -->|"同一工作区、会话和事件流"| Phone
|
|
192
244
|
```
|
|
193
245
|
|
|
194
|
-
插件包含三层:Host face 负责发现、配对、HTTPS、回环代理和扩展注册表;Client face 提供独立的移动布局与扩展 SDK;Android App 提供受限的原生 Bridge。Bridge 使用 `androidx.webkit` WebMessage,每条消息都校验精确顶层 Origin 和主 Frame,并限制消息大小;不使用 `addJavascriptInterface`。DeepSeek Harness
|
|
246
|
+
插件包含三层:Host face 负责发现、配对、HTTPS、回环代理和扩展注册表;Client face 提供独立的移动布局与扩展 SDK;Android App 提供受限的原生 Bridge。Bridge 使用 `androidx.webkit` WebMessage,每条消息都校验精确顶层 Origin 和主 Frame,并限制消息大小;不使用 `addJavascriptInterface`。DeepSeek Harness 的源码和当前 Web 页面(默认端口 3080,可通过 `dsh web --port` 修改)都不会被改动,安装和卸载完全通过插件机制完成。
|
|
195
247
|
|
|
196
248
|
## 安全
|
|
197
249
|
|
|
@@ -208,17 +260,18 @@ flowchart LR
|
|
|
208
260
|
|
|
209
261
|
下表列出各插件版本验证支持到的 DeepSeek Harness 版本(早于该版本的 0.1.x 均兼容)。0.3.6 起插件不再按版本号拒绝启动,未列出的更新版本由 CI 契约检查兜底。历史记录见 [CHANGELOG.md](CHANGELOG.md)。
|
|
210
262
|
|
|
211
|
-
|
|
212
|
-
|
|
|
213
|
-
|
|
|
214
|
-
| `0.3.
|
|
215
|
-
| `0.3.
|
|
216
|
-
| `0.3.
|
|
217
|
-
| `0.3.
|
|
218
|
-
| `0.3.0
|
|
219
|
-
| `0.
|
|
220
|
-
|
|
221
|
-
|
|
263
|
+
|
|
264
|
+
| DSH Mobile 插件 | 验证支持的 DeepSeek Harness 版本 |
|
|
265
|
+
| ----------------------------------------- | -------------------------------------------------------------- |
|
|
266
|
+
| `0.3.15`、`0.3.16`、`0.4.0` | `0.1.5-rc.2`(契约检查);`0.1.5-rc.1`(@idoall 局域网实测) |
|
|
267
|
+
| `0.3.14` | `0.1.3-alpha.2` |
|
|
268
|
+
| `0.3.9`-`0.3.12` | `0.1.3-alpha.1` |
|
|
269
|
+
| `0.3.6`-`0.3.8` | `0.1.2-rc.1` |
|
|
270
|
+
| `0.3.4`、`0.3.5` | `0.1.2-alpha.2` |
|
|
271
|
+
| `0.3.0`-`0.3.3` | `0.1.2-alpha.1` |
|
|
272
|
+
| `0.1.4`、`0.2.x` | `0.1.1-rc.2` |
|
|
273
|
+
|
|
274
|
+
现有 0.3.3–0.4.0 App 无需重新配对;cpolar 用户应使用 0.3.15 或更新 App,较早版本可能在免费线路的慢速首次加载完成前超时;更早的 App 还使用不同的状态栏策略。App 0.4.0 才支持多设备列表、启动行为设置和电脑端撤销状态同步;旧版 App 仍可连接已保存的单台设备。App 0.1.3 及更早版本需卸载重装并重新配对。
|
|
222
275
|
|
|
223
276
|
## 卸载
|
|
224
277
|
|
package/lib/cli.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { execFile } from "node:child_process";
|
|
3
2
|
import { chmod, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
4
3
|
import { homedir, networkInterfaces } from "node:os";
|
|
5
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
4
|
+
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
6
5
|
import { X509Certificate, createPrivateKey, createPublicKey } from "node:crypto";
|
|
6
|
+
import { execFile } from "node:child_process";
|
|
7
7
|
import { generate } from "selfsigned";
|
|
8
8
|
import "@deepseek-ai/cordis";
|
|
9
9
|
import "@deepseek-ai/schemastery";
|
|
@@ -324,6 +324,88 @@ async function refreshManagedServerCertificate(setup, address) {
|
|
|
324
324
|
});
|
|
325
325
|
await Promise.all([atomicWrite(setup.tls.certFile, server.cert), atomicWrite(setup.tls.keyFile, server.private)]);
|
|
326
326
|
}
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region src/lan-setup.ts
|
|
329
|
+
const FIREWALL_TCP_RULE = "DSH Mobile HTTPS";
|
|
330
|
+
const FIREWALL_UDP_RULE = "DSH Mobile Discovery";
|
|
331
|
+
async function runElevatedPowerShell(script) {
|
|
332
|
+
await execFileText("powershell.exe", [
|
|
333
|
+
"-NoProfile",
|
|
334
|
+
"-NonInteractive",
|
|
335
|
+
"-Command",
|
|
336
|
+
[
|
|
337
|
+
"$ErrorActionPreference = 'Stop'; $process = Start-Process -FilePath 'powershell.exe' -Verb RunAs -WindowStyle Hidden -Wait -PassThru",
|
|
338
|
+
` -ArgumentList @('-NoProfile','-NonInteractive','-EncodedCommand','${Buffer.from(script, "utf16le").toString("base64")}')`,
|
|
339
|
+
"; exit $process.ExitCode"
|
|
340
|
+
].join(" ")
|
|
341
|
+
], { windowsHide: true });
|
|
342
|
+
}
|
|
343
|
+
/** Install the two Windows inbound rules used by the managed LAN listener. */
|
|
344
|
+
async function configureWindowsFirewall(port) {
|
|
345
|
+
if (process.platform !== "win32") return;
|
|
346
|
+
await runElevatedPowerShell([
|
|
347
|
+
"$ErrorActionPreference = 'Stop'",
|
|
348
|
+
`Get-NetFirewallRule -DisplayName '${FIREWALL_TCP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,
|
|
349
|
+
`Get-NetFirewallRule -DisplayName '${FIREWALL_UDP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,
|
|
350
|
+
`New-NetFirewallRule -DisplayName '${FIREWALL_TCP_RULE}' -Direction Inbound -Action Allow -Protocol TCP -LocalPort ${String(port)} -RemoteAddress LocalSubnet -Profile Any | Out-Null`,
|
|
351
|
+
`New-NetFirewallRule -DisplayName '${FIREWALL_UDP_RULE}' -Direction Inbound -Action Allow -Protocol UDP -LocalPort ${String(port)} -RemoteAddress LocalSubnet -Profile Any | Out-Null`
|
|
352
|
+
].join("; "));
|
|
353
|
+
}
|
|
354
|
+
/** Remove only the two Windows firewall rules owned by DSH Mobile. */
|
|
355
|
+
async function removeWindowsFirewall() {
|
|
356
|
+
if (process.platform !== "win32") return;
|
|
357
|
+
await runElevatedPowerShell([
|
|
358
|
+
"$ErrorActionPreference = 'Stop'",
|
|
359
|
+
`Get-NetFirewallRule -DisplayName '${FIREWALL_TCP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,
|
|
360
|
+
`Get-NetFirewallRule -DisplayName '${FIREWALL_UDP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`
|
|
361
|
+
].join("; "));
|
|
362
|
+
}
|
|
363
|
+
/** Generate private TLS material and persist a managed setup selected by the user. */
|
|
364
|
+
async function prepareManagedLanSetup(options) {
|
|
365
|
+
if (!isAbsolute(options.setupFile) || !isAbsolute(options.controlFile)) throw new Error("managed LAN setup paths must be absolute");
|
|
366
|
+
if (!Number.isSafeInteger(options.listenPort) || options.listenPort < 1024 || options.listenPort > 65535) throw new Error("managed LAN listenPort must be from 1024 through 65535");
|
|
367
|
+
if (!Number.isSafeInteger(options.dshPort) || options.dshPort < 1024 || options.dshPort > 65535) throw new Error("managed LAN dshPort must be from 1024 through 65535");
|
|
368
|
+
const directory = dirname(options.setupFile);
|
|
369
|
+
const tlsDirectory = join(directory, "tls");
|
|
370
|
+
await mkdir(tlsDirectory, {
|
|
371
|
+
recursive: true,
|
|
372
|
+
mode: 448
|
|
373
|
+
});
|
|
374
|
+
const managedTls = {
|
|
375
|
+
mode: "managed",
|
|
376
|
+
caCertFile: join(tlsDirectory, "ca.pem"),
|
|
377
|
+
caKeyFile: join(tlsDirectory, "ca-key.pem"),
|
|
378
|
+
certFile: join(tlsDirectory, "server-cert.pem"),
|
|
379
|
+
keyFile: join(tlsDirectory, "server-key.pem")
|
|
380
|
+
};
|
|
381
|
+
const ca = await ensureManagedCa(managedTls, {
|
|
382
|
+
certFile: join(tlsDirectory, "cert.pem"),
|
|
383
|
+
keyFile: join(tlsDirectory, "key.pem")
|
|
384
|
+
});
|
|
385
|
+
const setup = {
|
|
386
|
+
version: 2,
|
|
387
|
+
networkInterface: options.network.name,
|
|
388
|
+
listenPort: options.listenPort,
|
|
389
|
+
upstreamOrigin: `http://127.0.0.1:${String(options.dshPort)}`,
|
|
390
|
+
tls: managedTls
|
|
391
|
+
};
|
|
392
|
+
await refreshManagedServerCertificate(setup, options.network.address);
|
|
393
|
+
const androidCertificate = join(tlsDirectory, "dsh-mobile-ca.cer");
|
|
394
|
+
await writeFile(androidCertificate, ca.raw, { mode: 384 });
|
|
395
|
+
await Promise.all([...Object.values(managedTls).filter((value) => value !== "managed").map((file) => restrictPrivateFile(file)), restrictPrivateFile(androidCertificate)]);
|
|
396
|
+
if (options.configureFirewall) await configureWindowsFirewall(options.listenPort);
|
|
397
|
+
await Promise.all([writeFile(options.setupFile, `${JSON.stringify({
|
|
398
|
+
...setup,
|
|
399
|
+
tls: Object.fromEntries(Object.entries(setup.tls).map(([key, value]) => [key, typeof value === "string" ? value.replaceAll("\\", "/") : value]))
|
|
400
|
+
}, null, 2)}\n`, { mode: 384 }), writeFile(options.controlFile, "{\"version\":1,\"enabled\":true}\n", { mode: 384 })]);
|
|
401
|
+
await Promise.all([restrictPrivateFile(options.setupFile), restrictPrivateFile(options.controlFile)]);
|
|
402
|
+
return Object.freeze({
|
|
403
|
+
setup: Object.freeze(setup),
|
|
404
|
+
network: Object.freeze({ ...options.network }),
|
|
405
|
+
origin: `https://${options.network.address}:${String(options.listenPort)}`,
|
|
406
|
+
androidCertificate
|
|
407
|
+
});
|
|
408
|
+
}
|
|
327
409
|
Object.freeze({
|
|
328
410
|
manifest: 65536,
|
|
329
411
|
script: 1048576,
|
|
@@ -351,8 +433,6 @@ function assertExtensionId(value) {
|
|
|
351
433
|
}
|
|
352
434
|
//#endregion
|
|
353
435
|
//#region src/cli.ts
|
|
354
|
-
const FIREWALL_TCP_RULE = "DSH Mobile HTTPS";
|
|
355
|
-
const FIREWALL_UDP_RULE = "DSH Mobile Discovery";
|
|
356
436
|
function parseOptions(args) {
|
|
357
437
|
let address;
|
|
358
438
|
let port = 3443;
|
|
@@ -394,84 +474,16 @@ function parseOptions(args) {
|
|
|
394
474
|
function dshHome() {
|
|
395
475
|
return resolve(process.env.DSH_HOME ?? join(homedir(), ".dsh"));
|
|
396
476
|
}
|
|
397
|
-
async function runElevatedPowerShell(script) {
|
|
398
|
-
await execFileText("powershell.exe", [
|
|
399
|
-
"-NoProfile",
|
|
400
|
-
"-NonInteractive",
|
|
401
|
-
"-Command",
|
|
402
|
-
[
|
|
403
|
-
"$ErrorActionPreference = 'Stop'; $process = Start-Process -FilePath 'powershell.exe' -Verb RunAs -WindowStyle Hidden -Wait -PassThru",
|
|
404
|
-
` -ArgumentList @('-NoProfile','-NonInteractive','-EncodedCommand','${Buffer.from(script, "utf16le").toString("base64")}')`,
|
|
405
|
-
"; exit $process.ExitCode"
|
|
406
|
-
].join(" ")
|
|
407
|
-
], { windowsHide: true });
|
|
408
|
-
}
|
|
409
|
-
async function configureWindowsFirewall(port) {
|
|
410
|
-
if (process.platform !== "win32") return;
|
|
411
|
-
const script = [
|
|
412
|
-
"$ErrorActionPreference = 'Stop'",
|
|
413
|
-
`Get-NetFirewallRule -DisplayName '${FIREWALL_TCP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,
|
|
414
|
-
`Get-NetFirewallRule -DisplayName '${FIREWALL_UDP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,
|
|
415
|
-
`New-NetFirewallRule -DisplayName '${FIREWALL_TCP_RULE}' -Direction Inbound -Action Allow -Protocol TCP -LocalPort ${String(port)} -RemoteAddress LocalSubnet -Profile Any | Out-Null`,
|
|
416
|
-
`New-NetFirewallRule -DisplayName '${FIREWALL_UDP_RULE}' -Direction Inbound -Action Allow -Protocol UDP -LocalPort ${String(port)} -RemoteAddress LocalSubnet -Profile Any | Out-Null`
|
|
417
|
-
].join("; ");
|
|
418
|
-
console.log("Windows will request administrator approval for two LAN-only firewall rules.");
|
|
419
|
-
await runElevatedPowerShell(script);
|
|
420
|
-
}
|
|
421
|
-
async function removeWindowsFirewall() {
|
|
422
|
-
if (process.platform !== "win32") return;
|
|
423
|
-
await runElevatedPowerShell([
|
|
424
|
-
"$ErrorActionPreference = 'Stop'",
|
|
425
|
-
`Get-NetFirewallRule -DisplayName '${FIREWALL_TCP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,
|
|
426
|
-
`Get-NetFirewallRule -DisplayName '${FIREWALL_UDP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`
|
|
427
|
-
].join("; "));
|
|
428
|
-
}
|
|
429
477
|
async function setup(args) {
|
|
430
478
|
const options = parseOptions(args);
|
|
431
479
|
const preferredInterfaces = options.address === void 0 ? await preferredLanInterfaceNames() : [];
|
|
432
480
|
const network = selectLanNetwork(options.address, void 0, void 0, preferredInterfaces);
|
|
433
481
|
const home = dshHome();
|
|
434
482
|
const directory = join(home, "mobile-access");
|
|
435
|
-
|
|
436
|
-
await mkdir(tls, {
|
|
483
|
+
await mkdir(directory, {
|
|
437
484
|
recursive: true,
|
|
438
485
|
mode: 448
|
|
439
486
|
});
|
|
440
|
-
const legacyCertFile = join(tls, "cert.pem");
|
|
441
|
-
const legacyKeyFile = join(tls, "key.pem");
|
|
442
|
-
const certFile = join(tls, "server-cert.pem");
|
|
443
|
-
const keyFile = join(tls, "server-key.pem");
|
|
444
|
-
const caCertFile = join(tls, "ca.pem");
|
|
445
|
-
const caKeyFile = join(tls, "ca-key.pem");
|
|
446
|
-
const androidCertificate = join(tls, "dsh-mobile-ca.cer");
|
|
447
|
-
const managedTls = {
|
|
448
|
-
mode: "managed",
|
|
449
|
-
caCertFile,
|
|
450
|
-
caKeyFile,
|
|
451
|
-
certFile,
|
|
452
|
-
keyFile
|
|
453
|
-
};
|
|
454
|
-
const ca = await ensureManagedCa(managedTls, {
|
|
455
|
-
certFile: legacyCertFile,
|
|
456
|
-
keyFile: legacyKeyFile
|
|
457
|
-
});
|
|
458
|
-
const managedSetup = {
|
|
459
|
-
version: 2,
|
|
460
|
-
networkInterface: network.name,
|
|
461
|
-
listenPort: options.port,
|
|
462
|
-
upstreamOrigin: `http://127.0.0.1:${String(options.dshPort)}`,
|
|
463
|
-
tls: managedTls
|
|
464
|
-
};
|
|
465
|
-
await refreshManagedServerCertificate(managedSetup, network.address);
|
|
466
|
-
await writeFile(androidCertificate, ca.raw, { mode: 384 });
|
|
467
|
-
await Promise.all([
|
|
468
|
-
restrictPrivateFile(caCertFile),
|
|
469
|
-
restrictPrivateFile(caKeyFile),
|
|
470
|
-
restrictPrivateFile(certFile),
|
|
471
|
-
restrictPrivateFile(keyFile),
|
|
472
|
-
restrictPrivateFile(androidCertificate)
|
|
473
|
-
]);
|
|
474
|
-
if (options.configureFirewall) await configureWindowsFirewall(options.port);
|
|
475
487
|
const customCss = join(directory, "mobile.css");
|
|
476
488
|
try {
|
|
477
489
|
await readFile(customCss);
|
|
@@ -507,14 +519,17 @@ async function setup(args) {
|
|
|
507
519
|
mode: 448
|
|
508
520
|
});
|
|
509
521
|
await createExtensionScaffold(extensions, "custom", "自定义移动扩展", false);
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
522
|
+
if (options.configureFirewall && process.platform === "win32") console.log("Windows will request administrator approval for two LAN-only firewall rules.");
|
|
523
|
+
const result = await prepareManagedLanSetup({
|
|
524
|
+
setupFile: join(directory, "setup.json"),
|
|
525
|
+
controlFile: join(directory, "control.json"),
|
|
526
|
+
network,
|
|
527
|
+
listenPort: options.port,
|
|
528
|
+
dshPort: options.dshPort,
|
|
529
|
+
configureFirewall: options.configureFirewall
|
|
530
|
+
});
|
|
531
|
+
console.log(`DSH Mobile follows ${network.name} and is currently configured for ${result.origin}`);
|
|
532
|
+
console.log(`Install this CA certificate on Android once: ${result.androidCertificate}`);
|
|
518
533
|
console.log(`Ask DSH to customize the mobile Web UI and features in: ${customCss} and ${customScript}`);
|
|
519
534
|
console.log(`Additional extensions live in: ${extensions}`);
|
|
520
535
|
console.log("Start DSH with: dsh --profile web");
|