Add adapter environment testing infrastructure
Introduce testEnvironment() on ServerAdapterModule with structured pass/warn/fail diagnostics for all four adapter types (claude_local, codex_local, process, http). Adds POST test-environment endpoint, shared types/validators, adapter test implementations, and UI API client. Includes asset type foundations used by related features. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
116
server/src/adapters/http/test.ts
Normal file
116
server/src/adapters/http/test.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import type {
|
||||
AdapterEnvironmentCheck,
|
||||
AdapterEnvironmentTestContext,
|
||||
AdapterEnvironmentTestResult,
|
||||
} from "../types.js";
|
||||
import { asString, parseObject } from "../utils.js";
|
||||
|
||||
function summarizeStatus(checks: AdapterEnvironmentCheck[]): AdapterEnvironmentTestResult["status"] {
|
||||
if (checks.some((check) => check.level === "error")) return "fail";
|
||||
if (checks.some((check) => check.level === "warn")) return "warn";
|
||||
return "pass";
|
||||
}
|
||||
|
||||
function normalizeMethod(input: string): string {
|
||||
const trimmed = input.trim();
|
||||
return trimmed.length > 0 ? trimmed.toUpperCase() : "POST";
|
||||
}
|
||||
|
||||
export async function testEnvironment(
|
||||
ctx: AdapterEnvironmentTestContext,
|
||||
): Promise<AdapterEnvironmentTestResult> {
|
||||
const checks: AdapterEnvironmentCheck[] = [];
|
||||
const config = parseObject(ctx.config);
|
||||
const urlValue = asString(config.url, "");
|
||||
const method = normalizeMethod(asString(config.method, "POST"));
|
||||
|
||||
if (!urlValue) {
|
||||
checks.push({
|
||||
code: "http_url_missing",
|
||||
level: "error",
|
||||
message: "HTTP adapter requires a URL.",
|
||||
hint: "Set adapterConfig.url to an absolute http(s) endpoint.",
|
||||
});
|
||||
return {
|
||||
adapterType: ctx.adapterType,
|
||||
status: summarizeStatus(checks),
|
||||
checks,
|
||||
testedAt: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
let url: URL | null = null;
|
||||
try {
|
||||
url = new URL(urlValue);
|
||||
} catch {
|
||||
checks.push({
|
||||
code: "http_url_invalid",
|
||||
level: "error",
|
||||
message: `Invalid URL: ${urlValue}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (url && url.protocol !== "http:" && url.protocol !== "https:") {
|
||||
checks.push({
|
||||
code: "http_url_protocol_invalid",
|
||||
level: "error",
|
||||
message: `Unsupported URL protocol: ${url.protocol}`,
|
||||
hint: "Use an http:// or https:// endpoint.",
|
||||
});
|
||||
}
|
||||
|
||||
if (url) {
|
||||
checks.push({
|
||||
code: "http_url_valid",
|
||||
level: "info",
|
||||
message: `Configured endpoint: ${url.toString()}`,
|
||||
});
|
||||
}
|
||||
|
||||
checks.push({
|
||||
code: "http_method_configured",
|
||||
level: "info",
|
||||
message: `Configured method: ${method}`,
|
||||
});
|
||||
|
||||
if (url && (url.protocol === "http:" || url.protocol === "https:")) {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 3000);
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: "HEAD",
|
||||
signal: controller.signal,
|
||||
});
|
||||
if (!response.ok && response.status !== 405 && response.status !== 501) {
|
||||
checks.push({
|
||||
code: "http_endpoint_probe_unexpected_status",
|
||||
level: "warn",
|
||||
message: `Endpoint probe returned HTTP ${response.status}.`,
|
||||
hint: "Verify the endpoint is reachable from the Paperclip server host.",
|
||||
});
|
||||
} else {
|
||||
checks.push({
|
||||
code: "http_endpoint_probe_ok",
|
||||
level: "info",
|
||||
message: "Endpoint responded to a HEAD probe.",
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
checks.push({
|
||||
code: "http_endpoint_probe_failed",
|
||||
level: "warn",
|
||||
message: err instanceof Error ? err.message : "Endpoint probe failed",
|
||||
hint: "This may be expected in restricted networks; verify connectivity when invoking runs.",
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
adapterType: ctx.adapterType,
|
||||
status: summarizeStatus(checks),
|
||||
checks,
|
||||
testedAt: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user