rover self updating!

This commit is contained in:
legop3
2026-06-05 15:09:10 -04:00
parent 870de4f135
commit c097d94ba8
14 changed files with 359 additions and 132 deletions
@@ -26,6 +26,7 @@ export default function AdminPanelContent() {
setGlobalObjective,
setAdminReason,
rebootRover,
updateRover,
rebootServer,
setAudioLevels,
setPrivateSafety,
@@ -38,6 +39,7 @@ export default function AdminPanelContent() {
const roster = useMemo(() => session?.roster ?? [], [session?.roster]);
const [lockStates, setLockStates] = useState({});
const [rebootStates, setRebootStates] = useState({});
const [updateStates, setUpdateStates] = useState({});
const [serverRebooting, setServerRebooting] = useState(false);
const [clearingLlmHistory, setClearingLlmHistory] = useState(false);
const [clearingOverseerHistory, setClearingOverseerHistory] = useState(false);
@@ -104,6 +106,27 @@ export default function AdminPanelContent() {
}
};
const handleUpdate = async (rover) => {
if (!rover?.id) return;
const ok = window.confirm(
`Update rover "${rover.name || rover.id}" now? The rover will git pull the repo, run the roverd installer, and may disconnect while services restart.`,
);
if (!ok) return;
setUpdateStates((prev) => ({ ...prev, [rover.id]: true }));
try {
// The rover acknowledges once the privileged self-update helper has been
// launched, not when the full install finishes. That is deliberate: a
// successful installer run restarts roverd, so waiting for completion over
// the same websocket would make the button look failed even when the
// update is doing exactly what it should.
await updateRover(rover.id);
} catch (err) {
alert(err.message);
} finally {
setUpdateStates((prev) => ({ ...prev, [rover.id]: false }));
}
};
const handleServerReboot = async () => {
const ok = window.confirm('Reboot the server host now? This will disconnect all users.');
if (!ok) return;
@@ -435,6 +458,14 @@ export default function AdminPanelContent() {
>
{rebootStates[rover.id] ? 'Rebooting...' : 'Reboot'}
</button>
<button
type="button"
onClick={() => handleUpdate(rover)}
disabled={Boolean(updateStates[rover.id])}
className="button-danger disabled:cursor-not-allowed disabled:opacity-60"
>
{updateStates[rover.id] ? 'Updating...' : 'Update'}
</button>
{rover?.private?.enabled ? (
<div className="w-full rounded border border-slate-700/70 p-0.5 space-y-0.5 text-[0.7rem]">
<div className="text-slate-300">Private safety</div>
+5
View File
@@ -195,6 +195,11 @@ export function SessionProvider({ children }) {
setAdminReason: (text) => emitWithAck('adminReason:set', { text }),
rebootRover: (roverId) =>
emitWithAck('command', { roverId, type: 'reboot', data: { reboot: {} } }),
// Rover updates are intentionally parameter-free from the browser. The Pi
// side owns the git pull + installer sequence so the admin UI can request
// maintenance without becoming a remote shell.
updateRover: (roverId) =>
emitWithAck('command', { roverId, type: 'update', data: { update: {} } }),
rebootServer: () => emitWithAck('server:reboot'),
playUploadedAudio: ({ roverId, name, mime, dataBase64 }) =>
emitWithAck('audio:uploadPlay', { roverId, name, mime, dataBase64 }),