Download src/cli/cron-cli/register.cron-edit-options.ts from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 12.1 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/cli/cron-cli/register.cron-edit-options.ts
- Command line
-
hf download hf://SaylorTwift/openclaw/src/cli/cron-cli/register.cron-edit-options.ts
-
curl -L -o register.cron-edit-options.ts https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/cli/cron-cli/register.cron-edit-options.ts
12.1 kB
| import { | |
| normalizeOptionalString, | |
| readNonBlankString, | |
| } from "@openclaw/normalization-core/string-coerce"; | |
| import { isSystemMonitorDeclaration } from "../../cron/system-owned-declaration.js"; | |
| import type { CronJob } from "../../cron/types.js"; | |
| import { isSystemOwnedCronPayloadKind } from "../../cron/types.js"; | |
| import { CronCliError } from "./cron-cli-error.js"; | |
| import { | |
| parseCronCommandArgv, | |
| parseCronCommandEnv, | |
| parseCronIntegerOption, | |
| parseCronNoOutputTimeoutOption, | |
| parseCronStringList, | |
| } from "./shared.js"; | |
| import { parseCronThreadIdOption } from "./thread-id-shared.js"; | |
| import { readCronPayloadScript } from "./trigger-options.js"; | |
| const assignIf = ( | |
| target: Record<string, unknown>, | |
| key: string, | |
| value: unknown, | |
| shouldAssign: boolean, | |
| ) => { | |
| if (shouldAssign) { | |
| target[key] = value; | |
| } | |
| }; | |
| export async function resolveCronEditPayloadDeliveryPatch( | |
| opts: Record<string, unknown>, | |
| loadExistingJob: () => Promise<CronJob>, | |
| webhookUrl: string | undefined, | |
| commandCwd: string | undefined, | |
| ): Promise<Record<string, unknown>> { | |
| const patch: Record<string, unknown> = {}; | |
| const hasSystemEventPatch = typeof opts.systemEvent === "string"; | |
| const scriptPath = readNonBlankString(opts.script); | |
| const commandShell = normalizeOptionalString(opts.command); | |
| const commandArgv = parseCronCommandArgv(opts.commandArgv); | |
| if (commandShell && commandArgv) { | |
| throw new CronCliError( | |
| "Pass command payload either with --command or --command-argv, not both.", | |
| ); | |
| } | |
| // Raw flag presence owns the set/clear mutex even when normalization omits a blank value. | |
| const hasModel = typeof opts.model === "string"; | |
| const model = normalizeOptionalString(opts.model); | |
| if (hasModel && opts.clearModel) { | |
| throw new CronCliError("Use --model or --clear-model, not both"); | |
| } | |
| const hasThinking = typeof opts.thinking === "string"; | |
| const thinking = normalizeOptionalString(opts.thinking); | |
| if (hasThinking && opts.clearThinking) { | |
| throw new CronCliError("Use --thinking or --clear-thinking, not both"); | |
| } | |
| const fallbacks = parseCronStringList(opts.fallbacks); | |
| if (typeof opts.fallbacks === "string" && opts.clearFallbacks) { | |
| throw new CronCliError("Use --fallbacks or --clear-fallbacks, not both"); | |
| } | |
| const toolsAllow = parseCronStringList(opts.tools); | |
| const timeoutSeconds = parseCronIntegerOption( | |
| opts.timeoutSeconds, | |
| "--timeout-seconds", | |
| "non-negative", | |
| ); | |
| const hasTimeoutSeconds = timeoutSeconds !== undefined; | |
| const noOutputTimeoutSeconds = parseCronNoOutputTimeoutOption(opts); | |
| const outputMaxBytes = parseCronIntegerOption(opts.outputMaxBytes, "--output-max-bytes"); | |
| const scriptTimeoutSeconds = parseCronIntegerOption( | |
| opts.scriptTimeoutSeconds, | |
| "--script-timeout-seconds", | |
| ); | |
| const scriptToolBudget = parseCronIntegerOption(opts.scriptToolBudget, "--script-tool-budget"); | |
| const hasWebhookDelivery = Boolean(webhookUrl); | |
| const hasDeliveryModeFlag = | |
| opts.announce || typeof opts.deliver === "boolean" || hasWebhookDelivery; | |
| const threadId = parseCronThreadIdOption(opts.threadId); | |
| const hasDeliveryThreadId = typeof threadId === "number"; | |
| const hasDeliveryTarget = | |
| typeof opts.channel === "string" || | |
| typeof opts.to === "string" || | |
| hasDeliveryThreadId || | |
| Boolean(opts.clearChannel) || | |
| Boolean(opts.clearTo) || | |
| Boolean(opts.clearThreadId); | |
| const hasDeliveryAccount = typeof opts.account === "string" || Boolean(opts.clearAccount); | |
| const hasBestEffort = typeof opts.bestEffortDeliver === "boolean"; | |
| if (hasWebhookDelivery && (hasDeliveryTarget || hasDeliveryAccount)) { | |
| throw new CronCliError("--webhook cannot be combined with chat delivery options."); | |
| } | |
| if (typeof opts.channel === "string" && opts.clearChannel) { | |
| throw new CronCliError("Use --channel or --clear-channel, not both"); | |
| } | |
| if (typeof opts.to === "string" && opts.clearTo) { | |
| throw new CronCliError("Use --to or --clear-to, not both"); | |
| } | |
| if (hasDeliveryThreadId && opts.clearThreadId) { | |
| throw new CronCliError("Use --thread-id or --clear-thread-id, not both"); | |
| } | |
| if (typeof opts.account === "string" && opts.clearAccount) { | |
| throw new CronCliError("Use --account or --clear-account, not both"); | |
| } | |
| // Unlike cwd, command stdin intentionally accepts empty and whitespace strings. | |
| const hasCommandInput = typeof opts.commandInput === "string"; | |
| const hasCommandSpecificPayloadField = | |
| Boolean(commandShell) || | |
| Boolean(commandArgv) || | |
| Boolean(commandCwd) || | |
| hasCommandInput || | |
| opts.commandEnv !== undefined || | |
| noOutputTimeoutSeconds !== undefined || | |
| outputMaxBytes !== undefined; | |
| const hasToolsAllowPatch = | |
| typeof opts.tools === "string" || Array.isArray(opts.tools) || Boolean(opts.clearTools); | |
| const hasAgentTurnSpecificPayloadField = | |
| typeof opts.message === "string" || | |
| Boolean(model) || | |
| Boolean(opts.clearModel) || | |
| typeof opts.fallbacks === "string" || | |
| Boolean(opts.clearFallbacks) || | |
| Boolean(thinking) || | |
| Boolean(opts.clearThinking) || | |
| typeof opts.lightContext === "boolean"; | |
| const hasScriptSpecificPayloadField = | |
| Boolean(scriptPath) || scriptTimeoutSeconds !== undefined || scriptToolBudget !== undefined; | |
| if (hasTimeoutSeconds && hasScriptSpecificPayloadField) { | |
| throw new CronCliError("Use --script-timeout-seconds for script jobs, not --timeout-seconds."); | |
| } | |
| if (hasTimeoutSeconds && hasSystemEventPatch) { | |
| throw new CronCliError("--timeout-seconds is not supported for systemEvent jobs."); | |
| } | |
| let timeoutOnlyPayloadKind: "agentTurn" | "command" | undefined; | |
| if (hasTimeoutSeconds && !hasCommandSpecificPayloadField && !hasAgentTurnSpecificPayloadField) { | |
| const existingJob = await loadExistingJob(); | |
| const existingKind = existingJob.payload.kind; | |
| if (existingKind === "script") { | |
| throw new CronCliError( | |
| "Use --script-timeout-seconds for script jobs, not --timeout-seconds.", | |
| ); | |
| } | |
| if ( | |
| existingKind === "systemEvent" || | |
| isSystemOwnedCronPayloadKind(existingKind) || | |
| isSystemMonitorDeclaration(existingJob.declarationKey) | |
| ) { | |
| throw new CronCliError(`--timeout-seconds is not supported for ${existingKind} jobs.`); | |
| } | |
| timeoutOnlyPayloadKind = existingKind; | |
| } | |
| let toolsOnlyPayloadKind: CronJob["payload"]["kind"] | undefined; | |
| if ( | |
| hasToolsAllowPatch && | |
| !hasSystemEventPatch && | |
| !hasAgentTurnSpecificPayloadField && | |
| !hasCommandSpecificPayloadField && | |
| !hasScriptSpecificPayloadField && | |
| !hasTimeoutSeconds | |
| ) { | |
| // Tool grants are shared by every payload kind; a policy-only edit must | |
| // preserve the stored execution kind instead of creating an agent turn. | |
| const existingJob = await loadExistingJob(); | |
| if (isSystemMonitorDeclaration(existingJob.declarationKey)) { | |
| throw new CronCliError("System-owned cron jobs cannot be edited by cron clients."); | |
| } | |
| toolsOnlyPayloadKind = existingJob.payload.kind; | |
| } | |
| const hasAgentTurnPayloadField = | |
| hasAgentTurnSpecificPayloadField || | |
| timeoutOnlyPayloadKind === "agentTurn" || | |
| (hasToolsAllowPatch && toolsOnlyPayloadKind === "agentTurn"); | |
| const hasCommandPayloadField = | |
| hasCommandSpecificPayloadField || | |
| timeoutOnlyPayloadKind === "command" || | |
| toolsOnlyPayloadKind === "command"; | |
| const hasAgentTurnPatch = hasAgentTurnPayloadField; | |
| const hasCommandPatch = hasCommandPayloadField; | |
| const hasScriptPatch = hasScriptSpecificPayloadField || toolsOnlyPayloadKind === "script"; | |
| const hasSystemEventOrToolsPatch = hasSystemEventPatch || toolsOnlyPayloadKind === "systemEvent"; | |
| if ( | |
| [hasSystemEventOrToolsPatch, hasAgentTurnPatch, hasCommandPatch, hasScriptPatch].filter(Boolean) | |
| .length > 1 | |
| ) { | |
| throw new CronCliError("Choose at most one payload change"); | |
| } | |
| const assignToolsAllowPatch = (payload: Record<string, unknown>): void => { | |
| if (opts.clearTools) { | |
| // Clearing a restriction means an explicit unrestricted grant. Persisting | |
| // a wildcard avoids creating a new capless legacy job at the upgrade boundary. | |
| payload.toolsAllow = ["*"]; | |
| } else if (toolsAllow) { | |
| payload.toolsAllow = toolsAllow; | |
| } | |
| }; | |
| if (hasSystemEventOrToolsPatch) { | |
| const payload: Record<string, unknown> = { kind: "systemEvent" }; | |
| assignIf(payload, "text", String(opts.systemEvent), hasSystemEventPatch); | |
| assignToolsAllowPatch(payload); | |
| patch.payload = payload; | |
| } else if (hasAgentTurnPatch) { | |
| const payload: Record<string, unknown> = { kind: "agentTurn" }; | |
| assignIf(payload, "message", String(opts.message), typeof opts.message === "string"); | |
| if (opts.clearModel) { | |
| payload.model = null; | |
| } else { | |
| assignIf(payload, "model", model, Boolean(model)); | |
| } | |
| assignIf(payload, "fallbacks", fallbacks, typeof opts.fallbacks === "string"); | |
| assignIf(payload, "fallbacks", null, Boolean(opts.clearFallbacks)); | |
| if (opts.clearThinking) { | |
| payload.thinking = null; | |
| } else { | |
| assignIf(payload, "thinking", thinking, Boolean(thinking)); | |
| } | |
| assignIf(payload, "timeoutSeconds", timeoutSeconds, hasTimeoutSeconds); | |
| assignIf(payload, "lightContext", opts.lightContext, typeof opts.lightContext === "boolean"); | |
| assignToolsAllowPatch(payload); | |
| patch.payload = payload; | |
| } else if (hasCommandPatch) { | |
| const payload: Record<string, unknown> = { kind: "command" }; | |
| assignIf(payload, "argv", commandArgv, Boolean(commandArgv)); | |
| assignIf(payload, "argv", ["sh", "-lc", commandShell], Boolean(commandShell)); | |
| assignIf(payload, "cwd", commandCwd, Boolean(commandCwd)); | |
| assignIf(payload, "env", parseCronCommandEnv(opts.commandEnv), opts.commandEnv !== undefined); | |
| assignIf(payload, "input", opts.commandInput, hasCommandInput); | |
| assignIf(payload, "timeoutSeconds", timeoutSeconds, hasTimeoutSeconds); | |
| assignIf( | |
| payload, | |
| "noOutputTimeoutSeconds", | |
| noOutputTimeoutSeconds, | |
| noOutputTimeoutSeconds !== undefined, | |
| ); | |
| assignIf(payload, "outputMaxBytes", outputMaxBytes, outputMaxBytes !== undefined); | |
| assignToolsAllowPatch(payload); | |
| patch.payload = payload; | |
| } else if (hasScriptPatch) { | |
| const payload: Record<string, unknown> = { kind: "script" }; | |
| if (scriptPath) { | |
| payload.script = await readCronPayloadScript(scriptPath); | |
| } | |
| assignIf(payload, "timeoutSeconds", scriptTimeoutSeconds, scriptTimeoutSeconds !== undefined); | |
| assignIf(payload, "toolBudget", scriptToolBudget, scriptToolBudget !== undefined); | |
| assignToolsAllowPatch(payload); | |
| patch.payload = payload; | |
| } | |
| if (hasDeliveryModeFlag || hasDeliveryTarget || hasDeliveryAccount || hasBestEffort) { | |
| const delivery: Record<string, unknown> = {}; | |
| if (hasDeliveryModeFlag) { | |
| delivery.mode = hasWebhookDelivery | |
| ? "webhook" | |
| : opts.announce || opts.deliver === true | |
| ? "announce" | |
| : "none"; | |
| } else if (opts.bestEffortDeliver === true) { | |
| // Back-compat: enabling best-effort historically implied announce mode. | |
| delivery.mode = "announce"; | |
| } | |
| if (opts.clearChannel) { | |
| delivery.channel = null; | |
| } else if (typeof opts.channel === "string") { | |
| const channel = opts.channel.trim(); | |
| delivery.channel = channel ? channel : undefined; | |
| } | |
| if (hasWebhookDelivery) { | |
| delivery.to = webhookUrl; | |
| } else if (opts.clearTo) { | |
| delivery.to = null; | |
| } else if (typeof opts.to === "string") { | |
| const to = opts.to.trim(); | |
| delivery.to = to ? to : undefined; | |
| } | |
| if (opts.clearThreadId) { | |
| delivery.threadId = null; | |
| } else if (hasDeliveryThreadId) { | |
| delivery.threadId = threadId; | |
| } | |
| if (opts.clearAccount) { | |
| delivery.accountId = null; | |
| } else if (typeof opts.account === "string") { | |
| const account = opts.account.trim(); | |
| delivery.accountId = account ? account : undefined; | |
| } | |
| if (typeof opts.bestEffortDeliver === "boolean") { | |
| delivery.bestEffort = opts.bestEffortDeliver; | |
| } | |
| patch.delivery = delivery; | |
| } | |
| return patch; | |
| } | |