refactor(device-pair): reduce duplicated gateway parsing

This commit is contained in:
Peter Steinberger
2026-02-18 16:08:29 +00:00
parent 95d52b06d5
commit 29d3bb278f

View File

@@ -37,45 +37,49 @@ type ResolveAuthResult = {
};
function normalizeUrl(raw: string, schemeFallback: "ws" | "wss"): string | null {
const trimmed = raw.trim();
if (!trimmed) {
const candidate = raw.trim();
if (!candidate) {
return null;
}
try {
const parsed = new URL(trimmed);
const scheme = parsed.protocol.replace(":", "");
if (!scheme) {
return null;
}
const resolvedScheme = scheme === "http" ? "ws" : scheme === "https" ? "wss" : scheme;
if (resolvedScheme !== "ws" && resolvedScheme !== "wss") {
return null;
}
const host = parsed.hostname;
if (!host) {
return null;
}
const port = parsed.port ? `:${parsed.port}` : "";
return `${resolvedScheme}://${host}${port}`;
} catch {
// Fall through to host:port parsing.
const parsedUrl = parseNormalizedGatewayUrl(candidate);
if (parsedUrl) {
return parsedUrl;
}
const hostPort = candidate.split("/", 1)[0]?.trim() ?? "";
return hostPort ? `${schemeFallback}://${hostPort}` : null;
}
const withoutPath = trimmed.split("/")[0] ?? "";
if (!withoutPath) {
function parseNormalizedGatewayUrl(raw: string): string | null {
try {
const parsed = new URL(raw);
const scheme = parsed.protocol.slice(0, -1);
const normalizedScheme = scheme === "http" ? "ws" : scheme === "https" ? "wss" : scheme;
if (!(normalizedScheme === "ws" || normalizedScheme === "wss")) {
return null;
}
if (!parsed.hostname) {
return null;
}
return `${normalizedScheme}://${parsed.hostname}${parsed.port ? `:${parsed.port}` : ""}`;
} catch {
return null;
}
return `${schemeFallback}://${withoutPath}`;
}
function parsePositiveInteger(raw: string | undefined): number | null {
if (!raw) {
return null;
}
const parsed = Number.parseInt(raw, 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
}
function resolveGatewayPort(cfg: OpenClawPluginApi["config"]): number {
const envRaw =
process.env.OPENCLAW_GATEWAY_PORT?.trim() || process.env.CLAWDBOT_GATEWAY_PORT?.trim();
if (envRaw) {
const parsed = Number.parseInt(envRaw, 10);
if (Number.isFinite(parsed) && parsed > 0) {
return parsed;
}
const envPort =
parsePositiveInteger(process.env.OPENCLAW_GATEWAY_PORT?.trim()) ??
parsePositiveInteger(process.env.CLAWDBOT_GATEWAY_PORT?.trim());
if (envPort) {
return envPort;
}
const configPort = cfg.gateway?.port;
if (typeof configPort === "number" && Number.isFinite(configPort) && configPort > 0) {
@@ -215,25 +219,20 @@ function parsePossiblyNoisyJsonObject(raw: string): Record<string, unknown> {
function resolveAuth(cfg: OpenClawPluginApi["config"]): ResolveAuthResult {
const mode = cfg.gateway?.auth?.mode;
const token =
process.env.OPENCLAW_GATEWAY_TOKEN?.trim() ||
process.env.CLAWDBOT_GATEWAY_TOKEN?.trim() ||
cfg.gateway?.auth?.token?.trim();
pickFirstDefined([
process.env.OPENCLAW_GATEWAY_TOKEN,
process.env.CLAWDBOT_GATEWAY_TOKEN,
cfg.gateway?.auth?.token,
]) ?? undefined;
const password =
process.env.OPENCLAW_GATEWAY_PASSWORD?.trim() ||
process.env.CLAWDBOT_GATEWAY_PASSWORD?.trim() ||
cfg.gateway?.auth?.password?.trim();
pickFirstDefined([
process.env.OPENCLAW_GATEWAY_PASSWORD,
process.env.CLAWDBOT_GATEWAY_PASSWORD,
cfg.gateway?.auth?.password,
]) ?? undefined;
if (mode === "password") {
if (!password) {
return { error: "Gateway auth is set to password, but no password is configured." };
}
return { password, label: "password" };
}
if (mode === "token") {
if (!token) {
return { error: "Gateway auth is set to token, but no token is configured." };
}
return { token, label: "token" };
if (mode === "token" || mode === "password") {
return resolveRequiredAuth(mode, { token, password });
}
if (token) {
return { token, label: "token" };
@@ -244,6 +243,30 @@ function resolveAuth(cfg: OpenClawPluginApi["config"]): ResolveAuthResult {
return { error: "Gateway auth is not configured (no token or password)." };
}
function pickFirstDefined(candidates: Array<string | undefined>): string | null {
for (const value of candidates) {
const trimmed = value?.trim();
if (trimmed) {
return trimmed;
}
}
return null;
}
function resolveRequiredAuth(
mode: "token" | "password",
values: { token?: string; password?: string },
): ResolveAuthResult {
if (mode === "token") {
return values.token
? { token: values.token, label: "token" }
: { error: "Gateway auth is set to token, but no token is configured." };
}
return values.password
? { password: values.password, label: "password" }
: { error: "Gateway auth is set to password, but no password is configured." };
}
async function resolveGatewayUrl(api: OpenClawPluginApi): Promise<ResolveUrlResult> {
const cfg = api.config;
const pluginCfg = (api.pluginConfig ?? {}) as DevicePairPluginConfig;