From 0469b3182994becf9d5c4df71c68ff81f8b632ef Mon Sep 17 00:00:00 2001 From: Myron Blair Date: Fri, 12 Jun 2026 01:44:59 +0000 Subject: [PATCH] =?UTF-8?q?Agent=20version=20tracking=20=E2=80=94=20worker?= =?UTF-8?q?s=20tab=20shows=20current=20vs=20latest=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add version column to registered_agents table - Agents send version on registration (Linux 3.1, Windows 3.0, macOS 3.0) - workers_list API returns latest_versions per platform - Workers tab: VERSION column with green check (up-to-date) or red (outdated) - Outdated agents highlight row and show blue UPDATE button - Up-to-date agents show dimmed UPDATE button - Update button dispatches update command immediately Co-Authored-By: Claude Sonnet 4.6 --- agent/jarvis-agent-mac.py | 2 +- agent/jarvis-agent-windows.py | 2 +- agent/jarvis-agent.py | 1 + api/endpoints/agent.php | 11 ++--- public_html/admin/index.php | 41 +++++++++++++++---- public_html/agent/jarvis-agent-mac.py | 2 +- public_html/agent/jarvis-agent-mac.py.sha256 | 2 +- public_html/agent/jarvis-agent-windows.py | 2 +- .../agent/jarvis-agent-windows.py.sha256 | 2 +- public_html/agent/jarvis-agent.py | 1 + public_html/agent/jarvis-agent.py.sha256 | 2 +- 11 files changed, 49 insertions(+), 19 deletions(-) diff --git a/agent/jarvis-agent-mac.py b/agent/jarvis-agent-mac.py index 6bd487a..69f9247 100644 --- a/agent/jarvis-agent-mac.py +++ b/agent/jarvis-agent-mac.py @@ -291,7 +291,7 @@ def register(cfg: dict, state: dict) -> str: result = api_post( f"{cfg['jarvis_url']}/api/agent/register", - {"hostname": hostname, "agent_type": agent_type, "ip_address": ip, + {"hostname": hostname, "version": AGENT_VERSION, "agent_type": agent_type, "ip_address": ip, "capabilities": capabilities, "agent_id": agent_id}, headers={"X-Registration-Key": cfg["registration_key"]}, ssl_verify=ssl_verify, diff --git a/agent/jarvis-agent-windows.py b/agent/jarvis-agent-windows.py index d66655c..0b5c9ec 100644 --- a/agent/jarvis-agent-windows.py +++ b/agent/jarvis-agent-windows.py @@ -241,7 +241,7 @@ def register(cfg: dict, state: dict) -> str: result = api_post( f"{cfg['jarvis_url']}/api/agent/register", - {"hostname": hostname, "agent_type": agent_type, "ip_address": ip, + {"hostname": hostname, "version": AGENT_VERSION, "agent_type": agent_type, "ip_address": ip, "capabilities": capabilities, "agent_id": agent_id}, headers={"X-Registration-Key": cfg["registration_key"]}, ssl_verify=ssl_verify, diff --git a/agent/jarvis-agent.py b/agent/jarvis-agent.py index 4494c7a..53bbef7 100755 --- a/agent/jarvis-agent.py +++ b/agent/jarvis-agent.py @@ -136,6 +136,7 @@ def register(cfg: dict, state: dict) -> str: f"{cfg['jarvis_url']}/api/agent/register", { "hostname": hostname, + "version": AGENT_VERSION, "agent_type": agent_type, "ip_address": ip, "capabilities": capabilities, diff --git a/api/endpoints/agent.php b/api/endpoints/agent.php index c1cd30f..288b8da 100644 --- a/api/endpoints/agent.php +++ b/api/endpoints/agent.php @@ -79,23 +79,24 @@ switch ($agentAction) { $ipAddress = $data['ip_address'] ?? ($_SERVER['REMOTE_ADDR'] ?? ''); $capabilities = $data['capabilities'] ?? []; $agentId = $data['agent_id'] ?? ($hostname . '_' . substr(md5($hostname . $ipAddress), 0, 8)); + $version = trim($data['version'] ?? ''); if (!$hostname) agent_error(400, 'hostname required'); - if (!in_array($agentType, ['linux', 'homeassistant', 'proxmox', 'windows'])) agent_error(400, 'Invalid agent_type'); + if (!in_array($agentType, ['linux', 'homeassistant', 'proxmox', 'windows', 'macos'])) agent_error(400, 'Invalid agent_type'); // Upsert agent $existing = JarvisDB::query('SELECT api_key FROM registered_agents WHERE agent_id = ?', [$agentId]); if ($existing) { $apiKey = $existing[0]['api_key']; JarvisDB::query( - 'UPDATE registered_agents SET hostname=?, agent_type=?, ip_address=?, capabilities=?, last_seen=NOW(), status="online" WHERE agent_id=?', - [$hostname, $agentType, $ipAddress, json_encode($capabilities), $agentId] + 'UPDATE registered_agents SET hostname=?, agent_type=?, ip_address=?, capabilities=?, version=?, last_seen=NOW(), status="online" WHERE agent_id=?', + [$hostname, $agentType, $ipAddress, json_encode($capabilities), $version ?: null, $agentId] ); } else { $apiKey = generate_api_key(); JarvisDB::query( - 'INSERT INTO registered_agents (agent_id, hostname, agent_type, ip_address, api_key, capabilities, last_seen, status) VALUES (?,?,?,?,?,?,NOW(),"online")', - [$agentId, $hostname, $agentType, $ipAddress, $apiKey, json_encode($capabilities)] + 'INSERT INTO registered_agents (agent_id, hostname, agent_type, ip_address, api_key, capabilities, version, last_seen, status) VALUES (?,?,?,?,?,?,?,NOW(),"online")', + [$agentId, $hostname, $agentType, $ipAddress, $apiKey, json_encode($capabilities), $version ?: null] ); } diff --git a/public_html/admin/index.php b/public_html/admin/index.php index efa6888..ac084b9 100644 --- a/public_html/admin/index.php +++ b/public_html/admin/index.php @@ -467,9 +467,17 @@ if ($action) { case 'workers_list': $agents = JarvisDB::query( - 'SELECT agent_id, hostname, agent_type, ip_address, status, capabilities, last_seen + 'SELECT agent_id, hostname, agent_type, ip_address, status, capabilities, version, last_seen FROM registered_agents ORDER BY status DESC, hostname ASC' ); + // Latest available versions per platform + $latestVersions = [ + 'linux' => '3.1', + 'proxmox' => '3.1', + 'windows' => '3.0', + 'macos' => '3.0', + 'homeassistant' => null, + ]; $reactorRaw = @file_get_contents('http://127.0.0.1:7474/status'); $reactor = $reactorRaw ? json_decode($reactorRaw, true) : null; $arcStats = JarvisDB::query( @@ -506,7 +514,7 @@ if ($action) { } $doLog = '/var/log/do-server-backup.log'; if (file_exists($doLog)) $cronLast['do_server_backup'] = date('Y-m-d H:i:s', filemtime($doLog)); - j(['agents'=>$agents,'reactor'=>$reactor,'arc_counts'=>$arcCounts,'cron_last'=>$cronLast]); + j(['agents'=>$agents,'reactor'=>$reactor,'arc_counts'=>$arcCounts,'cron_last'=>$cronLast,'latest_versions'=>$latestVersions]); break; case 'worker_action': @@ -1343,8 +1351,8 @@ select.filter-sel:focus{border-color:var(--cyan)}
FIELD AGENTS
- -
HOSTNAMETYPEIPSTATUSCAPABILITIESLAST SEENACTIONS
LOADING...
+ HOSTNAMETYPEIPSTATUSVERSIONCAPABILITIESLAST SEENACTIONS + LOADING...
CRON WORKERS
@@ -2171,10 +2179,11 @@ async function workerAction(type,id,action) { async function loadWorkers() { const d=await api('workers_list'); if(!d||d.error) return; + const latestVer = d.latest_versions || {}; // Field Agents const agTbody=document.getElementById('workers-agents'); if(!d.agents||!d.agents.length){ - agTbody.innerHTML=''; + agTbody.innerHTML=''; } else { agTbody.innerHTML=d.agents.map(ag=>{ const on=ag.status==='online'; @@ -2185,14 +2194,32 @@ async function loadWorkers() { return `${c.toUpperCase()}`; }).join(''); const shotBtn=caps.includes('screenshot')?``:''; - return ` + // Version column + const curVer = ag.version || null; + const latVer = latestVer[ag.agent_type] || null; + let verHtml; + if (!latVer) { + verHtml = ''; + } else if (!curVer) { + verHtml = `? / ${latVer}`; + } else if (curVer === latVer) { + verHtml = `v${curVer} ✓`; + } else { + verHtml = `v${curVer} → v${latVer}`; + } + const needsUpdate = latVer && curVer !== latVer; + const updBtn = caps.includes('commands') + ? `` + : ''; + return ` + - + `; }).join(''); } diff --git a/public_html/agent/jarvis-agent-mac.py b/public_html/agent/jarvis-agent-mac.py index 6bd487a..69f9247 100644 --- a/public_html/agent/jarvis-agent-mac.py +++ b/public_html/agent/jarvis-agent-mac.py @@ -291,7 +291,7 @@ def register(cfg: dict, state: dict) -> str: result = api_post( f"{cfg['jarvis_url']}/api/agent/register", - {"hostname": hostname, "agent_type": agent_type, "ip_address": ip, + {"hostname": hostname, "version": AGENT_VERSION, "agent_type": agent_type, "ip_address": ip, "capabilities": capabilities, "agent_id": agent_id}, headers={"X-Registration-Key": cfg["registration_key"]}, ssl_verify=ssl_verify, diff --git a/public_html/agent/jarvis-agent-mac.py.sha256 b/public_html/agent/jarvis-agent-mac.py.sha256 index dc36921..85dd380 100644 --- a/public_html/agent/jarvis-agent-mac.py.sha256 +++ b/public_html/agent/jarvis-agent-mac.py.sha256 @@ -1 +1 @@ -d0aeaab1686f788c9d46ffeaac57a42e3e82b80c2d7fac2be443c05cf70c87be jarvis-agent-mac.py +6a0cb7a876f0d4ba36c5f7aaf6a80d6224b52d6e23ac32d6cd9f8c03ca9d8062 jarvis-agent-mac.py diff --git a/public_html/agent/jarvis-agent-windows.py b/public_html/agent/jarvis-agent-windows.py index d66655c..0b5c9ec 100644 --- a/public_html/agent/jarvis-agent-windows.py +++ b/public_html/agent/jarvis-agent-windows.py @@ -241,7 +241,7 @@ def register(cfg: dict, state: dict) -> str: result = api_post( f"{cfg['jarvis_url']}/api/agent/register", - {"hostname": hostname, "agent_type": agent_type, "ip_address": ip, + {"hostname": hostname, "version": AGENT_VERSION, "agent_type": agent_type, "ip_address": ip, "capabilities": capabilities, "agent_id": agent_id}, headers={"X-Registration-Key": cfg["registration_key"]}, ssl_verify=ssl_verify, diff --git a/public_html/agent/jarvis-agent-windows.py.sha256 b/public_html/agent/jarvis-agent-windows.py.sha256 index f4a888c..919cf24 100644 --- a/public_html/agent/jarvis-agent-windows.py.sha256 +++ b/public_html/agent/jarvis-agent-windows.py.sha256 @@ -1 +1 @@ -feadcb033426838f0d9e87df933fc3cd6b09b0d74eea7dc697b29a12421a1f2d jarvis-agent-windows.py +1232a5ffa6dda93fca04878952d26e889fde5db479b9f23ea35111be2c3f77f5 jarvis-agent-windows.py diff --git a/public_html/agent/jarvis-agent.py b/public_html/agent/jarvis-agent.py index 4494c7a..53bbef7 100644 --- a/public_html/agent/jarvis-agent.py +++ b/public_html/agent/jarvis-agent.py @@ -136,6 +136,7 @@ def register(cfg: dict, state: dict) -> str: f"{cfg['jarvis_url']}/api/agent/register", { "hostname": hostname, + "version": AGENT_VERSION, "agent_type": agent_type, "ip_address": ip, "capabilities": capabilities, diff --git a/public_html/agent/jarvis-agent.py.sha256 b/public_html/agent/jarvis-agent.py.sha256 index b7f4083..c3b619a 100644 --- a/public_html/agent/jarvis-agent.py.sha256 +++ b/public_html/agent/jarvis-agent.py.sha256 @@ -1 +1 @@ -bccfeed43d7fbcd5f002656e866432b8aaa5035bc797f855727f50147f609427 jarvis-agent.py +1a9e8e24e5aee8f27a5900b6340373023ff2171e844e71e451eecdbf3b2b0f03 jarvis-agent.py
WORKERSCHEDULEHOSTLAST RUNACTIONS
NO AGENTS
NO AGENTS
${dot}${ag.hostname} ${ag.agent_type||'linux'} ${ag.ip_address||'—'} ${dot}${on?'ONLINE':'OFFLINE'}${verHtml} ${capHtml||''} ${wAgo(ag.last_seen)}${shotBtn}${updBtn}${shotBtn}