openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
167 lines (166 loc) • 7.53 kB
JavaScript
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 };