mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-26 16:06:16 +00:00
69 lines
2.0 KiB
TypeScript
69 lines
2.0 KiB
TypeScript
import { resolveGlobalSingleton } from "../shared/global-singleton.js";
|
|
import { notifyListeners, registerListener } from "../shared/listeners.js";
|
|
|
|
export type HeartbeatIndicatorType = "ok" | "alert" | "error";
|
|
|
|
export type HeartbeatEventPayload = {
|
|
ts: number;
|
|
status: "sent" | "ok-empty" | "ok-token" | "skipped" | "failed";
|
|
to?: string;
|
|
accountId?: string;
|
|
preview?: string;
|
|
durationMs?: number;
|
|
hasMedia?: boolean;
|
|
reason?: string;
|
|
/** The channel this heartbeat was sent to. */
|
|
channel?: string;
|
|
/** Whether the message was silently suppressed (showOk: false). */
|
|
silent?: boolean;
|
|
/** Indicator type for UI status display. */
|
|
indicatorType?: HeartbeatIndicatorType;
|
|
};
|
|
|
|
export function resolveIndicatorType(
|
|
status: HeartbeatEventPayload["status"],
|
|
): HeartbeatIndicatorType | undefined {
|
|
switch (status) {
|
|
case "ok-empty":
|
|
case "ok-token":
|
|
return "ok";
|
|
case "sent":
|
|
return "alert";
|
|
case "failed":
|
|
return "error";
|
|
case "skipped":
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
type HeartbeatEventState = {
|
|
lastHeartbeat: HeartbeatEventPayload | null;
|
|
listeners: Set<(evt: HeartbeatEventPayload) => void>;
|
|
};
|
|
|
|
const HEARTBEAT_EVENT_STATE_KEY = Symbol.for("openclaw.heartbeatEvents.state");
|
|
|
|
const state = resolveGlobalSingleton<HeartbeatEventState>(HEARTBEAT_EVENT_STATE_KEY, () => ({
|
|
lastHeartbeat: null,
|
|
listeners: new Set<(evt: HeartbeatEventPayload) => void>(),
|
|
}));
|
|
|
|
export function emitHeartbeatEvent(evt: Omit<HeartbeatEventPayload, "ts">) {
|
|
const enriched: HeartbeatEventPayload = { ts: Date.now(), ...evt };
|
|
state.lastHeartbeat = enriched;
|
|
notifyListeners(state.listeners, enriched);
|
|
}
|
|
|
|
export function onHeartbeatEvent(listener: (evt: HeartbeatEventPayload) => void): () => void {
|
|
return registerListener(state.listeners, listener);
|
|
}
|
|
|
|
export function getLastHeartbeatEvent(): HeartbeatEventPayload | null {
|
|
return state.lastHeartbeat;
|
|
}
|
|
|
|
export function resetHeartbeatEventsForTest(): void {
|
|
state.lastHeartbeat = null;
|
|
state.listeners.clear();
|
|
}
|