UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

167 lines (166 loc) 7.53 kB
import { a as asOptionalRecord } from "../../record-coerce-DItp3I4t.js"; import { l as normalizeOptionalString } from "../../string-coerce-CIXf7egm.js"; import { r as runCommandWithTimeout } from "../../exec-BIE-3oLG.js"; import "../../string-coerce-runtime-GQa0ehRA.js"; import "../../process-runtime-pcN9RjT8.js"; import { d as resolveOpenClawRoot, r as findCrabboxBinary, t as CRABBOX_WORKER_PROVIDER_ID } from "../../crabbox-worker-profile-COqYDYGL.js"; import { l as listCrabboxWarmImages, o as crabboxWarmImageRecoveryHint, s as isCrabboxWarmImageCapturePaused } from "../../crabbox-worker-warm-image-store-DUhnKJbP.js"; import { fileURLToPath } from "node:url"; import path from "node:path"; //#region extensions/crabbox/src/crabbox-worker-doctor-runtime.ts const CRABBOX_VERSION_TIMEOUT_MS = 2e3; const CRABBOX_VERSION_MAX_OUTPUT_BYTES = 65536; async function probeCrabboxVersion(binary) { let result; try { result = await runCommandWithTimeout([binary, "--version"], { killProcessTree: true, maxOutputBytes: CRABBOX_VERSION_MAX_OUTPUT_BYTES, timeoutMs: CRABBOX_VERSION_TIMEOUT_MS }); } catch { return { status: "indeterminate", reason: "version command could not start" }; } if (result.termination !== "exit" || result.code !== 0 || result.outputLimitExceeded) return { status: "indeterminate", reason: result.termination === "timeout" ? `version command timed out after ${CRABBOX_VERSION_TIMEOUT_MS} ms` : result.outputLimitExceeded ? "version output exceeded 64 KiB" : result.termination !== "exit" ? `version command did not exit normally (${result.termination})` : `version command exited with code ${result.code ?? "unknown"}` }; const match = /(?:^|\s)v?(\d+)\.(\d+)\.(\d+)(?:\s|$)/u.exec(`${result.stdout}\n${result.stderr}`.trim()); if (!match) return { status: "indeterminate", reason: "version output was not recognized" }; const major = Number(match[1]); const minor = Number(match[2]); const patch = Number(match[3]); const version = `${major}.${minor}.${patch}`; return major > 0 || major === 0 && (minor > 41 || minor === 41 && patch >= 1) ? { status: "supported", version } : { status: "outdated", version }; } //#endregion //#region extensions/crabbox/src/doctor.ts const CRABBOX_CLOUD_WORKER_PROFILE_CHECK_ID = "crabbox/cloud-worker-profiles"; const CRABBOX_WARM_IMAGES_CHECK_ID = "crabbox/warm-images"; function finding(params) { return { checkId: CRABBOX_CLOUD_WORKER_PROFILE_CHECK_ID, severity: params.severity ?? "warning", source: "crabbox", message: `Cloud worker profile "${params.profileId}" ${params.message}`, ...params.binary ? { path: params.binary } : {}, ocPath: `cloudWorkers.profiles.${params.profileId}.settings.binary`, target: params.profileId, requirement: "an executable Crabbox 0.41.1 or newer binary", fixHint: params.fixHint }; } function repairHint(profileId, explicitBinary) { const configPath = `cloudWorkers.profiles.${profileId}.settings.binary`; return explicitBinary ? `Install Crabbox 0.41.1 or newer at ${explicitBinary}, or set ${configPath} to an executable absolute path, then rerun \`openclaw doctor --json\`.` : `Install Crabbox 0.41.1 or newer on the Gateway user's PATH, or set ${configPath} to an executable absolute path, then rerun \`openclaw doctor --json\`.`; } function createCrabboxCloudWorkerProfileCheck(openclawRoot) { return { id: CRABBOX_CLOUD_WORKER_PROFILE_CHECK_ID, kind: "plugin", description: "Verify configured Crabbox cloud worker profiles before dispatch.", source: "crabbox", async detect(ctx) { const profiles = Object.entries(ctx.cfg.cloudWorkers?.profiles ?? {}).filter(([, profile]) => profile.provider.trim().toLowerCase() === CRABBOX_WORKER_PROVIDER_ID); if (profiles.length === 0) return []; const probes = /* @__PURE__ */ new Map(); const findings = []; for (const [profileId, profile] of profiles) { const settings = asOptionalRecord(profile.settings); const explicitBinary = normalizeOptionalString(settings?.binary); const binary = findCrabboxBinary({ ...explicitBinary ? { explicit: explicitBinary } : {}, openclawRoot, pathEnv: ctx.env?.PATH ?? process.env.PATH }); if (!binary) { findings.push(finding({ profileId, ...explicitBinary ? { binary: explicitBinary } : {}, message: explicitBinary ? `cannot use Crabbox because ${explicitBinary} is not an executable file.` : "cannot resolve an executable Crabbox binary from the Gateway user's PATH.", fixHint: repairHint(profileId, explicitBinary) })); continue; } let probe = probes.get(binary); if (!probe) { probe = probeCrabboxVersion(binary); probes.set(binary, probe); } const result = await probe; if (result.status === "outdated") findings.push(finding({ profileId, binary, message: `uses Crabbox ${result.version}, but cloud workers require 0.41.1 or newer.`, fixHint: repairHint(profileId, explicitBinary) })); else if (result.status === "indeterminate") findings.push(finding({ profileId, binary, severity: "info", message: `has an executable Crabbox binary, but Doctor could not determine its version: ${result.reason}.`, fixHint: `Run \`${binary} --version\` and confirm it reports Crabbox 0.41.1 or newer, then rerun \`openclaw doctor --json --severity-min info\`.` })); } return findings; } }; } function registerCrabboxWorkerProviderDoctorChecks(host) { if (!host.getHealthCheck("crabbox/cloud-worker-profiles")) host.registerHealthCheck(createCrabboxCloudWorkerProfileCheck(host.openclawRoot)); if (!host.getHealthCheck(CRABBOX_WARM_IMAGES_CHECK_ID)) host.registerHealthCheck({ id: CRABBOX_WARM_IMAGES_CHECK_ID, kind: "plugin", description: "Report paused Crabbox warm-image captures and retained cleanup obligations.", source: "crabbox", async detect(ctx) { const findings = []; for (const image of listCrabboxWarmImages(ctx.env)) { const details = { checkId: CRABBOX_WARM_IMAGES_CHECK_ID, severity: "warning", source: "crabbox", target: image.profileKey }; if (image.capture) { const paused = isCrabboxWarmImageCapturePaused(image.capture); findings.push({ ...details, severity: paused ? "warning" : "info", message: paused ? `Warm-image capture ${image.capture.selector} is paused; its provider outcome requires manual reconciliation.` : `Warm-image capture ${image.capture.selector} is in progress.`, fixHint: paused ? crabboxWarmImageRecoveryHint(image.capture.selector) : "Allow the current capture to finish; inspect `openclaw crabbox warm-images --json` if it remains pending." }); } if (image.retirement) findings.push({ ...details, message: `Warm-image checkpoint ${image.retirement.checkpointId} is still awaiting deletion.`, fixHint: "Cleanup retries during the next warm-image capture or worker teardown. Inspect `openclaw crabbox warm-images --json` and resolve provider deletion errors if it remains pending." }); } return findings; } }); } //#endregion //#region extensions/crabbox/doctor-health-api.ts const CRABBOX_PLUGIN_ROOT = path.dirname(fileURLToPath(import.meta.url)); function registerWorkerProviderDoctorChecks(host) { registerCrabboxWorkerProviderDoctorChecks({ ...host, openclawRoot: resolveOpenClawRoot(CRABBOX_PLUGIN_ROOT) }); } //#endregion export { CRABBOX_CLOUD_WORKER_PROFILE_CHECK_ID, registerWorkerProviderDoctorChecks };