const { PLUGIN_REQUIRED_MESSAGE, isHyprlandSession } = require("./cua-hyprland.cjs"); const DESKTOP_PLATFORMS = new Set(["darwin", "linux", "win32"]); const UNAVAILABLE_LOCAL_STATUSES = new Set(["disabled", "checking", "starting", "error", "stopped", "unavailable"]); function normalizedPlatform(platform) { return DESKTOP_PLATFORMS.has(platform) ? platform : "other"; } function nativeDesktopActions(platform) { const appleNative = normalizedPlatform(platform) === "darwin"; return Object.freeze({ appleMediaPermissions: appleNative, applePrivacySettings: appleNative, appleSpeech: appleNative, }); } function linuxSession(platform, env) { if (platform !== "linux") return "unknown"; const declared = String(env.XDG_SESSION_TYPE ?? "").toLowerCase(); // A Wayland session may also expose DISPLAY for XWayland: prefer Wayland so the UI never bypasses portals. if (declared === "wayland" || env.WAYLAND_DISPLAY) return "wayland"; if (declared === "x11" || declared === "xorg") return "x11"; if (env.DISPLAY) return "x11"; return "headless"; } /** `hyprlandPlugin` is what `probeCuaHyprlandPlugin` found: "active" or anything else. */ function linuxLocalControlSupport(platform, env, { hyprlandPlugin } = {}) { if (platform !== "linux") { return Object.freeze({ available: false, session: "unknown", reasonCode: "unsupported-platform", message: "Local control is not available on this platform.", }); } const session = linuxSession(platform, env); if (session === "x11") return Object.freeze({ available: true, session }); // Hyprland with Cua's plugin is the one Wayland desktop allowed: the plugin gives the bot seats of its own inside the // compositor. Every other Wayland desktop keeps the seat-safety block. if (session === "wayland" && isHyprlandSession(env)) { if (hyprlandPlugin === "active") return Object.freeze({ available: true, session, compositor: "hyprland" }); return Object.freeze({ available: false, session, compositor: "hyprland", reasonCode: "linux-hyprland-plugin-required", message: PLUGIN_REQUIRED_MESSAGE, }); } if (session === "wayland") { return Object.freeze({ available: false, session, reasonCode: "linux-wayland-seat-safety-blocked", message: "Local control is not available on Wayland yet. Sign out and choose Ubuntu on Xorg to use This computer.", }); } return Object.freeze({ available: false, session, reasonCode: "headless-session", message: "Local control needs an active Ubuntu Xorg desktop session.", }); } /** Computer control's helper is gone, or the system refused to run it: access denied (os error 5), or Windows Security * found it unsafe (os error 225, ERROR_VIRUS_INFECTED). */ function driverBlocked(reason) { return /binary not found|is missing|ENOENT|EPERM|EACCES|os error (?:2|5|225)\b|access is denied|virus/i.test(String(reason ?? "")); } function localComputerReady(platform, connection) { const validLegacyConnection = connection && (!Object.hasOwn(connection, "status") || connection.status === "ready") && typeof connection.socketPath === "string" && connection.socketPath.length > 0 && typeof connection.mcpCommand === "string" && connection.mcpCommand.trim().length > 0 && Array.isArray(connection.mcpArgs) && connection.mcpArgs[0] === "mcp" && connection.mcpArgs.every((arg) => typeof arg === "string") && connection.mcpEnv && typeof connection.mcpEnv === "object" && !Array.isArray(connection.mcpEnv) && Object.values(connection.mcpEnv).every((value) => typeof value === "string"); if (platform === "darwin") { return Boolean(validLegacyConnection && (connection.mode === "embedded" || connection.mode === "standalone")); } if (platform === "win32") { return Boolean(validLegacyConnection && connection.mode === "embedded"); } if ( platform !== "linux" || connection?.schemaVersion !== 1 || connection?.platform !== "linux" || connection?.enabled !== true || connection?.status !== "ready" ) { return false; } if (connection.mode === "linux-x11-supervised") { return connection.session === "x11"; } if (connection.mode === "linux-wayland-hyprland-supervised") { return connection.session === "wayland" && connection.compositor === "hyprland"; } return false; } function desktopCapabilities({ platform = process.platform, env = process.env, packaged = false, localConnection = null, homeDir = require("node:os").homedir(), // Whether the page draws the window's frame now. frame = true, // A remote server's page: this computer's screen, voice and local control are never on offer. remote = false, } = {}) { const hostPlatform = normalizedPlatform(platform); const isMac = hostPlatform === "darwin"; const hostSession = linuxSession(hostPlatform, env); const linuxPreview = hostPlatform === "linux" && hostSession !== "headless"; const localAvailable = localComputerReady(hostPlatform, localConnection); const localStatus = localAvailable ? "ready" : UNAVAILABLE_LOCAL_STATUSES.has(localConnection?.status) ? localConnection.status : "unavailable"; const screenPreview = { available: isMac || linuxPreview, interaction: isMac || hostSession === "x11" ? "direct" : hostSession === "wayland" ? "portal-picker" : "none", }; if (!(isMac || linuxPreview)) { screenPreview.reasonCode = hostPlatform === "linux" ? "headless-session" : "unsupported-platform"; } // Off the Mac the page listens itself and Fish Audio turns each clip into text (src/lib/fish-speech.ts). const fishListening = hostPlatform === "win32" || hostPlatform === "linux"; const dictation = { available: isMac || fishListening, engine: isMac ? "apple-speech" : fishListening ? "fish-audio" : "none", onDevice: isMac, }; if (!dictation.available) dictation.reasonCode = "unsupported-platform"; const localComputer = { available: localAvailable, support: localAvailable && hostPlatform === "linux" ? "limited" : localAvailable ? "supported" : "unsupported", enabled: connectionEnabled(hostPlatform, localConnection), status: localStatus, }; // Local-machine detail (driver, seat, diagnostics): a remote page must never receive these. if (!remote) { if (localConnection?.mode === "unavailable" && typeof localConnection.reason === "string" && localConnection.reason.trim() && localConnection.reason.length <= 2_000) { localComputer.message = localConnection.reason.trim(); } if (typeof localConnection?.message === "string") { localComputer.message = localConnection.message; } if (typeof localConnection?.driver?.path === "string") { localComputer.driverPath = localConnection.driver.path; } if (typeof localConnection?.driver?.version === "string") { localComputer.driverVersion = localConnection.driver.version; } if (typeof localConnection?.driver?.source === "string") { localComputer.driverSource = localConnection.driver.source; } if (typeof localConnection?.session === "string") { localComputer.session = localConnection.session; } if (typeof localConnection?.compositor === "string") { localComputer.compositor = localConnection.compositor; } } if (!localAvailable) { localComputer.reasonCode = localConnection?.reasonCode ?? (hostPlatform === "darwin" || hostPlatform === "win32" ? driverBlocked(localConnection?.reason) ? "cua-driver-blocked" : "cua-driver-unavailable" : "unsupported-platform"); } if (remote) { const unavailable = { reasonCode: "remote-server" }; Object.assign(screenPreview, { available: false, interaction: "none" }, unavailable); Object.assign(dictation, { available: false, engine: "none", onDevice: false }, unavailable); Object.assign(localComputer, { available: false, support: "unsupported", enabled: false, status: "unavailable", driverPath: "", driverVersion: "", driverSource: "", session: "", compositor: "", message: "", }, unavailable); } return { remote: Boolean(remote), host: { platform: hostPlatform, label: hostPlatform === "darwin" ? "macOS" : hostPlatform === "linux" ? "Linux" : hostPlatform === "win32" ? "Windows" : "Desktop", session: hostSession, packaged: Boolean(packaged), // The sandboxed preload has no Node builtins; a remote page learns nothing about this computer's users. homeDir: remote ? "" : homeDir, }, windowChrome: isMac ? "mac-inset" : hostPlatform === "win32" && frame ? "win-caption" : "native", windowFrame: frame !== false, screenPreview, dictation, localComputer, }; } function connectionEnabled(platform, connection) { if (platform === "darwin" || platform === "win32") { return localComputerReady(platform, connection); } return platform === "linux" && connection?.enabled === true; } module.exports = { connectionEnabled, desktopCapabilities, driverBlocked, linuxLocalControlSupport, linuxSession, localComputerReady, nativeDesktopActions, };