Implement robust auto-update system for GuruConnect agent
Features: - Agent checks for updates periodically (hourly) during idle - Admin can trigger immediate updates via dashboard "Update Agent" button - Silent updates with in-place binary replacement (no reboot required) - SHA-256 checksum verification before installation - Semantic version comparison Server changes: - New releases table for tracking available versions - GET /api/version endpoint for agent polling (unauthenticated) - POST /api/machines/:id/update endpoint for admin push updates - Release management API (/api/releases CRUD) - Track agent_version in machine status Agent changes: - New update.rs module with download/verify/install/restart logic - Handle ADMIN_UPDATE WebSocket command for push updates - --post-update flag for cleanup after successful update - Periodic update check in idle loop (persistent agents only) - agent_version included in AgentStatus messages Dashboard changes: - Version display in machine detail panel - "Update Agent" button for each connected machine 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -908,6 +908,7 @@
|
||||
const statusText = m.is_online ? 'Online' : 'Offline';
|
||||
const connectDisabled = m.is_online ? '' : 'disabled';
|
||||
const connectTitle = m.is_online ? '' : 'title="Agent is offline"';
|
||||
const versionText = m.agent_version || 'Unknown';
|
||||
|
||||
container.innerHTML =
|
||||
'<div class="detail-section">' +
|
||||
@@ -915,6 +916,7 @@
|
||||
'<div class="detail-row"><span class="detail-label">Status</span><span class="detail-value" style="color: ' + statusColor + ';">' + statusText + '</span></div>' +
|
||||
'<div class="detail-row"><span class="detail-label">Agent ID</span><span class="detail-value">' + m.agent_id.slice(0,8) + '...</span></div>' +
|
||||
'<div class="detail-row"><span class="detail-label">Session ID</span><span class="detail-value">' + m.id.slice(0,8) + '...</span></div>' +
|
||||
'<div class="detail-row"><span class="detail-label">Version</span><span class="detail-value">' + escapeHtml(versionText) + '</span></div>' +
|
||||
'<div class="detail-row"><span class="detail-label">Connected</span><span class="detail-value">' + started + '</span></div>' +
|
||||
'<div class="detail-row"><span class="detail-label">Viewers</span><span class="detail-value">' + m.viewer_count + '</span></div>' +
|
||||
'</div>' +
|
||||
@@ -923,6 +925,7 @@
|
||||
'<button class="btn btn-primary" style="width: 100%; margin-bottom: 8px;" onclick="connectToMachine(\'' + m.id + '\')" ' + connectDisabled + ' ' + connectTitle + '>Connect</button>' +
|
||||
'<button class="btn btn-outline" style="width: 100%; margin-bottom: 8px;" onclick="openChat(\'' + m.id + '\', \'' + (m.agent_name || 'Client').replace(/'/g, "\\'") + '\')" ' + connectDisabled + '>Chat</button>' +
|
||||
'<button class="btn btn-outline" style="width: 100%; margin-bottom: 8px;" disabled>Transfer Files</button>' +
|
||||
'<button class="btn btn-outline" style="width: 100%; margin-bottom: 8px;" onclick="triggerUpdate(\'' + m.agent_id + '\', \'' + (m.agent_name || m.agent_id).replace(/'/g, "\\'") + '\')" ' + connectDisabled + '>Update Agent</button>' +
|
||||
'<button class="btn btn-outline" style="width: 100%; color: hsl(0, 62.8%, 50%);" onclick="disconnectMachine(\'' + m.id + '\', \'' + (m.agent_name || m.agent_id).replace(/'/g, "\\'") + '\')">Disconnect</button>' +
|
||||
'</div>';
|
||||
}
|
||||
@@ -1043,6 +1046,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function triggerUpdate(agentId, machineName) {
|
||||
if (!confirm("Send update command to " + machineName + "?\n\nThe agent will download and install the latest version, then restart.")) return;
|
||||
try {
|
||||
const response = await fetch("/api/machines/" + agentId + "/update", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" }
|
||||
});
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
alert("Update command sent to " + machineName + ".\n\n" + (result.message || "Agent will update shortly."));
|
||||
} else {
|
||||
const errorText = await response.text();
|
||||
alert("Failed to trigger update: " + errorText);
|
||||
}
|
||||
} catch (err) {
|
||||
alert("Error triggering update: " + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh machines every 5 seconds
|
||||
loadMachines();
|
||||
setInterval(loadMachines, 5000);
|
||||
|
||||
Reference in New Issue
Block a user