openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
159 lines (158 loc) • 5.22 kB
JavaScript
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { t as escapeRegExp } from "./regexp-BZyMFTlj.js";
import "./string-coerce-runtime-GQa0ehRA.js";
import "./paths-CA_QMzCv.js";
import { t as registerBrowserRoutes } from "./routes-g8jdcRKn.js";
//#region extensions/browser/src/browser/request-policy.ts
/**
* Request policy helpers for profile-aware Browser control server routes.
*/
/** Normalizes route paths so mutation-policy checks compare stable slash forms. */
function normalizeBrowserRequestPath(value) {
const trimmed = value.trim();
if (!trimmed) return trimmed;
const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
if (withLeadingSlash.length <= 1) return withLeadingSlash;
return withLeadingSlash.replace(/\/+$/, "");
}
/** Returns true when a control request mutates persistent browser profile state. */
function isPersistentBrowserProfileMutation(method, path) {
const normalizedPath = normalizeBrowserRequestPath(path);
if (method === "POST" && (normalizedPath === "/profiles/create" || normalizedPath === "/profiles/import" || normalizedPath === "/reset-profile")) return true;
return method === "DELETE" && /^\/profiles\/[^/]+$/.test(normalizedPath);
}
/**
* Returns true for the system-profile cookie import route. Import must run where
* the user's Keychain lives, so it is exempt from the host-local persistent
* mutation block while remaining blocked over a node proxy.
*/
function isBrowserSystemProfileImport(method, path) {
return method === "POST" && normalizeBrowserRequestPath(path) === "/profiles/import";
}
/**
* Returns true for routes that only make sense on the host that owns the local
* Keychain and Chrome-family profiles: system-profile listing and import. These
* must be dispatched host-local and never proxied to a browser node.
*/
function isBrowserHostLocalRoute(method, path) {
if (isBrowserSystemProfileImport(method, path)) return true;
const normalizedPath = normalizeBrowserRequestPath(path);
return method === "GET" && (normalizedPath === "/system-profiles" || normalizedPath === "/system-profile-import/status") || method === "POST" && normalizedPath === "/system-profile-import/dismiss";
}
/** Resolves the requested profile from query, body, or route defaults. */
function resolveRequestedBrowserProfile(params) {
const queryProfile = normalizeOptionalString(params.query?.profile);
if (queryProfile) return queryProfile;
if (params.body && typeof params.body === "object") {
const bodyProfile = "profile" in params.body ? normalizeOptionalString(params.body.profile) : void 0;
if (bodyProfile) return bodyProfile;
}
return normalizeOptionalString(params.profile);
}
//#endregion
//#region extensions/browser/src/browser/routes/dispatcher.ts
/**
* Browser route dispatcher.
*
* Provides an in-process request/response adapter so Gateway nodes can invoke
* the same route handlers without opening an HTTP socket.
*/
function compileRoute(path) {
const paramNames = [];
const parts = path.split("/").map((part) => {
if (part.startsWith(":")) {
const name = part.slice(1);
paramNames.push(name);
return "([^/]+)";
}
return escapeRegExp(part);
});
return {
regex: new RegExp(`^${parts.join("/")}$`),
paramNames
};
}
function createRegistry() {
const routes = [];
const register = (method) => (path, handler) => {
const { regex, paramNames } = compileRoute(path);
routes.push({
method,
path,
regex,
paramNames,
handler
});
};
return {
routes,
router: {
get: register("GET"),
post: register("POST"),
delete: register("DELETE")
}
};
}
/** Create an in-process dispatcher for registered browser routes. */
function createBrowserRouteDispatcher(ctx) {
const registry = createRegistry();
registerBrowserRoutes(registry.router, ctx);
return { dispatch: async (req) => {
const method = req.method;
const path = normalizeBrowserRequestPath(req.path) || "/";
const query = req.query ?? {};
const body = req.body;
const signal = req.signal;
const match = registry.routes.find((route) => {
if (route.method !== method) return false;
return route.regex.test(path);
});
if (!match) return {
status: 404,
body: { error: "Not Found" }
};
const exec = match.regex.exec(path);
const params = {};
if (exec) for (const [idx, name] of match.paramNames.entries()) {
const value = exec[idx + 1];
if (typeof value === "string") try {
params[name] = decodeURIComponent(value);
} catch {
return {
status: 400,
body: { error: `invalid path parameter encoding: ${name}` }
};
}
}
let status = 200;
let payload = void 0;
const res = {
status(code) {
status = code;
return res;
},
json(bodyValue) {
payload = bodyValue;
}
};
try {
await match.handler({
params,
query,
body,
signal
}, res);
} catch (err) {
return {
status: 500,
body: { error: String(err) }
};
}
return {
status,
body: payload
};
} };
}
//#endregion
export { resolveRequestedBrowserProfile as a, normalizeBrowserRequestPath as i, isBrowserHostLocalRoute as n, isPersistentBrowserProfileMutation as r, createBrowserRouteDispatcher as t };