mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 03:11:25 +00:00
Cron: normalize cron.add inputs + align channels (#256)
* fix: harden cron add and align channels * fix: keep cron tool id params --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -635,6 +635,8 @@ export const CronPayloadSchema = Type.Union([
|
||||
Type.Literal("telegram"),
|
||||
Type.Literal("discord"),
|
||||
Type.Literal("slack"),
|
||||
Type.Literal("signal"),
|
||||
Type.Literal("imessage"),
|
||||
]),
|
||||
),
|
||||
to: Type.Optional(Type.String()),
|
||||
|
||||
@@ -17,6 +17,10 @@ import {
|
||||
validateWakeParams,
|
||||
} from "../protocol/index.js";
|
||||
import type { GatewayRequestHandlers } from "./types.js";
|
||||
import {
|
||||
normalizeCronJobCreate,
|
||||
normalizeCronJobPatch,
|
||||
} from "../../cron/normalize.js";
|
||||
|
||||
export const cronHandlers: GatewayRequestHandlers = {
|
||||
wake: ({ params, respond, context }) => {
|
||||
@@ -72,7 +76,8 @@ export const cronHandlers: GatewayRequestHandlers = {
|
||||
respond(true, status, undefined);
|
||||
},
|
||||
"cron.add": async ({ params, respond, context }) => {
|
||||
if (!validateCronAddParams(params)) {
|
||||
const normalized = normalizeCronJobCreate(params) ?? params;
|
||||
if (!validateCronAddParams(normalized)) {
|
||||
respond(
|
||||
false,
|
||||
undefined,
|
||||
@@ -83,11 +88,20 @@ export const cronHandlers: GatewayRequestHandlers = {
|
||||
);
|
||||
return;
|
||||
}
|
||||
const job = await context.cron.add(params as unknown as CronJobCreate);
|
||||
const job = await context.cron.add(
|
||||
normalized as unknown as CronJobCreate,
|
||||
);
|
||||
respond(true, job, undefined);
|
||||
},
|
||||
"cron.update": async ({ params, respond, context }) => {
|
||||
if (!validateCronUpdateParams(params)) {
|
||||
const normalizedPatch = normalizeCronJobPatch(
|
||||
(params as { patch?: unknown } | null)?.patch,
|
||||
);
|
||||
const candidate =
|
||||
normalizedPatch && typeof params === "object" && params !== null
|
||||
? { ...(params as Record<string, unknown>), patch: normalizedPatch }
|
||||
: params;
|
||||
if (!validateCronUpdateParams(candidate)) {
|
||||
respond(
|
||||
false,
|
||||
undefined,
|
||||
@@ -98,7 +112,7 @@ export const cronHandlers: GatewayRequestHandlers = {
|
||||
);
|
||||
return;
|
||||
}
|
||||
const p = params as {
|
||||
const p = candidate as {
|
||||
id: string;
|
||||
patch: Record<string, unknown>;
|
||||
};
|
||||
|
||||
@@ -68,6 +68,88 @@ describe("gateway server cron", () => {
|
||||
testState.cronStorePath = undefined;
|
||||
});
|
||||
|
||||
test("normalizes wrapped cron.add payloads", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-cron-"));
|
||||
testState.cronStorePath = path.join(dir, "cron", "jobs.json");
|
||||
await fs.mkdir(path.dirname(testState.cronStorePath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
testState.cronStorePath,
|
||||
JSON.stringify({ version: 1, jobs: [] }),
|
||||
);
|
||||
|
||||
const { server, ws } = await startServerWithClient();
|
||||
await connectOk(ws);
|
||||
|
||||
const atMs = Date.now() + 1000;
|
||||
const addRes = await rpcReq(ws, "cron.add", {
|
||||
data: {
|
||||
name: "wrapped",
|
||||
schedule: { atMs },
|
||||
payload: { text: "hello" },
|
||||
},
|
||||
});
|
||||
expect(addRes.ok).toBe(true);
|
||||
const payload = addRes.payload as
|
||||
| { schedule?: unknown; sessionTarget?: unknown; wakeMode?: unknown }
|
||||
| undefined;
|
||||
expect(payload?.sessionTarget).toBe("main");
|
||||
expect(payload?.wakeMode).toBe("next-heartbeat");
|
||||
expect((payload?.schedule as { kind?: unknown } | undefined)?.kind).toBe(
|
||||
"at",
|
||||
);
|
||||
|
||||
ws.close();
|
||||
await server.close();
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
testState.cronStorePath = undefined;
|
||||
});
|
||||
|
||||
test("normalizes cron.update patch payloads", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-cron-"));
|
||||
testState.cronStorePath = path.join(dir, "cron", "jobs.json");
|
||||
await fs.mkdir(path.dirname(testState.cronStorePath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
testState.cronStorePath,
|
||||
JSON.stringify({ version: 1, jobs: [] }),
|
||||
);
|
||||
|
||||
const { server, ws } = await startServerWithClient();
|
||||
await connectOk(ws);
|
||||
|
||||
const addRes = await rpcReq(ws, "cron.add", {
|
||||
name: "patch test",
|
||||
enabled: true,
|
||||
schedule: { kind: "every", everyMs: 60_000 },
|
||||
sessionTarget: "main",
|
||||
wakeMode: "next-heartbeat",
|
||||
payload: { kind: "systemEvent", text: "hello" },
|
||||
});
|
||||
expect(addRes.ok).toBe(true);
|
||||
const jobIdValue = (addRes.payload as { id?: unknown } | null)?.id;
|
||||
const jobId = typeof jobIdValue === "string" ? jobIdValue : "";
|
||||
expect(jobId.length > 0).toBe(true);
|
||||
|
||||
const atMs = Date.now() + 1_000;
|
||||
const updateRes = await rpcReq(ws, "cron.update", {
|
||||
id: jobId,
|
||||
patch: {
|
||||
schedule: { atMs },
|
||||
payload: { text: "updated" },
|
||||
},
|
||||
});
|
||||
expect(updateRes.ok).toBe(true);
|
||||
const updated = updateRes.payload as
|
||||
| { schedule?: { kind?: unknown }; payload?: { kind?: unknown } }
|
||||
| undefined;
|
||||
expect(updated?.schedule?.kind).toBe("at");
|
||||
expect(updated?.payload?.kind).toBe("systemEvent");
|
||||
|
||||
ws.close();
|
||||
await server.close();
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
testState.cronStorePath = undefined;
|
||||
});
|
||||
|
||||
test("writes cron run history to runs/<jobId>.jsonl", async () => {
|
||||
const dir = await fs.mkdtemp(
|
||||
path.join(os.tmpdir(), "clawdbot-gw-cron-log-"),
|
||||
|
||||
Reference in New Issue
Block a user