openclaw / src /cli /cron-cli /register.cron-scratch.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
eb3f11e verified
Raw History Blame Contribute Delete
3.42 kB
import { parseStrictNonNegativeInteger } from "@openclaw/normalization-core/number-coercion";
// Cron scratch CLI: private per-job prompt context reads and compare-and-swap writes.
import type { Command } from "commander";
import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js";
import { CronCliError } from "./cron-cli-error.js";
import { createCronOutputCommand } from "./output-mode.js";
import { handleCronCliError, printCronJson, requireCronJobId } from "./shared.js";
import { readCronScratchContent } from "./trigger-options.js";
type ScratchRecord = { content: string; revision: number; updatedAtMs: number };
type ScratchGetResult = {
scratch: ScratchRecord | null;
currentRevision: number;
maxBytes: number;
};
type ScratchSetResult =
| { ok: true; scratch: ScratchRecord | null; currentRevision: number; maxBytes: number }
| { ok: false; reason: "revision-conflict"; currentRevision: number };
function parseExpectedRevision(value: string | undefined): number | undefined {
if (value === undefined) {
return undefined;
}
const revision = parseStrictNonNegativeInteger(value);
if (revision === undefined) {
throw new CronCliError("--expected-revision must be a non-negative integer");
}
return revision;
}
export function registerCronScratchCommand(cron: Command) {
addGatewayClientOptions(
createCronOutputCommand(cron, "scratch")
.description("Read or replace an automation's private scratch")
.argument("<id>", "Job id")
.option("--set <text>", "Replace scratch with exact text")
.option("--file <path>", "Replace scratch from a file, or - for stdin")
.option("--unset", "Remove the scratch row", false)
.option("--expected-revision <n>", "Require the current scratch revision")
.action(async (idArg, opts) => {
try {
const id = requireCronJobId(idArg);
const mutations = [
opts.set !== undefined,
opts.file !== undefined,
opts.unset === true,
].filter(Boolean).length;
if (mutations > 1) {
throw new CronCliError("choose only one of --set, --file, or --unset");
}
const current = (await callGatewayFromCli("cron.scratch.get", opts, {
id,
})) as ScratchGetResult;
if (mutations === 0) {
if (opts.json) {
printCronJson(current);
} else if (current.scratch) {
process.stdout.write(current.scratch.content);
}
return;
}
const explicitRevision = parseExpectedRevision(opts.expectedRevision);
const expectedRevision = explicitRevision ?? current.currentRevision;
const content = opts.unset
? null
: opts.file !== undefined
? await readCronScratchContent(String(opts.file))
: String(opts.set ?? "");
const result = (await callGatewayFromCli("cron.scratch.set", opts, {
id,
content,
expectedRevision,
})) as ScratchSetResult;
if (!result.ok) {
throw new CronCliError(
`cron scratch changed concurrently (current revision ${result.currentRevision})`,
);
}
printCronJson(result);
} catch (error) {
handleCronCliError(error);
}
}),
);
}