diff --git a/frontend/dashboard.html b/frontend/dashboard.html index 80b903a..79f153c 100644 --- a/frontend/dashboard.html +++ b/frontend/dashboard.html @@ -157,6 +157,9 @@
Connecting... +
๐Ÿ–ฅ 0 Agents @@ -905,8 +908,33 @@ initSplitter('splitter-1', '.col-left', 160); // splitter-2: drag to resize col-right (min 300px) initSplitter('splitter-2', '.col-right', 300); +// โ”€โ”€ Version check โ”€โ”€ +async function checkForUpdates() { + try { + const r = await fetch('/api/version'); + const v = await r.json(); + if (v.update_available) { + document.getElementById('update-banner').style.display = 'inline'; + document.getElementById('update-count').textContent = v.commits_behind; + } + } catch(e) {} +} + +async function runGitPull() { + toast('Pulling latest code...'); + try { + const r = await api('POST', '/api/git/pull'); + toast(r.message || 'Updated! Please restart the server.'); + document.getElementById('update-banner').style.display = 'none'; + } catch(e) { + toast('Pull failed. Run manually: cd OpsPilot && git pull'); + } +} + // Init connect(); +checkForUpdates(); +setInterval(checkForUpdates, 300000); // Check every 5 min setInterval(() => { if (ws && ws.readyState === WebSocket.OPEN) ws.send('ping'); }, 8000); setInterval(() => { document.getElementById('stat-time').textContent = new Date().toLocaleTimeString('zh-CN'); diff --git a/server.py b/server.py index ea6abcc..0938e2c 100644 --- a/server.py +++ b/server.py @@ -140,6 +140,57 @@ async def get_state(): return await _build_state() +@app.post("/api/git/pull") +async def git_pull(): + """Pull latest code from GitHub.""" + import subprocess as _sp + try: + r = _sp.run(["git", "pull", "origin", "main"], capture_output=True, text=True, cwd=str(BASE_DIR), timeout=30) + add_log("info", "update", f"git pull: {r.stdout.strip() or r.stderr.strip()}") + if "Already up to date" in r.stdout or "Already up-to-date" in r.stdout: + return {"status": "ok", "message": "Already up to date"} + return {"status": "ok", "message": r.stdout.strip() or "Updated"} + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@app.get("/api/version") +async def check_version(): + """Check current version and compare with latest GitHub release.""" + import subprocess as _sp + info = { + "current_commit": "", + "current_short": "", + "latest_commit": "", + "latest_short": "", + "update_available": False, + "commits_behind": 0, + } + try: + # Get current commit + r = _sp.run(["git", "rev-parse", "HEAD"], capture_output=True, text=True, cwd=str(BASE_DIR), timeout=5) + if r.returncode == 0: + info["current_commit"] = r.stdout.strip() + info["current_short"] = info["current_commit"][:7] + # Get latest from remote (without pulling) + r2 = _sp.run(["git", "ls-remote", "origin", "HEAD"], capture_output=True, text=True, timeout=10) + if r2.returncode == 0 and r2.stdout.strip(): + info["latest_commit"] = r2.stdout.split()[0] + info["latest_short"] = info["latest_commit"][:7] + if info["current_commit"] and info["latest_commit"] and info["current_commit"] != info["latest_commit"]: + # Count commits behind + r3 = _sp.run( + ["git", "rev-list", "--count", f"{info['current_commit']}..{info['latest_commit']}"], + capture_output=True, text=True, timeout=5 + ) + if r3.returncode == 0: + info["commits_behind"] = int(r3.stdout.strip()) + info["update_available"] = info["commits_behind"] > 0 + except Exception: + pass + return info + + @app.post("/api/agents") async def register_agent(data: dict): """Register or update an agent."""