openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
116 lines (115 loc) • 4.18 kB
JavaScript
import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js";
import { i as isLoopbackHost } from "./net-DTe7AQiu.js";
import "./string-coerce-runtime-CEGJWkQ_.js";
import "./sdk-security-runtime-D1sDHmUD.js";
import { r as setBridgeAuthForPort, t as deleteBridgeAuthForPort } from "./bridge-auth-registry-BXJYZpp6.js";
import { n as installBrowserAuthMiddleware, r as installBrowserCommonMiddleware, t as hasVerifiedBrowserAuth } from "./server-middleware-BrxqA0ot.js";
import express from "express";
//#region extensions/browser/src/browser/bridge-server.ts
function buildNoVncBootstrapHtml(params) {
const hash = new URLSearchParams({
autoconnect: "1",
resize: "remote"
});
const password = normalizeOptionalString(params.password);
if (password) hash.set("password", password);
const targetUrl = `http://127.0.0.1:${params.noVncPort}/vnc.html#${hash.toString()}`;
return `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="referrer" content="no-referrer" />
<title>OpenClaw noVNC Observer</title>
</head>
<body>
<p>Opening sandbox observer...</p>
<script>
const target = ${JSON.stringify(targetUrl)};
window.location.replace(target);
<\/script>
</body>
</html>`;
}
/** Start an authenticated loopback browser bridge and register browser routes. */
async function startBrowserBridgeServer(params) {
const host = params.host ?? "127.0.0.1";
if (!isLoopbackHost(host)) throw new Error(`bridge server must bind to loopback host (got ${host})`);
const port = params.port ?? 0;
const app = express();
installBrowserCommonMiddleware(app);
const authToken = normalizeOptionalString(params.authToken);
const authPassword = normalizeOptionalString(params.authPassword);
if (!authToken && !authPassword) throw new Error("bridge server requires auth (authToken/authPassword missing)");
installBrowserAuthMiddleware(app, {
token: authToken,
password: authPassword
});
if (params.resolveSandboxNoVncToken) app.get("/sandbox/novnc", (req, res) => {
if (!hasVerifiedBrowserAuth(req)) {
res.status(401).send("Unauthorized");
return;
}
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
res.setHeader("Pragma", "no-cache");
res.setHeader("Expires", "0");
res.setHeader("Referrer-Policy", "no-referrer");
const rawToken = normalizeOptionalString(req.query?.token);
if (!rawToken) {
res.status(400).send("Missing token");
return;
}
const resolved = params.resolveSandboxNoVncToken?.(rawToken);
if (!resolved) {
res.status(404).send("Invalid or expired token");
return;
}
res.type("html").status(200).send(buildNoVncBootstrapHtml(resolved));
});
const state = {
server: null,
port,
resolved: params.resolved,
profiles: /* @__PURE__ */ new Map()
};
if (params.skipRouteRegistrationForTest) app.get("/", (_req, res) => {
res.status(200).send("OK");
});
else {
const [{ createBrowserRouteContext }, { registerBrowserRoutes }] = await Promise.all([import("./server-context-udSW7yfg.js"), import("./routes-Ca5yC1Xg.js")]);
registerBrowserRoutes(app, createBrowserRouteContext({
getState: () => state,
onEnsureAttachTarget: params.onEnsureAttachTarget
}));
}
const server = await new Promise((resolve, reject) => {
const s = app.listen(port, host, () => resolve(s));
s.once("error", reject);
});
const resolvedPort = server.address()?.port ?? port;
state.server = server;
state.port = resolvedPort;
state.resolved.controlPort = resolvedPort;
setBridgeAuthForPort(resolvedPort, {
token: authToken,
password: authPassword
});
return {
server,
port: resolvedPort,
baseUrl: `http://${host}:${resolvedPort}`,
state
};
}
/** Stop a browser bridge server and clear its ephemeral port auth. */
async function stopBrowserBridgeServer(server) {
try {
const address = server.address();
if (address?.port) deleteBridgeAuthForPort(address.port);
} catch {}
await new Promise((resolve) => {
server.close(() => resolve());
});
}
//#endregion
export { stopBrowserBridgeServer as n, startBrowserBridgeServer as t };