mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 06:01:23 +00:00
fix(cron): fix test failures and regenerate protocol files
- Add forceReload option to ensureLoaded to avoid stat I/O in normal paths while still detecting cross-service writes in the timer path - Post isolated job summary back to main session (restores the old isolation.postToMainPrefix behavior via delivery model) - Update legacy migration tests to check delivery.channel instead of payload.channel (normalization now moves delivery fields to top-level) - Remove legacy deliver/channel/to/bestEffortDeliver from payload schema - Update protocol conformance test for delivery modes - Regenerate GatewayModels.swift (isolation -> delivery)
This commit is contained in:
committed by
Peter Steinberger
parent
6fb8d8850e
commit
f8d2534062
@@ -126,23 +126,24 @@ async function getFileMtimeMs(path: string): Promise<number | null> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureLoaded(state: CronServiceState) {
|
||||
const fileMtimeMs = await getFileMtimeMs(state.deps.storePath);
|
||||
|
||||
// Check if we need to reload:
|
||||
// - No store loaded yet
|
||||
// - File modification time has changed
|
||||
// - File was modified after we last loaded (external edit)
|
||||
const needsReload =
|
||||
!state.store ||
|
||||
(fileMtimeMs !== null &&
|
||||
state.storeFileMtimeMs !== null &&
|
||||
fileMtimeMs > state.storeFileMtimeMs);
|
||||
|
||||
if (!needsReload) {
|
||||
export async function ensureLoaded(state: CronServiceState, opts?: { forceReload?: boolean }) {
|
||||
// Fast path: store is already in memory. The timer path passes
|
||||
// forceReload=true so that cross-service writes to the same store file
|
||||
// are always picked up. Other callers (add, list, run, …) trust the
|
||||
// in-memory copy to avoid a stat syscall on every operation.
|
||||
if (state.store && !opts?.forceReload) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (opts?.forceReload && state.store) {
|
||||
// Only pay for the stat when we're explicitly checking for external edits.
|
||||
const mtime = await getFileMtimeMs(state.deps.storePath);
|
||||
if (mtime !== null && state.storeFileMtimeMs !== null && mtime === state.storeFileMtimeMs) {
|
||||
return; // File unchanged since our last load/persist.
|
||||
}
|
||||
}
|
||||
|
||||
const fileMtimeMs = await getFileMtimeMs(state.deps.storePath);
|
||||
const loaded = await loadCronStore(state.deps.storePath);
|
||||
const jobs = (loaded.jobs ?? []) as unknown as Array<Record<string, unknown>>;
|
||||
let mutated = false;
|
||||
|
||||
@@ -37,7 +37,7 @@ export async function onTimer(state: CronServiceState) {
|
||||
state.running = true;
|
||||
try {
|
||||
await locked(state, async () => {
|
||||
await ensureLoaded(state);
|
||||
await ensureLoaded(state, { forceReload: true });
|
||||
await runDueJobs(state);
|
||||
await persist(state);
|
||||
armTimer(state);
|
||||
@@ -184,6 +184,18 @@ export async function executeJob(
|
||||
job,
|
||||
message: job.payload.message,
|
||||
});
|
||||
|
||||
// Post a short summary back to the main session so the user sees
|
||||
// the cron result without opening the isolated session.
|
||||
const summaryText = res.summary?.trim();
|
||||
if (summaryText) {
|
||||
const prefix = "Cron";
|
||||
const label =
|
||||
res.status === "error" ? `${prefix} (error): ${summaryText}` : `${prefix}: ${summaryText}`;
|
||||
state.deps.enqueueSystemEvent(label, { agentId: job.agentId });
|
||||
state.deps.requestHeartbeatNow({ reason: `cron:${job.id}` });
|
||||
}
|
||||
|
||||
if (res.status === "ok") {
|
||||
await finish("ok", undefined, res.summary);
|
||||
} else if (res.status === "skipped") {
|
||||
|
||||
Reference in New Issue
Block a user