#!/usr/bin/env node /** * DStatus macOS Agent - Mac mini M2 * 模拟 Go agent 上报格式: POST /stats/update, Header Key, Body {sid, data} */ const os = require('os'); const https = require('https'); const http = require('http'); const { execSync } = require('child_process'); const CONFIG = { server: 'http://51.81.222.43:5555', apiKey: 'macmini_report_key_2026', sid: '3b24c3cb-d06b-4747-83a8-a4c1ca11d4df', interval: 2, device: 'en0' }; let prevRx = 0, prevTx = 0, prevTime = 0, firstRx = 0, firstTx = 0; function getCpu() { try { const out = execSync("ps -A -o %cpu | awk '{s+=$1} END {print s/100}'", { encoding: 'utf8', timeout: 3000 }); const v = parseFloat(out.trim()) || 0; return { multi: Math.min(v, 1), single: os.cpus().map(() => Math.min(v, 1)) }; } catch { return { multi: 0, single: [0] }; } } function getDisk() { try { const out = execSync("df -k / | tail -1", { encoding: 'utf8', timeout: 3000 }); const p = out.trim().split(/\s+/); const total = parseInt(p[1]) * 1024; const used = parseInt(p[2]) * 1024; const free = parseInt(p[3]) * 1024; const pct = parseFloat(p[4]) || 0; return { free, total, used, disks: [{ device: p[0], free, total, used, fstype: 'apfs', mount: '/', percent: pct }] }; } catch { return { free: 0, total: 0, used: 0, disks: [] }; } } function getMem() { const total = os.totalmem(); const free = os.freemem(); const used = total - free; return { virtual: { total, available: free, used, usedPercent: (used / total) * 100, free, active: 0, inactive: 0, wired: 0, laundry: 0, buffers: 0, cached: 0, writeBack: 0, dirty: 0, writeBackTmp: 0, shared: 0, slab: 0, sreclaimable: 0, sunreclaim: 0, pageTables: 0, swapCached: 0, commitLimit: 0, committedAS: 0, highTotal: 0, highFree: 0, lowTotal: 0, lowFree: 0, swapTotal: 0, swapFree: 0, mapped: 0, vmallocTotal: 0, vmallocUsed: 0, vmallocChunk: 0, hugePagesTotal: 0, hugePagesFree: 0, hugePagesRsvd: 0, hugePagesSurp: 0, hugePageSize: 0, anonHugePages: 0 }, swap: { total: 0, used: 0, free: 0, usedPercent: 0, sin: 0, sout: 0, pgIn: 0, pgOut: 0, pgFault: 0, pgMajFault: 0 } }; } function getNet() { try { const out = execSync(`netstat -ibI ${CONFIG.device} | awk 'NR==2{print $7,$10}'`, { encoding: 'utf8', timeout: 3000 }); const [rx, tx] = out.trim().split(/\s+/).map(Number); const now = Date.now(); if (!firstRx) { firstRx = rx; firstTx = tx; } const deltaIn = prevRx ? Math.max(0, (rx || 0) - prevRx) : 0; const deltaOut = prevTx ? Math.max(0, (tx || 0) - prevTx) : 0; prevRx = rx || 0; prevTx = tx || 0; prevTime = now; return { delta: { in: deltaIn, out: deltaOut }, total: { in: (rx || 0) - firstRx, out: (tx || 0) - firstTx } }; } catch { return { delta: { in: 0, out: 0 }, total: { in: 0, out: 0 } }; } } function getHost() { return { hostname: os.hostname(), uptime: Math.floor(os.uptime()), bootTime: Math.floor(Date.now() / 1000 - os.uptime()), procs: 0, os: 'darwin', platform: 'macOS', platformFamily: 'darwin', platformVersion: os.release(), kernelVersion: os.release(), kernelArch: os.arch() === 'arm64' ? 'aarch64' : os.arch(), virtualizationSystem: '', virtualizationRole: '', hostId: CONFIG.sid }; } function collectData() { const cpu = getCpu(); const disk = getDisk(); const mem = getMem(); const net = getNet(); const host = getHost(); return { cpu, disk: { free: disk.free, total: disk.total, used: disk.used }, disks: disk.disks, host, hostname: os.hostname(), mem, net, timestamp: Date.now() }; } function report() { const data = collectData(); const body = JSON.stringify({ sid: CONFIG.sid, data }); const mod = CONFIG.server.startsWith('https') ? https : http; const req = mod.request(`${CONFIG.server}/stats/update`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), 'Key': CONFIG.apiKey } }, res => { let d = ''; res.on('data', c => d += c); res.on('end', () => { try { const r = JSON.parse(d); if (r.success || r.status === 1) console.log(`[${new Date().toLocaleTimeString()}] ✅ 上报成功`); else console.log(`[${new Date().toLocaleTimeString()}] ❌`, d.substring(0, 100)); } catch { console.log(`[${new Date().toLocaleTimeString()}] ❌ 解析失败:`, d.substring(0, 100)); } }); }); req.on('error', e => console.error('上报错误:', e.message)); req.write(body); req.end(); } console.log('DStatus macOS Agent | SID:', CONFIG.sid); report(); setInterval(report, CONFIG.interval * 1000);