dsh-remote-hub 0.2.1 → 0.2.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/README.md CHANGED
@@ -19,9 +19,9 @@ Kotlin/Compose 直连 DSH 自带的 HTTP API + WebSocket 网关。
19
19
 
20
20
  ## 下载手机端 App
21
21
 
22
- **DSH Remote v4.19.1** · Android 8.0 (API 26) 及以上 · 2.8 MB
22
+ **DSH Remote v4.20.0** · Android 8.0 (API 26) 及以上 · 2.8 MB
23
23
 
24
- [⬇️ 下载 APK](https://github.com/liaoguogang-hub/dsh-remote-public/releases/download/v4.19.1/DSH-Remote-v4.19.1.apk) ·
24
+ [⬇️ 下载 APK](https://github.com/liaoguogang-hub/dsh-remote-public/releases/download/v4.20.0/DSH-Remote-v4.20.0.apk) ·
25
25
  [全部版本](https://github.com/liaoguogang-hub/dsh-remote-public/releases)
26
26
 
27
27
  安装后应用名「DSH Remote」,包名 `com.dsh.remote.ngrok`(可与作者的 `DSH` 自用版共存)。
@@ -233,7 +233,7 @@ dsh-remote qr
233
233
  浏览器会打开 `http://127.0.0.1:6300/_dsh-remote/pair`,显示一个二维码。手机 DSH Remote 里点
234
234
  「扫码配对」,扫完自动填入地址并换取登录凭据,直接连上。
235
235
 
236
- 扫码后会自动判断:返回的是私有地址就填进「局域网」,公网地址填进「自定义」。
236
+ 扫码一次会同时写入「局域网」和「ngrok」两档地址;连接时优先局域网(同 WiFi 最快),局域网不通才走 ngrok。扫码后即保存,之后不用再扫。
237
237
 
238
238
  安全设计:
239
239
  - 二维码里**不含 Cookie**,只含一个 **10 分钟有效的一次性配对码**(`{"v":1,"u":"<地址>","c":"<配对码>","e":<过期时间>}`);
package/lib/index.js CHANGED
@@ -16,7 +16,7 @@ import { start } from './supervisor.js'
16
16
 
17
17
  export const name = 'dsh-remote-hub'
18
18
 
19
- export const version = '0.2.1'
19
+ export const version = '0.2.2'
20
20
 
21
21
  /** No host services are required: the plugin only needs Node builtins. */
22
22
  export const inject = []
@@ -15,9 +15,15 @@ import QRCode from 'qrcode'
15
15
 
16
16
  export const PAIR_PREFIX = '/_dsh-remote/pair'
17
17
 
18
- /** Build the payload the phone parses. `u` is the URL the phone should talk to. */
19
- export function buildPairPayload({ url, code, expiresAt }) {
20
- return JSON.stringify({ v: 1, u: url, c: code, e: expiresAt })
18
+ /**
19
+ * Build the payload the phone parses.
20
+ *
21
+ * Both addresses are shipped together so one scan can fill both tabs and the phone can pick the
22
+ * fastest one at connect time: `l` = LAN (private address), `u` = public tunnel. Either may be null
23
+ * when that transport is unavailable that moment.
24
+ */
25
+ export function buildPairPayload({ lan, publicUrl, code, expiresAt }) {
26
+ return JSON.stringify({ v: 1, l: lan ?? null, u: publicUrl ?? null, c: code, e: expiresAt })
21
27
  }
22
28
 
23
29
  /**
@@ -67,7 +73,10 @@ export async function handlePairing({ req, res, current, store }) {
67
73
  res.end(
68
74
  JSON.stringify({
69
75
  ok: true,
70
- url: snapshot.url,
76
+ // `url` keeps the old single-address shape working; new clients read lan/public.
77
+ url: snapshot.public ?? snapshot.lan ?? null,
78
+ lan: snapshot.lan ?? null,
79
+ public: snapshot.public ?? null,
71
80
  cookie: result.cookie,
72
81
  issuedAt: result.issuedAt,
73
82
  }),
@@ -77,7 +86,7 @@ export async function handlePairing({ req, res, current, store }) {
77
86
 
78
87
  // Any other GET under the prefix hands out a fresh code.
79
88
  const { code, expiresAt, ttlMs } = store.issue(snapshot.cookie)
80
- const payload = buildPairPayload({ url: snapshot.url, code, expiresAt })
89
+ const payload = buildPairPayload({ lan: snapshot.lan, publicUrl: snapshot.public, code, expiresAt })
81
90
 
82
91
  if (url === PAIR_PREFIX + '/svg') {
83
92
  const svg = await QRCode.toString(payload, { type: 'svg', margin: 1, width: 320, errorCorrectionLevel: 'M' })
@@ -110,8 +119,8 @@ export async function handlePairing({ req, res, current, store }) {
110
119
  <p>配对码 <code>${code}</code> · ${minutes} 分钟内有效,且只能用一次</p>
111
120
  <div class="qr">${svg}</div>
112
121
  <p class="ok">扫码后自动填入地址并换取登录凭据</p>
113
- <p class="addr">本页地址:<br>${snapshot.url}</p>
114
- <p class="addr">手输备用:打开 App → 设置 → 扫码配对 → 输入配对码</p>
122
+ <p class="addr">${snapshot.lan ? `局域网:${snapshot.lan}<br>` : ''}${snapshot.public ? `公网:${snapshot.public}` : ''}</p>
123
+ <p class="addr">扫一次会同时存下局域网与公网地址,之后自动优先走局域网</p>
115
124
  </div></body></html>`
116
125
  res.writeHead(200, { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store' })
117
126
  res.end(html)
package/lib/supervisor.js CHANGED
@@ -164,9 +164,12 @@ export async function start(config = {}) {
164
164
  const pairingStore = createPairingStore()
165
165
  const pairing = {
166
166
  store: pairingStore,
167
- // `current()` is read at redeem time so a URL that appears later (tunnel is still coming up)
168
- // is still handed to the phone.
169
- current: () => ({ url: lastPublicUrl ?? (ipForPairing ? `http://${ipForPairing}:${Number(port) || 6300}` : ''), cookie }),
167
+ // Read at redeem time so a tunnel URL that appears later is still handed to the phone.
168
+ current: () => ({
169
+ lan: ipForPairing ? `http://${ipForPairing}:${Number(port) || 6300}` : null,
170
+ public: lastPublicUrl ?? null,
171
+ cookie,
172
+ }),
170
173
  }
171
174
 
172
175
  const ipForPairing = lanAddress()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-remote-hub",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "DSH 插件:把电脑上的 DeepSeek Harness 变成手机可访问的服务——起桥接、自动开隧道(ngrok / cloudflared)、签发 30 天 Cookie,并输出地址与凭据,配合 DSH Remote Android App 使用。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",