mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-18 04:31:10 +00:00
234 lines
6.7 KiB
TypeScript
234 lines
6.7 KiB
TypeScript
import type { CronFailureDestinationConfig } from "../config/types.cron.js";
|
|
import type { CronDelivery, CronDeliveryMode, CronJob, CronMessageChannel } from "./types.js";
|
|
|
|
export type CronDeliveryPlan = {
|
|
mode: CronDeliveryMode;
|
|
channel?: CronMessageChannel;
|
|
to?: string;
|
|
threadId?: string | number;
|
|
/** Explicit channel account id from the delivery config, if set. */
|
|
accountId?: string;
|
|
source: "delivery";
|
|
requested: boolean;
|
|
};
|
|
|
|
function normalizeChannel(value: unknown): CronMessageChannel | undefined {
|
|
if (typeof value !== "string") {
|
|
return undefined;
|
|
}
|
|
const trimmed = value.trim().toLowerCase();
|
|
if (!trimmed) {
|
|
return undefined;
|
|
}
|
|
return trimmed as CronMessageChannel;
|
|
}
|
|
|
|
function normalizeTo(value: unknown): string | undefined {
|
|
if (typeof value !== "string") {
|
|
return undefined;
|
|
}
|
|
const trimmed = value.trim();
|
|
return trimmed ? trimmed : undefined;
|
|
}
|
|
|
|
function normalizeAccountId(value: unknown): string | undefined {
|
|
if (typeof value !== "string") {
|
|
return undefined;
|
|
}
|
|
const trimmed = value.trim();
|
|
return trimmed ? trimmed : undefined;
|
|
}
|
|
|
|
function normalizeThreadId(value: unknown): string | number | undefined {
|
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
return value;
|
|
}
|
|
if (typeof value !== "string") {
|
|
return undefined;
|
|
}
|
|
const trimmed = value.trim();
|
|
return trimmed ? trimmed : undefined;
|
|
}
|
|
|
|
export function resolveCronDeliveryPlan(job: CronJob): CronDeliveryPlan {
|
|
const delivery = job.delivery;
|
|
const hasDelivery = delivery && typeof delivery === "object";
|
|
const rawMode = hasDelivery ? (delivery as { mode?: unknown }).mode : undefined;
|
|
const normalizedMode = typeof rawMode === "string" ? rawMode.trim().toLowerCase() : rawMode;
|
|
const mode =
|
|
normalizedMode === "announce"
|
|
? "announce"
|
|
: normalizedMode === "webhook"
|
|
? "webhook"
|
|
: normalizedMode === "none"
|
|
? "none"
|
|
: normalizedMode === "deliver"
|
|
? "announce"
|
|
: undefined;
|
|
|
|
const deliveryChannel = normalizeChannel(
|
|
(delivery as { channel?: unknown } | undefined)?.channel,
|
|
);
|
|
const deliveryTo = normalizeTo((delivery as { to?: unknown } | undefined)?.to);
|
|
const deliveryThreadId = normalizeThreadId(
|
|
(delivery as { threadId?: unknown } | undefined)?.threadId,
|
|
);
|
|
const channel = deliveryChannel ?? "last";
|
|
const to = deliveryTo;
|
|
const deliveryAccountId = normalizeAccountId(
|
|
(delivery as { accountId?: unknown } | undefined)?.accountId,
|
|
);
|
|
if (hasDelivery) {
|
|
const resolvedMode = mode ?? "announce";
|
|
return {
|
|
mode: resolvedMode,
|
|
channel: resolvedMode === "announce" ? channel : undefined,
|
|
to,
|
|
threadId: resolvedMode === "announce" ? deliveryThreadId : undefined,
|
|
accountId: deliveryAccountId,
|
|
source: "delivery",
|
|
requested: resolvedMode === "announce",
|
|
};
|
|
}
|
|
|
|
const isIsolatedAgentTurn =
|
|
job.payload.kind === "agentTurn" &&
|
|
(job.sessionTarget === "isolated" ||
|
|
job.sessionTarget === "current" ||
|
|
job.sessionTarget.startsWith("session:"));
|
|
const resolvedMode = isIsolatedAgentTurn ? "announce" : "none";
|
|
|
|
return {
|
|
mode: resolvedMode,
|
|
channel: resolvedMode === "announce" ? "last" : undefined,
|
|
to: undefined,
|
|
threadId: undefined,
|
|
source: "delivery",
|
|
requested: resolvedMode === "announce",
|
|
};
|
|
}
|
|
|
|
export type CronFailureDeliveryPlan = {
|
|
mode: "announce" | "webhook";
|
|
channel?: CronMessageChannel;
|
|
to?: string;
|
|
accountId?: string;
|
|
};
|
|
|
|
export type CronFailureDestinationInput = {
|
|
channel?: CronMessageChannel;
|
|
to?: string;
|
|
accountId?: string;
|
|
mode?: "announce" | "webhook";
|
|
};
|
|
|
|
function normalizeFailureMode(value: unknown): "announce" | "webhook" | undefined {
|
|
if (typeof value !== "string") {
|
|
return undefined;
|
|
}
|
|
const trimmed = value.trim().toLowerCase();
|
|
if (trimmed === "announce" || trimmed === "webhook") {
|
|
return trimmed;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function resolveFailureDestination(
|
|
job: CronJob,
|
|
globalConfig?: CronFailureDestinationConfig,
|
|
): CronFailureDeliveryPlan | null {
|
|
const delivery = job.delivery;
|
|
const jobFailureDest = delivery?.failureDestination as CronFailureDestinationInput | undefined;
|
|
const hasJobFailureDest = jobFailureDest && typeof jobFailureDest === "object";
|
|
|
|
let channel: CronMessageChannel | undefined;
|
|
let to: string | undefined;
|
|
let accountId: string | undefined;
|
|
let mode: "announce" | "webhook" | undefined;
|
|
|
|
if (globalConfig) {
|
|
channel = normalizeChannel(globalConfig.channel);
|
|
to = normalizeTo(globalConfig.to);
|
|
accountId = normalizeAccountId(globalConfig.accountId);
|
|
mode = normalizeFailureMode(globalConfig.mode);
|
|
}
|
|
|
|
if (hasJobFailureDest) {
|
|
const jobChannel = normalizeChannel(jobFailureDest.channel);
|
|
const jobTo = normalizeTo(jobFailureDest.to);
|
|
const jobAccountId = normalizeAccountId(jobFailureDest.accountId);
|
|
const jobMode = normalizeFailureMode(jobFailureDest.mode);
|
|
const hasJobChannelField = "channel" in jobFailureDest;
|
|
const hasJobToField = "to" in jobFailureDest;
|
|
const hasJobAccountIdField = "accountId" in jobFailureDest;
|
|
|
|
const jobToExplicitValue = hasJobToField && jobTo !== undefined;
|
|
|
|
if (hasJobChannelField) {
|
|
channel = jobChannel;
|
|
}
|
|
if (hasJobToField) {
|
|
to = jobTo;
|
|
}
|
|
if (hasJobAccountIdField) {
|
|
accountId = jobAccountId;
|
|
}
|
|
if (jobMode !== undefined) {
|
|
const globalMode = globalConfig?.mode ?? "announce";
|
|
if (!jobToExplicitValue && globalMode !== jobMode) {
|
|
to = undefined;
|
|
}
|
|
mode = jobMode;
|
|
}
|
|
}
|
|
|
|
if (!channel && !to && !accountId && !mode) {
|
|
return null;
|
|
}
|
|
|
|
const resolvedMode = mode ?? "announce";
|
|
if (resolvedMode === "webhook" && !to) {
|
|
return null;
|
|
}
|
|
|
|
const result: CronFailureDeliveryPlan = {
|
|
mode: resolvedMode,
|
|
channel: resolvedMode === "announce" ? (channel ?? "last") : undefined,
|
|
to,
|
|
accountId,
|
|
};
|
|
|
|
if (delivery && isSameDeliveryTarget(delivery, result)) {
|
|
return null;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function isSameDeliveryTarget(
|
|
delivery: CronDelivery,
|
|
failurePlan: CronFailureDeliveryPlan,
|
|
): boolean {
|
|
const primaryMode = delivery.mode ?? "announce";
|
|
if (primaryMode === "none") {
|
|
return false;
|
|
}
|
|
|
|
const primaryChannel = delivery.channel;
|
|
const primaryTo = delivery.to;
|
|
const primaryAccountId = delivery.accountId;
|
|
|
|
if (failurePlan.mode === "webhook") {
|
|
return primaryMode === "webhook" && primaryTo === failurePlan.to;
|
|
}
|
|
|
|
const primaryChannelNormalized = primaryChannel ?? "last";
|
|
const failureChannelNormalized = failurePlan.channel ?? "last";
|
|
|
|
return (
|
|
failureChannelNormalized === primaryChannelNormalized &&
|
|
failurePlan.to === primaryTo &&
|
|
failurePlan.accountId === primaryAccountId
|
|
);
|
|
}
|