openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
193 lines (192 loc) • 8.97 kB
JavaScript
import { r as createLazyRuntimeModule } from "./lazy-runtime-CgCh8H_K.js";
import { n as isTruthyEnvValue } from "./env-M3R40TOb.js";
import { t as createSubsystemLogger } from "./subsystem-Dy2tqXOS.js";
import "./runtime-env-0Q-gCyUb.js";
import { t as isBrowserMachineOutput } from "./cli-output-mode-2_DMtO9_.js";
import { n as BROWSER_REQUEST_GATEWAY_SCOPE, t as BROWSER_REQUEST_GATEWAY_METHOD } from "./browser-gateway-contract-B6OC_gCs.js";
import { c as BROWSER_PROXY_UPLOAD_COMMAND, i as describeBrowserTool, n as createBrowserToolSchema, o as parseBrowserTabToolBinding, r as resolveBrowserToolCapabilities, s as BROWSER_PROXY_COMMAND, t as BrowserToolOutputSchema } from "./browser-tool.schema-CFr5Al89.js";
import { a as resolveProfile, r as resolveBrowserConfig } from "./config-BosO4Zbt.js";
import { t as getBrowserProfileCapabilities } from "./profile-capabilities-B30ig3n7.js";
import { s as initializeBrowserSessionTabStore } from "./session-tab-store-dEfDhf6J.js";
import { t as configureSystemProfileImportStateStore } from "./system-profile-import-state-DFy4mHBX.js";
//#region extensions/browser/plugin-registration.ts
const EAGER_BROWSER_CONTROL_SERVICE_ENV = "OPENCLAW_EAGER_BROWSER_CONTROL_SERVER";
const logger = createSubsystemLogger("browser");
const loadBrowserRegistrationRuntimeModule = createLazyRuntimeModule(() => import("./extensions/browser/register.runtime.js"));
const loadBrowserUploadCleanupRuntimeModule = createLazyRuntimeModule(() => import("./browser-proxy-upload-cleanup.runtime.js"));
function deriveChatTypeFromSessionKey(sessionKey) {
const tokens = new Set(sessionKey?.toLowerCase().split(":").filter(Boolean) ?? []);
if (tokens.has("group")) return "group";
if (tokens.has("channel")) return "channel";
if (tokens.has("direct") || tokens.has("dm")) return "direct";
}
const BROWSER_CLI_DESCRIPTOR = {
name: "browser",
description: "Manage OpenClaw's dedicated browser (Chrome/Chromium)",
hasSubcommands: true,
machineOutput: isBrowserMachineOutput
};
function createLazyBrowserTool(opts, config) {
const bindingResult = opts?.runToolBinding === void 0 ? void 0 : parseBrowserTabToolBinding(opts.runToolBinding);
if (bindingResult && !bindingResult.ok) throw new Error(`invalid browser run binding: ${bindingResult.error}`);
const targetDefault = opts?.sandboxBridgeUrl ? "sandbox" : "host";
const hostHint = opts?.allowHostControl === false ? "Host target blocked by policy." : "Host target allowed.";
const boundProfile = bindingResult?.ok && bindingResult.binding.target === "host" ? resolveProfile(resolveBrowserConfig(config?.browser, config), bindingResult.binding.profile) : void 0;
const capabilities = resolveBrowserToolCapabilities({
tabBound: bindingResult?.ok,
evaluateEnabled: config?.browser?.evaluateEnabled !== false,
...boundProfile ? { profileCapabilities: getBrowserProfileCapabilities(boundProfile) } : {}
});
return {
label: "Browser",
name: "browser",
resultContentSource: "network",
description: describeBrowserTool({
targetDefault,
hostHint,
capabilities
}),
parameters: createBrowserToolSchema(capabilities),
outputSchema: BrowserToolOutputSchema,
execute: async (toolCallId, args, signal, onUpdate) => {
const { createBrowserTool } = await loadBrowserRegistrationRuntimeModule();
return await createBrowserTool(bindingResult?.ok ? {
...opts,
runToolBinding: bindingResult.binding,
toolCapabilities: capabilities
} : {
...opts,
toolCapabilities: capabilities
}).execute(toolCallId, args, signal, onUpdate);
}
};
}
function createBrowserToolOptions(ctx) {
const mediaChannel = ctx.deliveryContext?.channel ?? ctx.messageChannel;
const mediaChatType = deriveChatTypeFromSessionKey(ctx.sessionKey);
return {
...ctx.browser?.sandboxBridgeUrl ? { sandboxBridgeUrl: ctx.browser.sandboxBridgeUrl } : {},
...ctx.browser?.allowHostControl !== void 0 ? { allowHostControl: ctx.browser.allowHostControl } : {},
...ctx.sessionKey ? { agentSessionKey: ctx.sessionKey } : {},
...ctx.agentId ? { agentId: ctx.agentId } : {},
...ctx.agentDir ? { agentDir: ctx.agentDir } : {},
...ctx.workspaceDir ? { workspaceDir: ctx.workspaceDir } : {},
...ctx.activeModel?.provider || ctx.activeModel?.modelId ? { activeModel: {
provider: ctx.activeModel.provider,
model: ctx.activeModel.modelId
} } : {},
...ctx.sessionKey || mediaChannel ? { mediaScope: {
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
...mediaChannel ? { channel: mediaChannel } : {},
...mediaChatType ? { chatType: mediaChatType } : {}
} } : {},
...ctx.toolBindings && Object.hasOwn(ctx.toolBindings, "browser") ? { runToolBinding: ctx.toolBindings.browser } : {}
};
}
/** Browser plugin reload policy. */
const browserPluginReload = {
restartPrefixes: ["browser"],
hotPrefixes: [
"browser.profiles",
"browser.defaultProfile",
"browser.headless",
"browser.executablePath",
"browser.attachOnly",
"browser.cdpUrl",
"browser.noSandbox",
"browser.extraArgs",
"browser.snapshotDefaults",
"browser.tabCleanup",
"browser.allowSystemProfileImport"
]
};
/** Node-host command descriptors exposed by the Browser plugin. */
function createBrowserProxyNodeHostCommand(command) {
return {
command,
cap: "browser",
isAvailable: ({ config }) => config.browser?.enabled !== false && config.nodeHost?.browserProxy?.enabled !== false,
handle: async (paramsJSON, _io, context) => {
const { runBrowserProxyCommand } = await loadBrowserRegistrationRuntimeModule();
return await runBrowserProxyCommand(paramsJSON, command, context?.signal);
},
...command === "browser.proxy.upload.v1" ? { watchAvailability: () => {
loadBrowserUploadCleanupRuntimeModule().then(({ ensureBrowserProxyUploadCleanup }) => ensureBrowserProxyUploadCleanup()).catch((error) => {
logger.warn(`browser proxy upload cleanup startup failed: ${String(error)}`);
});
} } : {}
};
}
const browserPluginNodeHostCommands = [createBrowserProxyNodeHostCommand(BROWSER_PROXY_COMMAND), createBrowserProxyNodeHostCommand(BROWSER_PROXY_UPLOAD_COMMAND)];
/** Security audit collectors contributed by the Browser plugin. */
const browserSecurityAuditCollectors = [async (ctx) => {
const { collectBrowserSecurityAuditFindings } = await loadBrowserRegistrationRuntimeModule();
return collectBrowserSecurityAuditFindings(ctx);
}];
function createLazyBrowserPluginService() {
let service = null;
const loadService = async () => {
if (!service) {
const { createBrowserPluginService, stopBrowserControlService } = await loadBrowserRegistrationRuntimeModule();
service = createBrowserPluginService({ stopOnDemand: stopBrowserControlService });
}
return service;
};
return {
id: "browser-control",
start: async (ctx) => {
if (!isTruthyEnvValue(process.env[EAGER_BROWSER_CONTROL_SERVICE_ENV])) return;
await (await loadService()).start(ctx);
},
stop: async (ctx) => {
if (!service) {
const loadedRuntime = loadBrowserRegistrationRuntimeModule.peek();
if (!loadedRuntime) return;
const { stopBrowserControlService } = await loadedRuntime;
await stopBrowserControlService();
return;
}
await service.stop?.(ctx);
}
};
}
/** Register Browser tool factories, CLI, gateway methods, services, and audits. */
function registerBrowserPlugin(api) {
initializeBrowserSessionTabStore(api.runtime);
configureSystemProfileImportStateStore(api.runtime.state.openKeyedStore({
namespace: "browser.system-profile-import",
maxEntries: 1
}));
api.registerTool(((ctx) => {
const config = ctx.getRuntimeConfig?.() ?? ctx.runtimeConfig ?? ctx.config;
return createLazyBrowserTool(createBrowserToolOptions(ctx), config);
}));
api.registerCli(async ({ program }) => {
const { registerBrowserCli } = await import("./browser-cli-CzTnNglY.js");
registerBrowserCli(program, process.argv, api.rootDir);
}, {
commands: ["browser"],
descriptors: [BROWSER_CLI_DESCRIPTOR]
});
api.registerGatewayMethod(BROWSER_REQUEST_GATEWAY_METHOD, async (opts) => {
const { handleBrowserGatewayRequest } = await loadBrowserRegistrationRuntimeModule();
return await handleBrowserGatewayRequest(opts);
}, { scope: BROWSER_REQUEST_GATEWAY_SCOPE });
api.registerHttpRoute({
path: "/browser/extension",
auth: "plugin",
match: "exact",
handler: (_req, res) => {
res.writeHead(426, { "Content-Type": "text/plain" });
res.end("Upgrade Required: connect the OpenClaw Chrome extension over WebSocket.");
},
handleUpgrade: async (req, socket, head) => {
await loadBrowserRegistrationRuntimeModule();
const { handleGatewayExtensionUpgrade } = await import("./gateway-relay-route-C92voZAt.js");
return await handleGatewayExtensionUpgrade(req, socket, head);
}
});
api.registerService(createLazyBrowserPluginService());
}
//#endregion
export { registerBrowserPlugin as i, browserPluginReload as n, browserSecurityAuditCollectors as r, browserPluginNodeHostCommands as t };