openclaw/src/gateway/server-tailscale.ts

62 lines
1.8 KiB
TypeScript

import {
disableTailscaleFunnel,
disableTailscaleServe,
enableTailscaleFunnel,
enableTailscaleServe,
getTailnetHostname,
} from "../infra/tailscale.js";
export async function startGatewayTailscaleExposure(params: {
tailscaleMode: "off" | "serve" | "funnel";
resetOnExit?: boolean;
socket?: string;
port: number;
controlUiBasePath?: string;
logTailscale: { info: (msg: string) => void; warn: (msg: string) => void };
}): Promise<(() => Promise<void>) | null> {
if (params.tailscaleMode === "off") {
return null;
}
const socketOpts = params.socket ? { socket: params.socket } : undefined;
try {
if (params.tailscaleMode === "serve") {
await enableTailscaleServe(params.port, undefined, socketOpts);
} else {
await enableTailscaleFunnel(params.port, undefined, socketOpts);
}
const host = await getTailnetHostname(undefined, undefined, socketOpts).catch(() => null);
if (host) {
const uiPath = params.controlUiBasePath ? `${params.controlUiBasePath}/` : "/";
params.logTailscale.info(
`${params.tailscaleMode} enabled: https://${host}${uiPath} (WS via wss://${host})`,
);
} else {
params.logTailscale.info(`${params.tailscaleMode} enabled`);
}
} catch (err) {
params.logTailscale.warn(
`${params.tailscaleMode} failed: ${err instanceof Error ? err.message : String(err)}`,
);
}
if (!params.resetOnExit) {
return null;
}
return async () => {
try {
if (params.tailscaleMode === "serve") {
await disableTailscaleServe(undefined, socketOpts);
} else {
await disableTailscaleFunnel(undefined, socketOpts);
}
} catch (err) {
params.logTailscale.warn(
`${params.tailscaleMode} cleanup failed: ${err instanceof Error ? err.message : String(err)}`,
);
}
};
}