electron/cua-restart.mjs
Rama main · réplica de lectura del repositorio original.
// Computer control's helper can die while the app runs. It is started again by itself a bounded number of times, and
// what is true meanwhile is what readiness says.
export const HELPER_RESTARTING = "Computer control stopped because its helper crashed. OpenMausBot is restarting it.";
export const HELPER_KEEPS_CRASHING =
"Computer control's helper keeps crashing, so OpenMausBot stopped restarting it. Quit and reopen OpenMausBot to try again.";
export const RESTART_WAITS_MS = [1_000, 5_000, 15_000];
/** A helper that ran this long was fine: the next crash starts a new count. */
export const STABLE_AFTER_MS = 120_000;
/**
* `restart()` resolves true when a working helper is back, false when it is not, and "needs-person" when only the person
* can fix why (the reason already published stays). `report(reason)` publishes why computer control is down.
* `started()` after any start that gave a working helper; `exited(detail)` when a working one died; `reset()` when the
* person restarts it or the app stops it, which cancels a waiting restart and forgets the count.
*/
export function createHelperRestarts({
restart,
report,
log = () => {},
now = Date.now,
waitsMs = RESTART_WAITS_MS,
stableAfterMs = STABLE_AFTER_MS,
setTimer = setTimeout,
clearTimer = clearTimeout,
}) {
let failures = 0;
let startedAt = null;
let timer = null;
let epoch = 0;
const tryAgain = () => {
if (failures >= waitsMs.length) {
log(`helper kept crashing; stopped after ${failures} restarts`);
report(HELPER_KEEPS_CRASHING);
return;
}
const wait = waitsMs[failures++];
const run = ++epoch;
report(HELPER_RESTARTING);
timer = setTimer(async () => {
timer = null;
let back = false;
try {
back = await restart();
} catch (error) {
log(`helper restart failed: ${error?.message ?? error}`);
}
if (run !== epoch || back === "needs-person") return;
if (back) startedAt = now();
else tryAgain();
}, wait);
timer.unref?.();
};
return {
started() {
startedAt = now();
},
exited(detail) {
if (startedAt !== null && now() - startedAt >= stableAfterMs) failures = 0;
startedAt = null;
log(`helper exited: ${detail}`);
tryAgain();
},
reset() {
epoch++;
if (timer !== null) clearTimer(timer);
timer = null;
failures = 0;
startedAt = null;
},
};
}
/** The Linux runtime publishes its own connection records; a died daemon and a ready one are what restarts need from them. */
export function followLinuxConnection(restarts, connection) {
if (connection?.status === "ready") restarts.started();
else if (connection?.reasonCode === "daemon-exited") restarts.exited(`exit ${connection.exitCode ?? connection.exitSignal ?? "unknown"}`);
}