mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-31 09:08:35 +00:00
fix(heartbeat): default non-finite schedule inputs
This commit is contained in:
@@ -61,6 +61,37 @@ describe("heartbeat schedule helpers", () => {
|
||||
}),
|
||||
).toBe(nextDueMs);
|
||||
});
|
||||
|
||||
it("falls back to finite schedule values for non-finite numeric inputs", () => {
|
||||
expect(
|
||||
resolveHeartbeatPhaseMs({
|
||||
schedulerSeed: "device-a",
|
||||
agentId: "main",
|
||||
intervalMs: Number.NaN,
|
||||
}),
|
||||
).toBe(0);
|
||||
|
||||
expect(
|
||||
computeNextHeartbeatPhaseDueMs({
|
||||
nowMs: Number.NaN,
|
||||
intervalMs: Number.NaN,
|
||||
phaseMs: Number.NaN,
|
||||
}),
|
||||
).toBe(1);
|
||||
|
||||
expect(
|
||||
resolveNextHeartbeatDueMs({
|
||||
nowMs: 10,
|
||||
intervalMs: Number.NaN,
|
||||
phaseMs: Number.NaN,
|
||||
prev: {
|
||||
intervalMs: 1,
|
||||
phaseMs: 0,
|
||||
nextDueMs: 20,
|
||||
},
|
||||
}),
|
||||
).toBe(20);
|
||||
});
|
||||
});
|
||||
|
||||
describe("seekNextActivePhaseDueMs", () => {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { resolveIntegerOption } from "./numeric-options.js";
|
||||
|
||||
function resolvePositiveIntervalMs(value: number): number {
|
||||
return resolveIntegerOption(value, 1, { min: 1 });
|
||||
}
|
||||
|
||||
function normalizeModulo(value: number, divisor: number) {
|
||||
return ((value % divisor) + divisor) % divisor;
|
||||
@@ -9,7 +14,7 @@ export function resolveHeartbeatPhaseMs(params: {
|
||||
agentId: string;
|
||||
intervalMs: number;
|
||||
}) {
|
||||
const intervalMs = Math.max(1, Math.floor(params.intervalMs));
|
||||
const intervalMs = resolvePositiveIntervalMs(params.intervalMs);
|
||||
const digest = createHash("sha256").update(`${params.schedulerSeed}:${params.agentId}`).digest();
|
||||
return digest.readUInt32BE(0) % intervalMs;
|
||||
}
|
||||
@@ -19,9 +24,12 @@ export function computeNextHeartbeatPhaseDueMs(params: {
|
||||
intervalMs: number;
|
||||
phaseMs: number;
|
||||
}) {
|
||||
const intervalMs = Math.max(1, Math.floor(params.intervalMs));
|
||||
const nowMs = Math.floor(params.nowMs);
|
||||
const phaseMs = normalizeModulo(Math.floor(params.phaseMs), intervalMs);
|
||||
const intervalMs = resolvePositiveIntervalMs(params.intervalMs);
|
||||
const nowMs = Number.isFinite(params.nowMs) ? Math.floor(params.nowMs) : 0;
|
||||
const phaseMs = normalizeModulo(
|
||||
Number.isFinite(params.phaseMs) ? Math.floor(params.phaseMs) : 0,
|
||||
intervalMs,
|
||||
);
|
||||
const cyclePositionMs = normalizeModulo(nowMs, intervalMs);
|
||||
let deltaMs = normalizeModulo(phaseMs - cyclePositionMs, intervalMs);
|
||||
if (deltaMs === 0) {
|
||||
@@ -40,8 +48,11 @@ export function resolveNextHeartbeatDueMs(params: {
|
||||
nextDueMs: number;
|
||||
};
|
||||
}) {
|
||||
const intervalMs = Math.max(1, Math.floor(params.intervalMs));
|
||||
const phaseMs = normalizeModulo(Math.floor(params.phaseMs), intervalMs);
|
||||
const intervalMs = resolvePositiveIntervalMs(params.intervalMs);
|
||||
const phaseMs = normalizeModulo(
|
||||
Number.isFinite(params.phaseMs) ? Math.floor(params.phaseMs) : 0,
|
||||
intervalMs,
|
||||
);
|
||||
const prev = params.prev;
|
||||
if (
|
||||
prev &&
|
||||
@@ -81,7 +92,7 @@ export function seekNextActivePhaseDueMs(params: {
|
||||
if (!isActive) {
|
||||
return params.startMs;
|
||||
}
|
||||
const intervalMs = Math.max(1, Math.floor(params.intervalMs));
|
||||
const intervalMs = resolvePositiveIntervalMs(params.intervalMs);
|
||||
const horizonMs = params.startMs + MAX_SEEK_HORIZON_MS;
|
||||
let candidateMs = params.startMs;
|
||||
let iterations = 0;
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
export function resolveNonNegativeIntegerOption(value: number, fallback: number): number {
|
||||
return Number.isFinite(value) ? Math.max(0, Math.floor(value)) : fallback;
|
||||
}
|
||||
|
||||
export function resolveIntegerOption(
|
||||
value: number,
|
||||
fallback: number,
|
||||
params: { min: number },
|
||||
): number {
|
||||
const candidate = Number.isFinite(value) ? value : fallback;
|
||||
return Math.max(params.min, Math.floor(candidate));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user