dsh-remote-hub 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,9 +19,9 @@ Kotlin/Compose 直连 DSH 自带的 HTTP API + WebSocket 网关。
19
19
 
20
20
  ## 下载手机端 App
21
21
 
22
- **DSH Remote v4.21.0** · Android 8.0 (API 26) 及以上 · 2.8 MB
22
+ **DSH Remote v4.21.2** · Android 8.0 (API 26) 及以上 · 2.8 MB
23
23
 
24
- [⬇️ 下载 APK](https://github.com/liaoguogang-hub/dsh-remote-public/releases/download/v4.21.0/DSH-Remote-v4.21.0.apk) ·
24
+ [⬇️ 下载 APK](https://github.com/liaoguogang-hub/dsh-remote-public/releases/download/v4.21.2/DSH-Remote-v4.21.2.apk) ·
25
25
  [全部版本](https://github.com/liaoguogang-hub/dsh-remote-public/releases)
26
26
 
27
27
  安装后应用名「DSH Remote」,包名 `com.dsh.remote.ngrok`(可与作者的 `DSH` 自用版共存)。
@@ -5,7 +5,7 @@
5
5
  * dsh-remote status show LAN URL, public URL, cookie and upstream port
6
6
  * dsh-remote connect print the two lines to paste into the phone
7
7
  * dsh-remote cookie print only the cookie
8
- * dsh-remote start [--open] [--public-url=URL] run the supervisor; --open 打印地址并打开配对页
8
+ * dsh-remote start [--open] [--restart] [--public-url=URL] --restart 会先停掉配置不同的旧桥接
9
9
  * dsh-remote stop ask a foreground `start` to stop (best effort, via status file)
10
10
  * dsh-remote doctor check every prerequisite in one pass
11
11
  * dsh-remote qr open the pairing page (QR) in the default browser
@@ -260,9 +260,26 @@ async function main() {
260
260
  break
261
261
  case 'start': {
262
262
  const wantOpen = process.argv.includes('--open')
263
+ const wantRestart = process.argv.includes('--restart')
263
264
  // --public-url=https://relay.example advertises a self-hosted relay in the QR payload.
264
265
  const publicArg = process.argv.find((a) => a.startsWith('--public-url='))
265
266
  const publicUrl = publicArg ? publicArg.slice('--public-url='.length) : process.env.DSH_REMOTE_PUBLIC_URL || ''
267
+ // A launcher that carries its own transport config must not silently adopt a bridge started
268
+ // with different config (that is how a public launcher ended up advertising a self-use relay).
269
+ if (wantRestart) {
270
+ const live = await bridgeHealthy(Number(process.env.DSH_REMOTE_PORT || 6300))
271
+ const mismatch = live && (live.relay || null) !== (publicUrl || null)
272
+ if (mismatch && live.pid) {
273
+ console.log(`已有桥接配置不同(relay=${live.relay ?? '无'}),先停止它(PID ${live.pid})再按当前配置启动`)
274
+ try {
275
+ process.kill(Number(live.pid))
276
+ } catch (err) {
277
+ console.log(`停止失败:${err.message}`)
278
+ }
279
+ await new Promise((r) => setTimeout(r, 1200))
280
+ }
281
+ }
282
+
266
283
  let handle
267
284
  try {
268
285
  handle = await start({ publicUrl, log: (m) => console.log(m) })
package/lib/bridge.js CHANGED
@@ -68,7 +68,7 @@ function trimSessionList(body) {
68
68
  * @param {(msg: string) => void} [options.log]
69
69
  * @returns {Promise<{close: () => Promise<void>, port: number}>}
70
70
  */
71
- export function startBridge({ listenPort, authority, upstream, pairing = null, log = () => {} }) {
71
+ export function startBridge({ listenPort, authority, upstream, pairing = null, relayUrl = null, log = () => {} }) {
72
72
  const forward = (req, res, attempt = 0) =>
73
73
  new Promise((resolve) => {
74
74
  upstream.resolve(attempt > 0).then((port) => {
@@ -155,6 +155,10 @@ export function startBridge({ listenPort, authority, upstream, pairing = null, l
155
155
  const body = JSON.stringify({
156
156
  ok: true,
157
157
  service: 'dsh-remote-bridge',
158
+ pid: process.pid,
159
+ // Whether this bridge advertises a self-hosted relay; a launcher that expects none must not
160
+ // adopt a bridge that has one, or the public app would show the self-use relay.
161
+ relay: relayUrl || null,
158
162
  // A bridge built before pairing existed has no such route; callers use this flag to decide
159
163
  // whether an already-listening port can be adopted or must be bypassed.
160
164
  pairing: pairing != null,
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.4.0'
19
+ export const version = '0.4.1'
20
20
 
21
21
  /** No host services are required: the plugin only needs Node builtins. */
22
22
  export const inject = []
package/lib/supervisor.js CHANGED
@@ -274,6 +274,10 @@ export async function start(config = {}) {
274
274
  const lanUrlsFor = (bridgePort) =>
275
275
  lanAddresses().map((address) => `http://${address}:${bridgePort}`)
276
276
 
277
+ // The self-hosted relay (if configured) and the auto tunnel are reported SEPARATELY: a self-use
278
+ // setup wants both (relay for 4G, ngrok as a second path), and the phone decides which to use.
279
+ const relayUrl = String(publicUrl || '').trim().replace(/\/+$/, '') || null
280
+
277
281
  let bridgeInstance = null
278
282
  const actualBridgePort = () => (bridgeInstance ? bridgeInstance.port : Number(port) || 6300)
279
283
 
@@ -312,6 +316,7 @@ export async function start(config = {}) {
312
316
  authority,
313
317
  upstream: discovery,
314
318
  pairing,
319
+ relayUrl,
315
320
  log,
316
321
  })
317
322
  } catch (err) {
@@ -337,7 +342,13 @@ export async function start(config = {}) {
337
342
  authority: existing.authority ?? authority,
338
343
  upstreamPort: existing.upstreamPort ?? null,
339
344
  lan: { ip, url: ip ? `http://${ip}:${livePort}` : null },
340
- public: prev.public ?? { url: null, kind: 'none', note: 'served by an existing bridge' },
345
+ // Never inherit the previous run's public address: it may describe a different transport
346
+ // (e.g. a self-use relay) than the launcher that is running now.
347
+ public: existing.relay
348
+ ? { url: existing.relay, kind: 'relay' }
349
+ : { url: null, kind: 'none', note: 'served by an existing bridge' },
350
+ relay: existing.relay ? { url: existing.relay } : null,
351
+ tunnel: null,
341
352
  cookie: prev.cookie ?? null,
342
353
  cookieExpiresAt: prev.cookieExpiresAt ?? null,
343
354
  pairing: { url: prev.public?.url ?? (ip ? `http://${ip}:${livePort}` : null), page: '/_dsh-remote/pair' },
@@ -405,9 +416,6 @@ export async function start(config = {}) {
405
416
  },
406
417
  })
407
418
 
408
- // The self-hosted relay (if configured) and the auto tunnel are reported SEPARATELY: a self-use
409
- // setup wants both (relay for 4G, ngrok as a second path), and the phone decides which to use.
410
- const relayUrl = String(publicUrl || '').trim().replace(/\/+$/, '') || null
411
419
  lastPublicUrl = relayUrl ?? tunnel.publicUrl
412
420
  let relayReachable = null
413
421
  if (relayUrl) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-remote-hub",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "DSH 插件:把电脑上的 DeepSeek Harness 变成手机可访问的服务——起桥接、自动开隧道(ngrok / cloudflared)、签发 30 天 Cookie,并输出地址与凭据,配合 DSH Remote Android App 使用。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",