// 技术验证:Electron 内嵌 WebContentsView + 远程调试端口,供 browser-use 通过 CDP 接管。 // 运行:npx electron spike/embed/main.js const { app, BrowserWindow, WebContentsView, session, protocol, net } = require("electron"); const path = require("path"); const { pathToFileURL } = require("url"); // 界面页走自定义协议 app://ui/...:file:// 没有主机名,会被 browser-use 的安全看门狗当成非法标签页关掉 protocol.registerSchemesAsPrivileged([{ scheme: "app", privileges: { standard: true, secure: true, supportFetchAPI: true } }]); const CDP_PORT = process.env.SPIKE_CDP_PORT || "9333"; app.commandLine.appendSwitch("remote-debugging-port", CDP_PORT); app.commandLine.appendSwitch("remote-allow-origins", "*"); app.whenReady().then(() => { protocol.handle("app", (request) => { const url = new URL(request.url); const rel = url.pathname === "/" ? "index.html" : url.pathname.replace(/^\/+/, ""); return net.fetch(pathToFileURL(path.join(__dirname, rel)).toString()); }); const win = new BrowserWindow({ width: 1280, height: 820, title: "embed-spike", webPreferences: { contextIsolation: true, nodeIntegration: false }, }); win.loadURL("app://ui/index.html"); const part = session.fromPartition("persist:spike-view"); const view = new WebContentsView({ webPreferences: { session: part, contextIsolation: true, nodeIntegration: false, sandbox: true }, }); win.contentView.addChildView(view); const layout = () => { const [w, h] = win.getContentSize(); view.setBounds({ x: Math.round(w * 0.36), y: 56, width: Math.round(w * 0.64), height: h - 56 }); }; win.on("resize", layout); layout(); view.webContents.loadURL("about:blank"); win.on("close", () => console.log("[win] close requested")); win.on("closed", () => console.log("[win] closed")); view.webContents.on("destroyed", () => console.log("[view] destroyed")); win.webContents.on("did-navigate", (_e, url) => console.log("[ui] did-navigate", url)); view.webContents.on("did-navigate", (_e, url) => console.log("[view] did-navigate", url)); view.webContents.on("did-navigate-in-page", (_e, url) => console.log("[view] in-page", url)); view.webContents.setWindowOpenHandler(({ url }) => { console.log("[view] window.open blocked", url); return { action: "deny" }; }); app.on("browser-window-created", () => console.log("[app] browser-window-created; total", BrowserWindow.getAllWindows().length)); app.on("web-contents-created", (_e, wc) => console.log("[app] web-contents-created type", wc.getType())); setInterval(() => { console.log("[hb] windows", BrowserWindow.getAllWindows().length, "| view url", view.webContents.getURL(), "| title", view.webContents.getTitle()); }, 5000); console.log("[spike] ready; CDP http://127.0.0.1:" + CDP_PORT + "/json"); }); app.on("window-all-closed", () => app.quit());