UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

116 lines (115 loc) 4.62 kB
import { r as listContextEngineQuarantines } from "./registry-Dca-zXEr.js"; import { o as getGatewayModelPricingHealth } from "./model-pricing-cache-state-0MA2CEaX.js"; import { in as errorShape, rn as ErrorCodes } from "./schema-BwaBORnA.js"; import "./src-oj0IwW6K.js"; import "./server-constants-BGwLM6XN.js"; import { t as formatForLog } from "./ws-log-DlzuStZb.js"; import { t as getStatusSummary } from "./status.summary-DPtaL3z7.js"; import "./status-Bz4G5wLX.js"; import { t as formatError } from "./server-utils-Co-s4JxP.js"; //#region src/gateway/server-methods/health.ts const ADMIN_SCOPE = "operator.admin"; function cachedAccountForRuntimeSnapshot(params) { const accountId = params.accountId; if (accountId && params.cachedChannel?.accounts?.[accountId]) return params.cachedChannel.accounts[accountId]; } function cachedLifecycleDiffersFromRuntime(params) { for (const key of ["running", "connected"]) { const runtimeValue = params.runtimeSnapshot[key]; if (typeof runtimeValue !== "boolean") continue; if (params.cachedAccount?.[key] !== runtimeValue) return true; } return false; } /** Checks whether cached channel health is stale against the live runtime snapshot. */ function cachedHealthDiffersFromRuntime(cached, runtime) { for (const [channelId, runtimeSnapshot] of Object.entries(runtime.channels)) { if (!runtimeSnapshot) continue; const cachedChannel = cached.channels[channelId]; if (cachedLifecycleDiffersFromRuntime({ cachedAccount: cachedChannel, runtimeSnapshot })) return true; } for (const [channelId, accounts] of Object.entries(runtime.channelAccounts)) { if (!accounts) continue; const cachedChannel = cached.channels[channelId]; for (const [accountId, runtimeSnapshot] of Object.entries(accounts)) { if (!runtimeSnapshot) continue; if (cachedLifecycleDiffersFromRuntime({ cachedAccount: cachedAccountForRuntimeSnapshot({ cachedChannel, accountId }), runtimeSnapshot })) return true; } } return false; } /** Merges cheap live runtime facts into a cached health summary before responding. */ function mergeCachedHealthRuntimeState(params) { const { contextEngines: _cachedContextEngines, ...cached } = params.cached; const quarantinedContextEngines = []; for (const entry of listContextEngineQuarantines()) { const summary = { engineId: entry.engineId, operation: entry.operation, reason: entry.reason, failedAt: entry.failedAt.getTime() }; if (entry.owner) summary.owner = entry.owner; quarantinedContextEngines.push(summary); } return { ...cached, ...params.eventLoop ? { eventLoop: params.eventLoop } : {}, ...quarantinedContextEngines.length > 0 ? { contextEngines: { quarantined: quarantinedContextEngines } } : {}, modelPricing: getGatewayModelPricingHealth({ enabled: params.cached.modelPricing?.state !== "disabled" }) }; } /** Gateway handlers for health snapshots and status summaries. */ const healthHandlers = { health: async ({ respond, context, params, client }) => { const { getHealthCache, refreshHealthSnapshot, logHealth } = context; const wantsProbe = params?.probe === true; const includeSensitive = (Array.isArray(client?.connect?.scopes) ? client.connect.scopes : []).includes(ADMIN_SCOPE); const now = Date.now(); const cached = getHealthCache(); let cachedDiffersFromRuntime = false; if (!wantsProbe && cached) try { cachedDiffersFromRuntime = cachedHealthDiffersFromRuntime(cached, context.getRuntimeSnapshot()); } catch { cachedDiffersFromRuntime = false; } if (!wantsProbe && cached && !cachedDiffersFromRuntime && now - cached.ts < 6e4) { respond(true, mergeCachedHealthRuntimeState({ cached, eventLoop: context.getEventLoopHealth?.() }), void 0, { cached: true }); refreshHealthSnapshot({ probe: false, includeSensitive }).catch((err) => logHealth.error(`background health refresh failed: ${formatError(err)}`)); return; } try { respond(true, await refreshHealthSnapshot({ probe: wantsProbe, includeSensitive }), void 0); } catch (err) { respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, formatForLog(err))); } }, status: async ({ respond, client, params, context }) => { const status = await getStatusSummary({ includeSensitive: (Array.isArray(client?.connect?.scopes) ? client.connect.scopes : []).includes(ADMIN_SCOPE), includeChannelSummary: params.includeChannelSummary !== false }); if (context.getEventLoopHealth) status.eventLoop = context.getEventLoopHealth(); respond(true, status, void 0); } }; //#endregion export { healthHandlers };