diff --git a/services/node-agent/index.js b/services/node-agent/index.js index 5d80378..79a8dc1 100644 --- a/services/node-agent/index.js +++ b/services/node-agent/index.js @@ -1,5 +1,6 @@ import express from 'express'; import os from 'os'; +import fs from 'fs'; const MAM_API_URL = (process.env.MAM_API_URL || 'http://localhost:3000').replace(/\/$/, ''); const NODE_TOKEN = process.env.NODE_TOKEN || ''; @@ -49,12 +50,48 @@ function getIp() { return null; } +// ── Hardware detection ──────────────────────────────────────────────────── +// GPU_COUNT env var overrides filesystem detection (useful when /dev not mapped into container) +// BMD_COUNT env var similarly overrides Blackmagic DeckLink detection +function detectHardware() { + const capabilities = { gpus: [], blackmagic: [] }; + + const gpuOverride = parseInt(process.env.GPU_COUNT || '-1', 10); + if (gpuOverride >= 0) { + for (let i = 0; i < gpuOverride; i++) { + capabilities.gpus.push({ device: `/dev/nvidia${i}`, type: 'nvidia', index: i }); + } + } else { + for (let i = 0; i < 16; i++) { + try { + fs.accessSync(`/dev/nvidia${i}`, fs.constants.F_OK); + capabilities.gpus.push({ device: `/dev/nvidia${i}`, type: 'nvidia', index: i }); + } catch (_) { break; } + } + } + + const bmdOverride = parseInt(process.env.BMD_COUNT || '-1', 10); + if (bmdOverride >= 0) { + for (let i = 0; i < bmdOverride; i++) { + capabilities.blackmagic.push({ device: `/dev/blackmagic/dv${i}`, index: i }); + } + } else { + try { + const bmdEntries = fs.readdirSync('/dev/blackmagic'); + capabilities.blackmagic = bmdEntries.map(d => ({ device: `/dev/blackmagic/${d}` })); + } catch (_) {} + } + + return capabilities; +} + // ── Heartbeat ───────────────────────────────────────────────────────────── async function heartbeat() { - const cpu_usage = await sampleCpu(); - const totalMem = os.totalmem(); - const freeMem = os.freemem(); - const ip_address = getIp(); + const cpu_usage = await sampleCpu(); + const totalMem = os.totalmem(); + const freeMem = os.freemem(); + const ip_address = getIp(); + const capabilities = detectHardware(); const payload = { hostname: os.hostname(), @@ -65,6 +102,7 @@ async function heartbeat() { cpu_usage, mem_used_mb: Math.round((totalMem - freeMem) / 1048576), mem_total_mb: Math.round(totalMem / 1048576), + capabilities, }; const headers = { 'Content-Type': 'application/json' }; @@ -78,9 +116,11 @@ async function heartbeat() { signal: AbortSignal.timeout(8000), }); if (res.ok) { + const gpuStr = capabilities.gpus.length ? ` gpu=${capabilities.gpus.length}` : ''; + const bmdStr = capabilities.blackmagic.length ? ` bmd=${capabilities.blackmagic.length}` : ''; process.stdout.write( `[hb] ${payload.hostname} cpu=${cpu_usage}% ` + - `mem=${payload.mem_used_mb}/${payload.mem_total_mb}MB\n` + `mem=${payload.mem_used_mb}/${payload.mem_total_mb}MB${gpuStr}${bmdStr}\n` ); } else { const txt = await res.text().catch(() => '');