Files
openclaw/extensions/qa-lab/src/lab-server-capture.ts
Gustavo Madeira Santana 4db162db7f QA: split lab runtime and extend Matrix coverage (#67430)
Merged via squash.

Prepared head SHA: 790418b93b
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-16 03:08:39 -04:00

128 lines
3.1 KiB
TypeScript

import net from "node:net";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
const CAPTURE_QUERY_PRESETS = new Set([
"double-sends",
"retry-storms",
"cache-busting",
"ws-duplicate-frames",
"missing-ack",
"error-bursts",
]);
export type QaStartupProbeStatus = {
label: string;
url: string;
ok: boolean;
error?: string;
};
export function isCaptureQueryPreset(
value: string,
): value is Parameters<
ReturnType<
typeof import("openclaw/plugin-sdk/proxy-capture").getDebugProxyCaptureStore
>["queryPreset"]
>[0] {
return CAPTURE_QUERY_PRESETS.has(value);
}
function parseCaptureMeta(metaJson: unknown): Record<string, unknown> | null {
if (typeof metaJson !== "string" || metaJson.trim().length === 0) {
return null;
}
try {
const parsed = JSON.parse(metaJson) as unknown;
return parsed && typeof parsed === "object" ? (parsed as Record<string, unknown>) : null;
} catch {
return null;
}
}
function readCaptureMetaString(
meta: Record<string, unknown> | null,
key: string,
): string | undefined {
const value = meta?.[key];
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
}
export function mapCaptureEventForQa(row: Record<string, unknown>) {
const meta = parseCaptureMeta(row.metaJson);
return {
...row,
payloadPreview: typeof row.dataText === "string" ? row.dataText : undefined,
provider: readCaptureMetaString(meta, "provider"),
api: readCaptureMetaString(meta, "api"),
model: readCaptureMetaString(meta, "model"),
captureOrigin: readCaptureMetaString(meta, "captureOrigin"),
};
}
function defaultPortForProtocol(protocol: string): number {
if (protocol === "https:") {
return 443;
}
if (protocol === "http:") {
return 80;
}
return 0;
}
export async function probeTcpReachability(
rawUrl: string,
timeoutMs = 700,
): Promise<QaStartupProbeStatus> {
let parsed: URL;
try {
parsed = new URL(rawUrl);
} catch {
return {
label: rawUrl,
url: rawUrl,
ok: false,
error: "invalid url",
};
}
const host = parsed.hostname;
const port = parsed.port ? Number(parsed.port) : defaultPortForProtocol(parsed.protocol);
if (!host || !Number.isFinite(port) || port <= 0) {
return {
label: parsed.origin,
url: parsed.toString(),
ok: false,
error: "missing host or port",
};
}
try {
await new Promise<void>((resolve, reject) => {
const socket = net.createConnection({ host, port });
const onError = (error: Error) => {
socket.destroy();
reject(error);
};
socket.setTimeout(timeoutMs, () => {
socket.destroy(new Error("timeout"));
});
socket.once("connect", () => {
socket.end();
resolve();
});
socket.once("error", onError);
socket.once("timeout", () => onError(new Error("timeout")));
});
return {
label: parsed.host,
url: parsed.toString(),
ok: true,
};
} catch (error) {
return {
label: parsed.host,
url: parsed.toString(),
ok: false,
error: formatErrorMessage(error),
};
}
}