mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 22:40:43 +00:00
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
128 lines
3.1 KiB
TypeScript
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),
|
|
};
|
|
}
|
|
}
|