Files
openclaw/extensions/diffs/src/url.ts
2026-03-04 02:35:12 -05:00

57 lines
1.9 KiB
TypeScript

import type { OpenClawConfig } from "openclaw/plugin-sdk/diffs";
const DEFAULT_GATEWAY_PORT = 18789;
export function buildViewerUrl(params: {
config: OpenClawConfig;
viewerPath: string;
baseUrl?: string;
}): string {
const baseUrl = params.baseUrl?.trim() || resolveGatewayBaseUrl(params.config);
const normalizedBase = normalizeViewerBaseUrl(baseUrl);
const viewerPath = params.viewerPath.startsWith("/")
? params.viewerPath
: `/${params.viewerPath}`;
const parsedBase = new URL(normalizedBase);
const basePath = parsedBase.pathname === "/" ? "" : parsedBase.pathname.replace(/\/+$/, "");
parsedBase.pathname = `${basePath}${viewerPath}`;
parsedBase.search = "";
parsedBase.hash = "";
return parsedBase.toString();
}
export function normalizeViewerBaseUrl(raw: string): string {
let parsed: URL;
try {
parsed = new URL(raw);
} catch {
throw new Error(`Invalid baseUrl: ${raw}`);
}
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
throw new Error(`baseUrl must use http or https: ${raw}`);
}
if (parsed.search || parsed.hash) {
throw new Error(`baseUrl must not include query/hash: ${raw}`);
}
parsed.search = "";
parsed.hash = "";
parsed.pathname = parsed.pathname.replace(/\/+$/, "");
const withoutTrailingSlash = parsed.toString().replace(/\/+$/, "");
return withoutTrailingSlash;
}
function resolveGatewayBaseUrl(config: OpenClawConfig): string {
const scheme = config.gateway?.tls?.enabled ? "https" : "http";
const port =
typeof config.gateway?.port === "number" ? config.gateway.port : DEFAULT_GATEWAY_PORT;
const customHost = config.gateway?.customBindHost?.trim();
if (config.gateway?.bind === "custom" && customHost) {
return `${scheme}://${customHost}:${port}`;
}
// Viewer links are used by local canvas/clients; default to loopback to avoid
// container/bridge interfaces that are often unreachable from the caller.
return `${scheme}://127.0.0.1:${port}`;
}