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
- | HOSTNAME | TYPE | IP | STATUS | CAPABILITIES | LAST SEEN | ACTIONS |
-
| LOADING... |
+ HOSTNAME | TYPE | IP | STATUS | VERSION | CAPABILITIES | LAST SEEN | ACTIONS |
+ | LOADING... |
CRON WORKERS
| WORKER | SCHEDULE | HOST | LAST RUN | ACTIONS |
@@ -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='
| NO AGENTS |
';
+ agTbody.innerHTML='| NO AGENTS |
';
} 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 `
| ${dot}${ag.hostname} |
${ag.agent_type||'linux'} |
${ag.ip_address||'—'} |
${dot}${on?'ONLINE':'OFFLINE'} |
+ ${verHtml} |
${capHtml||'—'} |
${wAgo(ag.last_seen)} |
- ${shotBtn} |
+ ${updBtn}${shotBtn} |
`;
}).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