dsh-mesh-ui 0.0.3 → 0.0.5
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/lib/collect.js +35 -7
- package/package.json +1 -1
package/lib/collect.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// 归宿:reusable(dsh-mesh/plugin/dsh-mesh-ui/lib/collect.js)
|
|
15
15
|
|
|
16
16
|
import { execFile } from 'node:child_process'
|
|
17
|
-
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
17
|
+
import { existsSync, readdirSync, readFileSync, statfsSync, writeFileSync } from 'node:fs'
|
|
18
18
|
import { lookup as dnsLookup } from 'node:dns/promises'
|
|
19
19
|
import { arch as osArch, cpus as osCpus, freemem as osFreemem, homedir, platform as osPlatform, release as osRelease, totalmem as osTotalmem } from 'node:os'
|
|
20
20
|
import { join } from 'node:path'
|
|
@@ -167,7 +167,19 @@ function gb(bytes) {
|
|
|
167
167
|
return Math.round((Number(bytes || 0) / 1024 / 1024 / 1024) * 10) / 10
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
/**
|
|
170
|
+
/** 本机系统盘 / 根分区容量(与远端 hardware.disk 结构一致)。 */
|
|
171
|
+
function localDisk() {
|
|
172
|
+
try {
|
|
173
|
+
const path = process.platform === 'win32' ? `${process.env.SystemDrive || 'C:'}\\` : '/'
|
|
174
|
+
const st = statfsSync(path)
|
|
175
|
+
const blockSize = Number(st.bsize || 4096)
|
|
176
|
+
return { path, totalGB: gb(Number(st.blocks) * blockSize), freeGB: gb(Number(st.bavail) * blockSize) }
|
|
177
|
+
} catch {
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** 本机硬件概况(本机直采;远端由 host_self 的 hardware 字段提供)。 */
|
|
171
183
|
function localHardware() {
|
|
172
184
|
const cpuList = osCpus()
|
|
173
185
|
const first = cpuList[0] || {}
|
|
@@ -177,7 +189,8 @@ function localHardware() {
|
|
|
177
189
|
cpuModel: String(first.model || '').replace(/\s+/g, ' ').trim(),
|
|
178
190
|
cpuCores: cpuList.length,
|
|
179
191
|
memTotalGB: gb(osTotalmem()),
|
|
180
|
-
memFreeGB: gb(osFreemem())
|
|
192
|
+
memFreeGB: gb(osFreemem()),
|
|
193
|
+
disk: localDisk()
|
|
181
194
|
}
|
|
182
195
|
}
|
|
183
196
|
|
|
@@ -426,12 +439,17 @@ export async function collect(repo) {
|
|
|
426
439
|
list.push({ id: inst.id, port: 3080 + idx, up: Boolean(inst.up) })
|
|
427
440
|
}
|
|
428
441
|
|
|
429
|
-
// 远端节点:host_self
|
|
442
|
+
// 远端节点:host_self 并发(超时放宽到 12s —— 跨公网节点 6s 偶尔不够,会导致硬件字段丢失)
|
|
430
443
|
const meshPeers = mesh.peers || []
|
|
431
444
|
const peerSelfs = await Promise.all(meshPeers.map(async (peer) => {
|
|
432
445
|
if (!peer.up) return null
|
|
433
|
-
return await cliJson(repo, ['peer', 'call', 'host_self', '--to', peer.name, '--args', '{}'], { timeout:
|
|
446
|
+
return await cliJson(repo, ['peer', 'call', 'host_self', '--to', peer.name, '--args', '{}'], { timeout: 12000 })
|
|
434
447
|
}))
|
|
448
|
+
// 硬件缓存:跨公网 host_self 偶发超时时,用上次成功结果兜底,避免面板时有时无
|
|
449
|
+
const HW_CACHE_FILE = join(meshHome(), 'peer-hardware.json')
|
|
450
|
+
let hwCache = {}
|
|
451
|
+
try { hwCache = JSON.parse(readFileSync(HW_CACHE_FILE, 'utf8')) || {} } catch { hwCache = {} }
|
|
452
|
+
const devKeyOf = (name) => String(name || '').toLowerCase().replace(/-(lan|wan|exec|exe|import|local|ctl)$/i, '')
|
|
435
453
|
const peers = meshPeers.map((peer, index) => {
|
|
436
454
|
const self = unwrapPeerCall(peerSelfs[index])
|
|
437
455
|
const stats = agentStats(self?.agents)
|
|
@@ -446,8 +464,8 @@ export async function collect(repo) {
|
|
|
446
464
|
instancesList: remoteByNode[peer.name] || [],
|
|
447
465
|
sessions: stats.sessions,
|
|
448
466
|
running: stats.running,
|
|
449
|
-
hostname: self?.node?.hostname || '',
|
|
450
|
-
hardware: self?.hardware || null,
|
|
467
|
+
hostname: self?.node?.hostname || '',
|
|
468
|
+
hardware: self?.hardware || hwCache[devKeyOf(peer.name)] || null,
|
|
451
469
|
release: self?.node?.release || '',
|
|
452
470
|
os: osLabel(self?.node?.platform, self?.node?.release),
|
|
453
471
|
uptimeSec: overviewByNode[peer.name]?.uptimeSec ?? null,
|
|
@@ -460,6 +478,16 @@ export async function collect(repo) {
|
|
|
460
478
|
error: peer.error?.code || null
|
|
461
479
|
}
|
|
462
480
|
})
|
|
481
|
+
// 同一设备可能有多个入口(company / company-exec / -lan / -wan):硬件信息互通,
|
|
482
|
+
// 避免 UI 去重时选中了恰好没有 hardware 的那条记录。
|
|
483
|
+
const hwByDev = {}
|
|
484
|
+
for (const p of peers) { const k = devKeyOf(p.name); if (p.hardware) hwByDev[k] = p.hardware }
|
|
485
|
+
for (const p of peers) {
|
|
486
|
+
const k = devKeyOf(p.name)
|
|
487
|
+
if (!p.hardware && hwByDev[k]) p.hardware = hwByDev[k]
|
|
488
|
+
if (p.hardware) hwCache[k] = p.hardware
|
|
489
|
+
}
|
|
490
|
+
try { writeFileSync(HW_CACHE_FILE, JSON.stringify(hwCache, null, 1)) } catch { /* 缓存写失败不影响主流程 */ }
|
|
463
491
|
|
|
464
492
|
// 2026-11-10:LAN 链路要显示 IP(不是主机名)→ 对域名型 tunnel.host 做一次 DNS 解析(800ms 超时,失败则回落主机名)
|
|
465
493
|
function isIpLiteral(host) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mesh-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "DSH settings-page panel for dsh-mesh: AI network topology, peer health, and delegation panel. Read-only; data is collected from the local mesh CLI.",
|
|
5
5
|
"icon": "icon.svg",
|
|
6
6
|
"type": "module",
|