dsh-remote-hub 0.3.3 → 0.3.4

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
@@ -267,6 +267,27 @@ curl http://127.0.0.1:<bridgePort>/_dsh-remote/health
267
267
  > 提示:`cordis.patch.yml` 里的 `port` 只是**首选**端口。若被占用,实际端口会写在 `~/.dsh/dsh-remote/status.json`,
268
268
  > 一切链接与二维码都以那里为准,不必手改配置。
269
269
 
270
+ ### 用自己的服务器做中转(阿里云等自建 relay)
271
+
272
+ 如果你不用 ngrok,而是用一台云服务器(Caddy/nginx 终止 TLS + SSH 反向隧道)把本机暴露出去,
273
+ 就让插件把这个地址写进二维码,否则扫码只会得到 ngrok/cloudflared 的地址:
274
+
275
+ ```bash
276
+ # 方式一:临时环境变量
277
+ export DSH_REMOTE_PUBLIC_URL="https://your-relay.example" # Windows: $env:DSH_REMOTE_PUBLIC_URL="https://..."
278
+ dsh-remote start --open
279
+
280
+ # 方式二:写进 profile 配置(推荐,长期有效)
281
+ # ~/.dsh/profiles/<name>/cordis.patch.yml 的 dsh-remote 行:
282
+ # publicUrl: 'https://your-relay.example'
283
+ ```
284
+
285
+ 配置后插件不再启动 ngrok/cloudflared,并把该地址作为「公网地址」放进配对载荷;
286
+ 启动时会探测 `<publicUrl>/_dsh-remote/health`,结果记在 `status.json` 的 `public.reachable`
287
+ (中转在、但反向隧道断了 → `false` 并打印警告)。
288
+
289
+ 前提:服务器侧确实把请求转发到**桥接监听的端口**(默认 6300)。
290
+
270
291
  ### 扫码配对(一次性配对码)
271
292
 
272
293
  不想手抄 Cookie 就用二维码:
@@ -62,6 +62,21 @@ function printStatus() {
62
62
  console.log('')
63
63
  }
64
64
 
65
+ /** Describe the process listening on a port (Windows only, best effort). */
66
+ function describePortOwner(port) {
67
+ if (process.platform !== 'win32') return Promise.resolve(null)
68
+ const script =
69
+ `$c = Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue | Select-Object -First 1;` +
70
+ 'if ($c) { $p = Get-CimInstance Win32_Process -Filter (\"ProcessId=$($c.OwningProcess)\");' +
71
+ ' "PID $($c.OwningProcess): $($p.CommandLine)" }'
72
+ return new Promise((resolve) => {
73
+ execFile('powershell', ['-NoProfile', '-NonInteractive', '-Command', script], { timeout: 8000, windowsHide: true }, (err, stdout) => {
74
+ const out = String(stdout || '').trim()
75
+ resolve(err || !out ? null : out.slice(0, 180))
76
+ })
77
+ })
78
+ }
79
+
65
80
  function openBrowser(url) {
66
81
  const cmd = process.platform === 'win32' ? 'cmd' : process.platform === 'darwin' ? 'open' : 'xdg-open'
67
82
  const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url]
@@ -188,6 +203,29 @@ async function doctor() {
188
203
  }
189
204
  console.log(' ' + '-'.repeat(52))
190
205
  console.log('')
206
+ // Who owns the bridge port right now, and what public URL is advertised.
207
+ const bridgePort = Number(process.env.DSH_REMOTE_PORT || 6300)
208
+ const health = await bridgeHealthy(bridgePort)
209
+ if (health) {
210
+ console.log(` ✔ 桥接端口 ${bridgePort}(本插件,pairing=${health.pairing})`)
211
+ } else {
212
+ console.log(` · 桥接端口 ${bridgePort} 上没有本插件在跑`)
213
+ const owner = await describePortOwner(bridgePort)
214
+ if (owner) console.log(` 被其它进程占用: ${owner}`)
215
+ }
216
+ const st = readStatus()
217
+ const pub = st?.public
218
+ if (pub?.url) {
219
+ const tag = pub.kind === 'relay' ? '自建中转' : pub.kind
220
+ console.log(` ▲ 公网地址 ${pub.url}(${tag}${pub.reachable === false ? ',当前不可达' : ''})`)
221
+ if (pub.kind !== 'relay' && process.env.DSH_REMOTE_PUBLIC_URL) {
222
+ console.log(' (已设置 DSH_REMOTE_PUBLIC_URL,但当前桥接未采用:需要重启桥接)')
223
+ }
224
+ } else if (process.env.DSH_REMOTE_PUBLIC_URL) {
225
+ console.log(` · 公网地址 未生效(要重启桥接才会用 ${process.env.DSH_REMOTE_PUBLIC_URL})`)
226
+ } else {
227
+ console.log(' · 公网地址 未配置(如需自建中转,设置 DSH_REMOTE_PUBLIC_URL)')
228
+ }
191
229
  }
192
230
 
193
231
  async function main() {
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.3.3'
19
+ export const version = '0.3.4'
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
@@ -11,6 +11,7 @@
11
11
 
12
12
  import http from 'node:http'
13
13
  import https from 'node:https'
14
+ import { execFile } from 'node:child_process'
14
15
  import fs from 'node:fs'
15
16
  import os from 'node:os'
16
17
  import path from 'node:path'
@@ -126,6 +127,21 @@ function writeStatus(status) {
126
127
  fs.writeFileSync(CONNECT_FILE, lines.join(os.EOL), 'utf8')
127
128
  }
128
129
 
130
+ /** Best-effort description of the process listening on `port` (Windows), for diagnostics. */
131
+ function describePortOwner(port) {
132
+ if (process.platform !== 'win32') return Promise.resolve(null)
133
+ const script =
134
+ `$c = Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue | Select-Object -First 1;` +
135
+ 'if ($c) { $p = Get-CimInstance Win32_Process -Filter (\"ProcessId=$($c.OwningProcess)\");' +
136
+ ' "PID $($c.OwningProcess): $($p.CommandLine)" }'
137
+ return new Promise((resolve) => {
138
+ execFile('powershell', ['-NoProfile', '-NonInteractive', '-Command', script], { timeout: 8000, windowsHide: true }, (err, stdout) => {
139
+ const out = String(stdout || '').trim()
140
+ resolve(err || !out ? null : out.slice(0, 200))
141
+ })
142
+ })
143
+ }
144
+
129
145
  /** Probe a full URL (used for the configured public relay) and report whether it answers. */
130
146
  function probeHealthUrl(url, timeoutMs = 4000) {
131
147
  return new Promise((resolve) => {
@@ -332,6 +348,10 @@ export async function start(config = {}) {
332
348
  log('dsh-remote: port is held by an old bridge WITHOUT pairing support; using another port')
333
349
  } else {
334
350
  log(`dsh-remote: port ${wanted} is busy (not a dsh-remote bridge); using another port`)
351
+ // Say WHO holds it: the usual culprit is a leftover lan-proxy.mjs from an older setup, and
352
+ // knowing the PID is what lets the user stop it instead of guessing.
353
+ const owner = await describePortOwner(wanted)
354
+ if (owner) log(`dsh-remote: port ${wanted} is held by: ${owner}`)
335
355
  }
336
356
  bridge = await startBridge({
337
357
  listenPort: 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-remote-hub",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "DSH 插件:把电脑上的 DeepSeek Harness 变成手机可访问的服务——起桥接、自动开隧道(ngrok / cloudflared)、签发 30 天 Cookie,并输出地址与凭据,配合 DSH Remote Android App 使用。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",