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:
2025-12-30 09:31:23 -07:00
parent 7df824c2ca
commit 4e5328fe4a
15 changed files with 1399 additions and 18 deletions

View File

@@ -23,6 +23,7 @@ mod session;
mod startup;
mod transport;
mod tray;
mod update;
mod viewer;
pub mod proto {
@@ -118,6 +119,10 @@ struct Cli {
/// Enable verbose logging
#[arg(short, long, global = true)]
verbose: bool,
/// Internal flag: set after auto-update to trigger cleanup
#[arg(long, hide = true)]
post_update: bool,
}
#[derive(Subcommand)]
@@ -182,6 +187,12 @@ fn main() -> Result<()> {
info!("GuruConnect {} ({})", build_info::short_version(), build_info::BUILD_TARGET);
info!("Built: {} | Commit: {}", build_info::BUILD_TIMESTAMP, build_info::GIT_COMMIT_DATE);
// Handle post-update cleanup
if cli.post_update {
info!("Post-update mode: cleaning up old executable");
update::cleanup_post_update();
}
match cli.command {
Some(Commands::Agent { code }) => {
run_agent_mode(code)