UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

193 lines (192 loc) 9.28 kB
import { t as formatCliCommand } from "./command-format-C7YfyMTd.js"; import { f as resolveConfigPath, w as resolveStateDir } from "./paths-D2sRr1a_.js"; import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js"; import { n as sanitizeTerminalText } from "./safe-text-BGBqp1a4.js"; import { t as VERSION } from "./version-v1kuAkGj.js"; import { n as isGatewaySecretRefUnavailableError } from "./credentials-CXbS4PS0.js"; import { o as isLoopbackGatewayUrl } from "./net-DbNPs6Xm.js"; import { a as buildGatewayProbeConnectionDetails, i as buildGatewayConnectionDetails, o as callGateway, p as isGatewayCredentialsRequiredError } from "./call-BWR5pPgw.js"; import { t as note } from "./note-DqHk3fA1.js"; import { t as probeGatewayStatus } from "./probe-FCgI-faz.js"; import { i as inspectInstalledGatewayStatePaths, r as compareCliGatewayStateDirs, t as GATEWAY_SERVICE_PATHS_UNVERIFIED } from "./state-dir-gateway-check-RkqSxLMK.js"; import { t as collectChannelStatusIssues } from "./channels-status-issues-pPpAHyro.js"; import { i as formatHealthCheckFailure, n as formatGatewayClosedDiagnostic } from "./health-format-BptmWUeD.js"; import { c as gatewayConnectErrorWasRateLimited, i as GATEWAY_HEALTH_RATE_LIMITED_TITLE, l as gatewayProbeResultSawGateway, n as GATEWAY_HEALTH_CREDENTIALS_REQUIRED_TITLE, r as GATEWAY_HEALTH_RATE_LIMITED_MESSAGE, t as GATEWAY_HEALTH_CREDENTIALS_REQUIRED_MESSAGE, u as gatewayProbeResultWasRateLimited } from "./gateway-health-auth-diagnostic-6ZBqs-mo.js"; import { t as projectDoctorSecretRuntimeDegradations } from "./doctor-secret-runtime-degradation-auwIRP9K.js"; import { t as formatTelemetryExporterSummary } from "./telemetry-exporter-summary-BzbrNr8-.js"; //#region src/commands/doctor-gateway-health.ts /** Gateway health probes used by doctor before deeper daemon and memory diagnostics. */ function isGatewayCallTimeout(message) { return /^gateway timeout after \d+ms(?:\n|$)/.test(message); } function isGatewayHealthAuthUnavailableError(error) { return isGatewayCredentialsRequiredError(error) || isGatewaySecretRefUnavailableError(error); } function noteCliGatewayVersionSkew(status) { const gatewayVersion = status?.runtimeVersion?.trim(); if (!gatewayVersion || gatewayVersion === VERSION) return; note([ `This command is OpenClaw ${VERSION}; the running Gateway is OpenClaw ${gatewayVersion}.`, "Check `openclaw --version`, `which openclaw`, and `openclaw gateway status --deep`.", "If this mismatch is unexpected, update PATH so `openclaw` points to the version you want, or reinstall the Gateway service from that same OpenClaw install." ].join("\n"), "OpenClaw version mismatch"); } function noteGatewayStateDirectory(snapshot, source) { if (!snapshot.stateDir) return; const comparison = compareCliGatewayStateDirs({ cliStateDir: resolveStateDir(process.env), cliConfigPath: resolveConfigPath(process.env), gatewayStateDir: snapshot.stateDir, gatewayConfigPath: snapshot.configPath, source, mode: "warn" }); if (comparison.kind === "warn") note(`${comparison.message}\nRun plugin inspection and doctor --fix with the Gateway's OPENCLAW_STATE_DIR and OPENCLAW_CONFIG_PATH. To change the managed service, run \`openclaw gateway install --force\` from the intended profile and review operator-owned service overrides.`, "Gateway state directory mismatch"); } async function noteInstalledGatewayStateDirectory(cfg, timeoutMs) { if (cfg.gateway?.mode === "remote") return; try { if (!isLoopbackGatewayUrl(buildGatewayConnectionDetails({ config: cfg }).url)) return; const paths = await inspectInstalledGatewayStatePaths(Math.min(timeoutMs, 3e3)); if (paths.kind === "known") noteGatewayStateDirectory(paths, "installed Gateway service"); else if (paths.kind === "unknown") note(GATEWAY_SERVICE_PATHS_UNVERIFIED, "Gateway state directory"); } catch { note(GATEWAY_SERVICE_PATHS_UNVERIFIED, "Gateway state directory"); } } /** * Probes gateway status and reports user-facing connection/auth/channel warnings. * * A credentials-required gateway still counts as healthy but unauthenticated when the preauth * probe confirms the server is reachable. */ async function checkGatewayHealth(params) { const timeoutMs = typeof params.timeoutMs === "number" && params.timeoutMs > 0 ? params.timeoutMs : 1e4; let healthOk = false; let status; let gatewaySnapshot; try { status = await callGateway({ method: "status", params: { includeChannelSummary: false }, timeoutMs, config: params.cfg, onHelloOk: ({ snapshot }) => { gatewaySnapshot = snapshot; noteGatewayStateDirectory(snapshot, "live Gateway"); } }); healthOk = true; noteCliGatewayVersionSkew(status); if (status.startupMigrationWarning) note(sanitizeTerminalText(status.startupMigrationWarning), "Startup migration warnings"); const secretDegradations = projectDoctorSecretRuntimeDegradations(status); if (secretDegradations.length > 0) note(secretDegradations.map((owner) => `- ${owner.message}\n Retry: ${owner.retryHint}`).join("\n"), "Secret runtime degradation"); if (status.degradedPlugins && status.degradedPlugins.length > 0) note(status.degradedPlugins.map((plugin) => `- ${plugin.pluginId} (${plugin.diagnostic.reason}): ${plugin.diagnostic.detail}`).join("\n"), "Plugins configured unavailable"); const [channelsResult, exporterResult] = await Promise.allSettled([callGateway({ method: "channels.status", params: { probe: true, timeoutMs: 5e3 }, timeoutMs: 6e3, config: params.cfg }), callGateway({ method: "diagnostics.stability", params: { type: "telemetry.exporter", limit: 1e3 }, timeoutMs: Math.min(timeoutMs, 6e3), config: params.cfg })]); if (channelsResult.status === "fulfilled") { const issues = collectChannelStatusIssues(channelsResult.value); if (issues.length > 0) note(issues.map((issue) => `- ${issue.channel} ${issue.accountId}: ${issue.message}${issue.fix ? ` (${issue.fix})` : ""}`).join("\n"), "Channel warnings"); } else note([`Channel status probe failed: ${sanitizeTerminalText(formatErrorMessage(channelsResult.reason))}`, `Retry: ${formatCliCommand("openclaw channels status --probe")}`].join("\n"), "Channel warnings"); if (exporterResult.status === "fulfilled") { const exporterSummary = formatTelemetryExporterSummary(exporterResult.value); if (exporterSummary) note(exporterSummary.lines.join("\n"), exporterSummary.title); } else note([`Exporter diagnostics failed: ${sanitizeTerminalText(formatErrorMessage(exporterResult.reason))}`, `Retry: ${formatCliCommand("openclaw gateway stability --type telemetry.exporter")}`].join("\n"), "Telemetry exporters"); return { healthOk, authenticated: true, status }; } catch (err) { if (!gatewaySnapshot?.stateDir) await noteInstalledGatewayStateDirectory(params.cfg, timeoutMs); if (gatewayConnectErrorWasRateLimited(err)) { note(GATEWAY_HEALTH_RATE_LIMITED_MESSAGE, GATEWAY_HEALTH_RATE_LIMITED_TITLE); return { healthOk: true, authenticated: false }; } if (isGatewayHealthAuthUnavailableError(err)) { const probeDetails = await buildGatewayProbeConnectionDetails({ config: params.cfg }); const probe = await probeGatewayStatus({ url: probeDetails.url, timeoutMs, tlsFingerprint: probeDetails.tlsFingerprint, preauthHandshakeTimeoutMs: probeDetails.preauthHandshakeTimeoutMs, config: params.cfg, json: true }); if (gatewayProbeResultSawGateway(probe)) { if (gatewayProbeResultWasRateLimited(probe)) note(GATEWAY_HEALTH_RATE_LIMITED_MESSAGE, GATEWAY_HEALTH_RATE_LIMITED_TITLE); else note(GATEWAY_HEALTH_CREDENTIALS_REQUIRED_MESSAGE, GATEWAY_HEALTH_CREDENTIALS_REQUIRED_TITLE); healthOk = true; return { healthOk, authenticated: false }; } } const closedDiagnostic = formatGatewayClosedDiagnostic(err); if (closedDiagnostic) { const gatewayDetails = buildGatewayConnectionDetails({ config: params.cfg }); note(closedDiagnostic, "Gateway"); note(gatewayDetails.message, "Gateway connection"); } else params.runtime.error(formatHealthCheckFailure(err)); } return { healthOk, authenticated: false, status }; } /** Probes gateway memory readiness without forcing deep embedding checks. */ async function probeGatewayMemoryStatus(params) { const timeoutMs = typeof params.timeoutMs === "number" && params.timeoutMs > 0 ? params.timeoutMs : 8e3; try { const payload = await callGateway({ method: "doctor.memory.status", params: { probe: false }, timeoutMs, config: params.cfg }); const gatewayChecked = payload.embedding.checked !== false; return { checked: gatewayChecked, ready: payload.embedding.ok, error: payload.embedding.error, ...payload.embeddingRuntime ? { runtimeFacts: payload.embeddingRuntime } : {}, skipped: !gatewayChecked }; } catch (err) { const message = formatErrorMessage(err); if (isGatewayCallTimeout(message)) return { checked: false, ready: false, error: `gateway memory probe timed out: ${message}`, skipped: false }; return { checked: true, ready: false, error: `gateway memory probe unavailable: ${message}`, skipped: false }; } } //#endregion export { checkGatewayHealth, probeGatewayMemoryStatus };