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 +21 -0
- package/bin/dsh-remote.mjs +38 -0
- package/lib/index.js +1 -1
- package/lib/supervisor.js +20 -0
- package/package.json +1 -1
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 就用二维码:
|
package/bin/dsh-remote.mjs
CHANGED
|
@@ -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.
|
|
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