import { setTimeout as sleep } from "node:timers/promises"; import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime"; import type { QaConfigSnapshot, QaSuiteRuntimeEnv } from "./suite-runtime-types.js";
function isConfigHashConflict(error: unknown) { return formatGatewayPrimaryErrorText(error).includes("config changed since last load");
}
function getGatewayRetryAfterMs(error: unknown) { const text = formatGatewayPrimaryErrorText(error); const millisecondsMatch = /retryAfterMs["=: ]+(\d+)/i.exec(text); if (millisecondsMatch) { const parsed = Number(millisecondsMatch[1]); if (Number.isFinite(parsed) && parsed > 0) { return parsed;
}
} const secondsMatch = /retry after (\d+)s/i.exec(text); if (secondsMatch) { const parsed = Number(secondsMatch[1]); if (Number.isFinite(parsed) && parsed > 0) { return parsed * 1_000;
}
} returnnull;
}
function isPlainObject(value: unknown): value is Record<string, unknown> { returntypeof value === "object" && value !== null && !Array.isArray(value);
}
function isObjectWithStringId(value: unknown): value is { id: string } & Record<string, unknown> { return isPlainObject(value) && typeof value.id === "string";
}
function applyQaMergePatch(target: unknown, patch: unknown): unknown { if (Array.isArray(target) && Array.isArray(patch)) { const merged = target.map((entry) => structuredClone(entry)); const indexById = new Map<string, number>(); for (const [index, entry] of merged.entries()) { if (isObjectWithStringId(entry)) {
indexById.set(entry.id, index);
}
} for (const patchEntry of patch) { if (!isObjectWithStringId(patchEntry)) {
merged.push(structuredClone(patchEntry)); continue;
} const existingIndex = indexById.get(patchEntry.id); if (existingIndex === undefined) {
merged.push(structuredClone(patchEntry));
indexById.set(patchEntry.id, merged.length - 1); continue;
}
merged[existingIndex] = applyQaMergePatch(merged[existingIndex], patchEntry);
} return merged;
} if (!isPlainObject(patch)) { return structuredClone(patch);
} const base = isPlainObject(target) ? structuredClone(target) : {}; for (const [key, value] of Object.entries(patch)) { if (value === null) { delete base[key]; continue;
}
base[key] = applyQaMergePatch(base[key], value);
} return base;
}
function areJsonValuesEqual(left: unknown, right: unknown): boolean { if (Object.is(left, right)) { returntrue;
} if (Array.isArray(left) || Array.isArray(right)) { if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) { returnfalse;
} return left.every((entry, index) => areJsonValuesEqual(entry, right[index]));
} if (isPlainObject(left) || isPlainObject(right)) { if (!isPlainObject(left) || !isPlainObject(right)) { returnfalse;
} const leftKeys = Object.keys(left).toSorted(); const rightKeys = Object.keys(right).toSorted(); if (!areJsonValuesEqual(leftKeys, rightKeys)) { returnfalse;
} return leftKeys.every((key) => areJsonValuesEqual(left[key], right[key]));
} returnfalse;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.