openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
37 lines (36 loc) • 3.13 kB
JavaScript
import { a as getNodeSqliteKysely, i as executeSqliteQueryTakeFirstSync } from "./kysely-sync-K7ftY-g4.mjs";
import "./sqlite-transaction-DsZ3X6nD.mjs";
import { t as GATEWAY_STARTUP_MAINTENANCE_REQUIRED_REASON } from "./startup-maintenance-required-rkhEAmAi.mjs";
//#region src/infra/gateway-boot-lifecycle.kernel.ts
const GATEWAY_BOOT_LOOP_UNCLEAN_THRESHOLD = 3;
const GATEWAY_BOOT_LOOP_WINDOW_MS = 3e5;
const GATEWAY_CRASH_LOOP_BREAKER_REASON = "gateway.crash_loop_breaker";
const GATEWAY_CRASH_LOOP_RECOVERED_REASON = "gateway.crash_loop_recovered";
const maintenanceStartupReasons = [GATEWAY_STARTUP_MAINTENANCE_REQUIRED_REASON, "gateway.agent_media_migration_required"];
function buildGatewayCrashLoopBreakerDecision(params) {
const tripped = params.uncleanBoots >= GATEWAY_BOOT_LOOP_UNCLEAN_THRESHOLD;
const hasUnrecoveredBreakerMarker = typeof params.latestBreakerStartedAtMs === "number" && (typeof params.latestRecoveryStartedAtMs !== "number" || params.latestRecoveryStartedAtMs < params.latestBreakerStartedAtMs);
return {
tripped,
recoveryPausedUntilMs: hasUnrecoveredBreakerMarker && params.latestUncleanAtMs != null ? params.latestUncleanAtMs + GATEWAY_BOOT_LOOP_WINDOW_MS + 1 : void 0,
uncleanBoots: params.uncleanBoots,
windowMs: GATEWAY_BOOT_LOOP_WINDOW_MS,
shouldWriteStabilityBundle: tripped && !hasUnrecoveredBreakerMarker,
recovered: !tripped && hasUnrecoveredBreakerMarker
};
}
function inspectGatewayCrashLoopBreakerInDatabase(db, nowMs) {
const kysely = getNodeSqliteKysely(db);
const windowStartMs = nowMs - GATEWAY_BOOT_LOOP_WINDOW_MS;
const uncleanRow = executeSqliteQueryTakeFirstSync(db, kysely.selectFrom("gateway_boot_lifecycle").select((eb) => [eb.fn.countAll().as("count"), eb.fn.max(eb.fn.coalesce("completed_at_ms", "started_at_ms")).as("latestUncleanAtMs")]).where((eb) => eb.or([eb("startup_reason", "is", null), eb("startup_reason", "not in", maintenanceStartupReasons)])).where((eb) => eb.or([eb.and([eb("completed_at_ms", "is", null), eb("started_at_ms", ">=", windowStartMs)]), eb.and([eb("outcome", "=", "startup_failed"), eb("completed_at_ms", ">=", windowStartMs)])])));
const latestBreaker = executeSqliteQueryTakeFirstSync(db, kysely.selectFrom("gateway_boot_lifecycle").select("started_at_ms as startedAtMs").where("startup_reason", "=", GATEWAY_CRASH_LOOP_BREAKER_REASON).orderBy("started_at_ms", "desc").limit(1));
const latestRecovery = executeSqliteQueryTakeFirstSync(db, kysely.selectFrom("gateway_boot_lifecycle").select("started_at_ms as startedAtMs").where("startup_reason", "=", GATEWAY_CRASH_LOOP_RECOVERED_REASON).orderBy("started_at_ms", "desc").limit(1));
return buildGatewayCrashLoopBreakerDecision({
uncleanBoots: uncleanRow?.count ?? 0,
latestUncleanAtMs: uncleanRow?.latestUncleanAtMs,
latestBreakerStartedAtMs: latestBreaker?.startedAtMs,
latestRecoveryStartedAtMs: latestRecovery?.startedAtMs
});
}
//#endregion
export { maintenanceStartupReasons as a, inspectGatewayCrashLoopBreakerInDatabase as i, GATEWAY_CRASH_LOOP_RECOVERED_REASON as n, buildGatewayCrashLoopBreakerDecision as r, GATEWAY_CRASH_LOOP_BREAKER_REASON as t };