fix(swarmtop): expose and render full peer identity/details from proc surfaces
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
async function run(ctx, argv) {
|
||||
const argv0 = argv[0] || 'dhtscan'
|
||||
const args = argv.slice(1)
|
||||
if (args.includes('-h') || args.includes('--help')) {
|
||||
ctx.console.log(
|
||||
'usage: ' +
|
||||
argv0 +
|
||||
'\n' +
|
||||
'Print compact DHT scan summary from /proc/bare_os/dht_scan.json.\n' +
|
||||
'See man dhtscan.'
|
||||
)
|
||||
return
|
||||
}
|
||||
const dht = await bareP2pReadProcJson(ctx, '/proc/bare_os/dht_scan.json')
|
||||
ctx.console.log(JSON.stringify(dht, null, 2))
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
async function run(ctx, argv) {
|
||||
const argv0 = argv[0] || 'dhttop'
|
||||
const args = argv.slice(1)
|
||||
if (args.includes('-h') || args.includes('--help')) {
|
||||
ctx.console.log(
|
||||
'usage: ' + argv0 + '\n' + 'Live DHT dashboard TUI.\n' + 'See man dhttop.'
|
||||
)
|
||||
return
|
||||
}
|
||||
const stdin = ctx.replStdin
|
||||
const isTTY = Boolean(stdin && /** @type {{ isTTY?: boolean }} */ (stdin).isTTY)
|
||||
if (!isTTY) {
|
||||
const dht = await bareP2pReadProcJson(ctx, '/proc/bare_os/dht_scan.json')
|
||||
ctx.console.log(JSON.stringify(dht, null, 2))
|
||||
return
|
||||
}
|
||||
await bareP2pRunSimpleTui(ctx, {
|
||||
title: 'dhttop',
|
||||
subtitle: 'dht operations · q quit · r refresh',
|
||||
renderLines: async () => {
|
||||
const dht = await bareP2pReadProcJson(ctx, '/proc/bare_os/dht_scan.json')
|
||||
const lines = []
|
||||
lines.push('schema: ' + String(dht.schema ?? '?'))
|
||||
lines.push('peers: ' + String(dht.peers ?? '?'))
|
||||
lines.push('firewalled: ' + String(dht.dhtStatus?.firewalled ?? '?'))
|
||||
lines.push('randomized: ' + String(dht.dhtStatus?.randomized ?? '?'))
|
||||
lines.push('bootstrap: ' + String(dht.bootstrapHint || '(default)'))
|
||||
lines.push('')
|
||||
lines.push('raw:')
|
||||
lines.push(JSON.stringify(dht))
|
||||
return lines
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
async function run(ctx, argv) {
|
||||
const argv0 = argv[0] || 'holepunch-view'
|
||||
const args = argv.slice(1)
|
||||
if (args.includes('-h') || args.includes('--help')) {
|
||||
ctx.console.log(
|
||||
'usage: ' +
|
||||
argv0 +
|
||||
'\n' +
|
||||
'NAT and holepunch summary dashboard.\n' +
|
||||
'See man holepunch-view.'
|
||||
)
|
||||
return
|
||||
}
|
||||
const stdin = ctx.replStdin
|
||||
const isTTY = Boolean(stdin && /** @type {{ isTTY?: boolean }} */ (stdin).isTTY)
|
||||
if (!isTTY) {
|
||||
const h = await bareP2pReadProcJson(ctx, '/proc/bare_os/holepunch_summary.json')
|
||||
ctx.console.log(JSON.stringify(h, null, 2))
|
||||
return
|
||||
}
|
||||
await bareP2pRunSimpleTui(ctx, {
|
||||
title: 'holepunch-view',
|
||||
subtitle: 'nat and holepunch telemetry · q quit · r refresh',
|
||||
renderLines: async () => {
|
||||
const h = await bareP2pReadProcJson(ctx, '/proc/bare_os/holepunch_summary.json')
|
||||
return [
|
||||
'peers: ' + String(h.peers ?? '?'),
|
||||
'firewalled: ' + String(h.firewalled ?? '?'),
|
||||
'randomized: ' + String(h.randomized ?? '?'),
|
||||
'',
|
||||
'note: ' + String(h.note || '')
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
async function run(ctx, argv) {
|
||||
const argv0 = argv[0] || 'p2ping'
|
||||
const args = argv.slice(1)
|
||||
if (args.includes('-h') || args.includes('--help')) {
|
||||
ctx.console.log(
|
||||
'usage: ' +
|
||||
argv0 +
|
||||
' [LABEL]\n' +
|
||||
'Emit a p2p ping event and print local swarm timing hints.\n' +
|
||||
'See man p2ping.'
|
||||
)
|
||||
return
|
||||
}
|
||||
const label = args.join(' ').trim() || 'ping'
|
||||
const t0 = Date.now()
|
||||
const swarm = await bareP2pReadSwarmSnapshot(ctx)
|
||||
const evtId = bareP2pId('p2ping')
|
||||
const send = bareP2pSend(ctx, 'p2ping', 'ping', {
|
||||
evtId,
|
||||
label,
|
||||
sentAtMs: t0
|
||||
})
|
||||
const t1 = Date.now()
|
||||
const out = {
|
||||
schema: 1,
|
||||
tool: argv0,
|
||||
evtId,
|
||||
label,
|
||||
ok: !(send && send.ok === false),
|
||||
sendReason: send && send.ok === false ? String(send.reason || 'send_failed') : null,
|
||||
localElapsedMs: t1 - t0,
|
||||
swarmPeers: swarm.peerCount
|
||||
}
|
||||
ctx.console.log(JSON.stringify(out, null, 2))
|
||||
if (send && send.ok === false) ctx.exitCode = 1
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
async function run(ctx, argv) {
|
||||
const argv0 = argv[0] || 'p2ptrace'
|
||||
const args = argv.slice(1)
|
||||
if (args.includes('-h') || args.includes('--help')) {
|
||||
ctx.console.log(
|
||||
'usage: ' +
|
||||
argv0 +
|
||||
' [APP] [N]\n' +
|
||||
'Dump recent p2p events as JSON lines.\n' +
|
||||
'APP defaults to all known p2p apps.\n' +
|
||||
'See man p2ptrace.'
|
||||
)
|
||||
return
|
||||
}
|
||||
const app = String(args[0] || '').trim()
|
||||
const n = Math.max(1, Math.min(2000, parseInt(args[1] || '200', 10) || 200))
|
||||
const apps = app
|
||||
? [app]
|
||||
: [
|
||||
'swarmtop',
|
||||
'meshdrop',
|
||||
'taskmesh',
|
||||
'peernote',
|
||||
'hypershell-board',
|
||||
'p2ping',
|
||||
'peerdiscover',
|
||||
'peerctl'
|
||||
]
|
||||
const rows = []
|
||||
for (const a of apps) {
|
||||
rows.push(...bareP2pCollectFromHistory(ctx, a, n))
|
||||
}
|
||||
rows.sort((a, b) => (a.receivedAtMs || 0) - (b.receivedAtMs || 0))
|
||||
for (const r of rows.slice(-n)) {
|
||||
ctx.console.log(
|
||||
JSON.stringify({
|
||||
atMs: r.receivedAtMs || 0,
|
||||
app: r.packet?.app || '?',
|
||||
kind: r.packet?.kind || '?',
|
||||
from: r.displayName || r.fromPeerKey || '',
|
||||
payload: r.packet?.payload || {}
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
async function run(ctx, argv) {
|
||||
const argv0 = argv[0] || 'peerctl'
|
||||
const args = argv.slice(1)
|
||||
if (args.includes('-h') || args.includes('--help') || args.length === 0) {
|
||||
ctx.console.log(
|
||||
'usage: ' +
|
||||
argv0 +
|
||||
' health\n' +
|
||||
' ' +
|
||||
argv0 +
|
||||
' request ACTION [JSON_PAYLOAD]\n' +
|
||||
' ' +
|
||||
argv0 +
|
||||
' history [N]\n' +
|
||||
'P2P control plane helper over event envelopes.\n' +
|
||||
'See man peerctl.'
|
||||
)
|
||||
if (args.length === 0) ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const sub = args[0]
|
||||
if (sub === 'health') {
|
||||
const swarm = await bareP2pReadSwarmSnapshot(ctx)
|
||||
const doctor = await bareP2pReadProcJson(ctx, '/proc/bare_os/swarm_doctor.json')
|
||||
ctx.console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
schema: 1,
|
||||
swarmPeers: swarm.peerCount,
|
||||
health: doctor.health || 'unknown',
|
||||
remediation: doctor.remediation || []
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
if (sub === 'request') {
|
||||
const action = String(args[1] || '').trim()
|
||||
if (!action) {
|
||||
ctx.console.error(argv0 + ': request requires ACTION')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
let payload = {}
|
||||
if (args[2]) {
|
||||
try {
|
||||
payload = JSON.parse(args.slice(2).join(' '))
|
||||
} catch {
|
||||
ctx.console.error(argv0 + ': invalid JSON payload')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
const r = bareP2pSend(ctx, 'peerctl', 'request', {
|
||||
requestId: bareP2pId('ctl'),
|
||||
action,
|
||||
payload
|
||||
})
|
||||
if (r && r.ok === false) ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
if (sub === 'history') {
|
||||
const n = Math.max(1, Math.min(1000, parseInt(args[1] || '50', 10) || 50))
|
||||
const rows = bareP2pCollectFromHistory(ctx, 'peerctl', 2000).slice(-n)
|
||||
for (const row of rows) {
|
||||
ctx.console.log(
|
||||
'[' +
|
||||
bareP2pFmtClock(row.receivedAtMs) +
|
||||
'] ' +
|
||||
String(row.packet?.kind || '?') +
|
||||
' ' +
|
||||
JSON.stringify(row.packet?.payload || {})
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
ctx.console.error(argv0 + ': unsupported subcommand')
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
async function run(ctx, argv) {
|
||||
const argv0 = argv[0] || 'peerdiscover'
|
||||
const args = argv.slice(1)
|
||||
if (args.includes('-h') || args.includes('--help') || args.length === 0) {
|
||||
ctx.console.log(
|
||||
'usage: ' +
|
||||
argv0 +
|
||||
' announce SERVICE [META]\n' +
|
||||
' ' +
|
||||
argv0 +
|
||||
' list [N]\n' +
|
||||
'Peer service announce/list over p2p event history.\n' +
|
||||
'See man peerdiscover.'
|
||||
)
|
||||
if (args.length === 0) ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const sub = args[0]
|
||||
if (sub === 'announce') {
|
||||
const service = String(args[1] || '').trim()
|
||||
if (!service) {
|
||||
ctx.console.error(argv0 + ': announce requires SERVICE')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const meta = args.slice(2).join(' ').trim()
|
||||
const r = bareP2pSend(ctx, 'peerdiscover', 'service.announce', {
|
||||
service,
|
||||
meta,
|
||||
id: bareP2pId('svc')
|
||||
})
|
||||
if (r && r.ok === false) ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
if (sub === 'list') {
|
||||
const n = Math.max(1, Math.min(500, parseInt(args[1] || '50', 10) || 50))
|
||||
const rows = bareP2pCollectFromHistory(ctx, 'peerdiscover', 2000)
|
||||
.filter((r) => r.packet?.kind === 'service.announce')
|
||||
.slice(-n)
|
||||
for (const row of rows) {
|
||||
const p = row.packet?.payload || {}
|
||||
ctx.console.log(
|
||||
'[' +
|
||||
bareP2pFmtClock(row.receivedAtMs) +
|
||||
'] ' +
|
||||
String(p.service || 'unknown') +
|
||||
(p.meta ? ' ' + String(p.meta) : '')
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
ctx.console.error(argv0 + ': unsupported subcommand')
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
async function run(ctx, argv) {
|
||||
const argv0 = argv[0] || 'routeview'
|
||||
const args = argv.slice(1)
|
||||
if (args.includes('-h') || args.includes('--help')) {
|
||||
ctx.console.log(
|
||||
'usage: ' + argv0 + '\n' + 'Route and relay visibility TUI.\n' + 'See man routeview.'
|
||||
)
|
||||
return
|
||||
}
|
||||
const stdin = ctx.replStdin
|
||||
const isTTY = Boolean(stdin && /** @type {{ isTTY?: boolean }} */ (stdin).isTTY)
|
||||
if (!isTTY) {
|
||||
const r = await bareP2pReadProcJson(ctx, '/proc/bare_os/route_summary.json')
|
||||
ctx.console.log(JSON.stringify(r, null, 2))
|
||||
return
|
||||
}
|
||||
await bareP2pRunSimpleTui(ctx, {
|
||||
title: 'routeview',
|
||||
subtitle: 'direct vs relay summary · q quit · r refresh',
|
||||
renderLines: async () => {
|
||||
const r = await bareP2pReadProcJson(ctx, '/proc/bare_os/route_summary.json')
|
||||
return [
|
||||
'peers: ' + String(r.peers ?? '?'),
|
||||
'directLikely: ' + String(r.directLikely ?? '?'),
|
||||
'relayLikely: ' + String(r.relayLikely ?? '?'),
|
||||
'',
|
||||
'note: ' + String(r.note || '')
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
async function run(ctx, argv) {
|
||||
const argv0 = argv[0] || 'swarmdoctor'
|
||||
const args = argv.slice(1)
|
||||
if (args.includes('-h') || args.includes('--help')) {
|
||||
ctx.console.log(
|
||||
'usage: ' +
|
||||
argv0 +
|
||||
'\n' +
|
||||
'Print swarm diagnosis from /proc/bare_os/swarm_doctor.json.\n' +
|
||||
'See man swarmdoctor.'
|
||||
)
|
||||
return
|
||||
}
|
||||
const doc = await bareP2pReadProcJson(ctx, '/proc/bare_os/swarm_doctor.json')
|
||||
ctx.console.log(JSON.stringify(doc, null, 2))
|
||||
if (doc && typeof doc === 'object' && doc.health && doc.health !== 'ok') {
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
async function run(ctx, argv) {
|
||||
const argv0 = argv[0] || 'swarmmap'
|
||||
const args = argv.slice(1)
|
||||
if (args.includes('-h') || args.includes('--help')) {
|
||||
ctx.console.log(
|
||||
'usage: ' + argv0 + '\n' + 'Swarm topology map TUI.\n' + 'See man swarmmap.'
|
||||
)
|
||||
return
|
||||
}
|
||||
const stdin = ctx.replStdin
|
||||
const isTTY = Boolean(stdin && /** @type {{ isTTY?: boolean }} */ (stdin).isTTY)
|
||||
const snap = await bareP2pReadSwarmSnapshot(ctx)
|
||||
if (!isTTY) {
|
||||
ctx.console.log(JSON.stringify(snap, null, 2))
|
||||
return
|
||||
}
|
||||
await bareP2pRunSimpleTui(ctx, {
|
||||
title: 'swarmmap',
|
||||
subtitle: 'peer topology · q quit · r refresh',
|
||||
renderLines: async () => {
|
||||
const s = await bareP2pReadSwarmSnapshot(ctx)
|
||||
const lines = []
|
||||
lines.push('peerCount: ' + String(s.peerCount ?? '?'))
|
||||
lines.push('topicCount: ' + String(s.topicCount ?? '?'))
|
||||
lines.push('')
|
||||
lines.push('Peers:')
|
||||
const peers = Array.isArray(s.peers) ? s.peers : []
|
||||
if (!peers.length) lines.push(' (none)')
|
||||
for (const p of peers.slice(0, 48)) {
|
||||
const id =
|
||||
p && typeof p === 'object' && typeof p.remotePublicKey === 'string'
|
||||
? p.remotePublicKey.slice(0, 20)
|
||||
: '?'
|
||||
lines.push(' ' + id + ' ' + String(p.state || 'connected'))
|
||||
}
|
||||
return lines
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -85,16 +85,31 @@ async function run(ctx, argv) {
|
||||
lines.push('Peers:')
|
||||
const peers = Array.isArray(snap.peers) ? snap.peers : []
|
||||
if (!peers.length) lines.push(' (no peers reported)')
|
||||
for (const p of peers.slice(0, 24)) {
|
||||
for (const p of peers.slice(0, 12)) {
|
||||
const key =
|
||||
p && typeof p === 'object' && typeof p.remotePublicKey === 'string'
|
||||
? p.remotePublicKey.slice(0, 16)
|
||||
? p.remotePublicKey
|
||||
: '?'
|
||||
const state =
|
||||
p && typeof p === 'object' && typeof p.state === 'string'
|
||||
? p.state
|
||||
: 'connected'
|
||||
lines.push(' ' + key + ' ' + state)
|
||||
const addr =
|
||||
p && typeof p === 'object' && p.endpoint && typeof p.endpoint === 'object'
|
||||
? String(p.endpoint.address || p.remoteAddress || '?') +
|
||||
':' +
|
||||
String(p.endpoint.port || p.remotePort || '?')
|
||||
: String(p?.remoteAddress || '?') + ':' + String(p?.remotePort || '?')
|
||||
const osHint =
|
||||
p &&
|
||||
typeof p === 'object' &&
|
||||
p.peerOs &&
|
||||
typeof p.peerOs === 'object' &&
|
||||
p.peerOs.osHint
|
||||
? String(p.peerOs.osHint)
|
||||
: 'unknown'
|
||||
lines.push(' pubkey: ' + key)
|
||||
lines.push(' state=' + state + ' endpoint=' + addr + ' os=' + osHint)
|
||||
}
|
||||
lines.push('')
|
||||
lines.push('Recent events:')
|
||||
|
||||
Reference in New Issue
Block a user