mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
fix: tolerate malformed cron schedule reloads
This commit is contained in:
@@ -21,6 +21,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Cron: make scheduler reload schedule comparison tolerate malformed persisted jobs, so one bad cron entry no longer aborts the whole tick. Fixes #75886. Thanks @samfox-ai.
|
||||
- Control UI/chat: keep live replies visible when a raw session alias such as `main` sends the chat turn but Gateway emits events under the canonical session key for the same run. Fixes #73716. Thanks @teebes.
|
||||
- CLI/models: reject `--agent` on `openclaw models set` and `set-image` instead of silently writing agent-scoped requests to global model defaults. Fixes #68391. Thanks @derrickabellard.
|
||||
- CLI: stop treating the legacy singular `openclaw tool ...` token as a plugin id under restrictive `plugins.allow`, so it falls through as a normal unknown/reserved command instead of suggesting a stale allowlist entry. Fixes #64732. Thanks @efe-arv, @SweetSophia, and @hashtag1974.
|
||||
|
||||
@@ -61,18 +61,6 @@ function resolveSchedulePayload(
|
||||
return schedulePayloadFromRecord(job);
|
||||
}
|
||||
|
||||
function cronScheduleIdentity(job: Pick<CronJob, "schedule"> & { enabled?: boolean }): string {
|
||||
const schedule = resolveSchedulePayload(job as unknown as Record<string, unknown>);
|
||||
if (!schedule) {
|
||||
throw new Error("Unsupported cron schedule kind");
|
||||
}
|
||||
return JSON.stringify({
|
||||
version: 1,
|
||||
enabled: job.enabled ?? true,
|
||||
schedule,
|
||||
});
|
||||
}
|
||||
|
||||
export function tryCronScheduleIdentity(
|
||||
job: { schedule?: unknown; enabled?: unknown } & Record<string, unknown>,
|
||||
): string | undefined {
|
||||
@@ -88,8 +76,14 @@ export function tryCronScheduleIdentity(
|
||||
}
|
||||
|
||||
export function cronSchedulingInputsEqual(
|
||||
previous: Pick<CronJob, "schedule"> & { enabled?: boolean },
|
||||
next: Pick<CronJob, "schedule"> & { enabled?: boolean },
|
||||
previous: Pick<CronJob, "schedule"> & { enabled?: unknown },
|
||||
next: Pick<CronJob, "schedule"> & { enabled?: unknown },
|
||||
): boolean {
|
||||
return cronScheduleIdentity(previous) === cronScheduleIdentity(next);
|
||||
const previousIdentity = tryCronScheduleIdentity(previous as Record<string, unknown>);
|
||||
const nextIdentity = tryCronScheduleIdentity(next as Record<string, unknown>);
|
||||
return (
|
||||
previousIdentity !== undefined &&
|
||||
nextIdentity !== undefined &&
|
||||
previousIdentity === nextIdentity
|
||||
);
|
||||
}
|
||||
|
||||
@@ -279,6 +279,36 @@ describe("cron service store seam coverage", () => {
|
||||
expect(findJobOrThrow(state, "reload-cron-expr-job").state.nextRunAtMs).toBe(dueNextRunAtMs);
|
||||
});
|
||||
|
||||
it("clears stale nextRunAtMs without throwing when a force-reloaded schedule is malformed", async () => {
|
||||
const { storePath } = await makeStorePath();
|
||||
const staleNextRunAtMs = STORE_TEST_NOW + 3_600_000;
|
||||
|
||||
await writeSingleJobStore(storePath, {
|
||||
...createReloadCronJob({
|
||||
state: { nextRunAtMs: staleNextRunAtMs },
|
||||
}),
|
||||
});
|
||||
|
||||
const state = createStoreTestState(storePath);
|
||||
await ensureLoaded(state, { skipRecompute: true });
|
||||
|
||||
await writeSingleJobStore(storePath, {
|
||||
...createReloadCronJob({
|
||||
updatedAtMs: STORE_TEST_NOW,
|
||||
state: { nextRunAtMs: staleNextRunAtMs },
|
||||
}),
|
||||
schedule: "0 17 * * *",
|
||||
});
|
||||
|
||||
await expect(ensureLoaded(state, { forceReload: true, skipRecompute: true })).resolves.toBe(
|
||||
undefined,
|
||||
);
|
||||
|
||||
const reloadedJob = findJobOrThrow(state, "reload-cron-expr-job");
|
||||
expect(reloadedJob.schedule).toBe("0 17 * * *");
|
||||
expect(reloadedJob.state.nextRunAtMs).toBeUndefined();
|
||||
});
|
||||
|
||||
it("preserves nextRunAtMs after force reload when scheduling inputs are unchanged", async () => {
|
||||
const { storePath } = await makeStorePath();
|
||||
const originalNextRunAtMs = STORE_TEST_NOW + 3_600_000;
|
||||
|
||||
Reference in New Issue
Block a user