Skip to content

WSL2: Control Windows browsers (Edge, Chrome) from OpenClaw #16649

@Fr4nzz

Description

@Fr4nzz

WSL2: Control Windows browsers (Edge, Chrome) from OpenClaw

Enable OpenClaw in WSL2 to auto-spawn and control Windows browsers (Edge, Chrome, Vivaldi) via CDP.

Proposed Config

{
  "browser": {
    "wsl2": {
      "enabled": true,
      "autoStart": true,
      "browsers": [
        "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe",
        "/mnt/c/Program Files/Google/Chrome/Application/chrome.exe"
      ],
      "cdpPort": 9223,
      "userDataDir": "C:\\Users\\{username}\\AppData\\Local\\OpenClaw\\browser-profile"
    }
  }
}

Implementation

1. WSL2 Detection (src/browser/wsl2.ts - new file)

import { execSync } from 'child_process';
import { existsSync } from 'fs';
import { spawn } from 'child_process';

export function isWSL2(): boolean {
  try {
    const release = execSync('uname -r', { encoding: 'utf-8' });
    return release.toLowerCase().includes('microsoft');
  } catch {
    return false;
  }
}

const BROWSER_PATHS = [
  '/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe',
  '/mnt/c/Program Files/Microsoft/Edge/Application/msedge.exe',
  '/mnt/c/Program Files/Google/Chrome/Application/chrome.exe',
  '/mnt/c/Program Files/Vivaldi/Application/vivaldi.exe',
];

export function findWindowsBrowser(): string | null {
  return BROWSER_PATHS.find(path => existsSync(path)) || null;
}

export function spawnWindowsBrowser(
  browserPath: string,
  cdpPort: number,
  userDataDir: string
): Promise<void> {
  return new Promise((resolve, reject) => {
    const args = [
      `--remote-debugging-port=${cdpPort}`,
      `--user-data-dir=${userDataDir}`,
      '--no-first-run',
      '--remote-allow-origins=*'
    ];

    const proc = spawn(browserPath, args, { detached: true, stdio: 'ignore' });
    proc.on('error', reject);
    proc.unref();
    
    // Wait for CDP
    const checkReady = (attempts = 0) => {
      fetch(`http://localhost:${cdpPort}/json/version`)
        .then(() => resolve())
        .catch(() => attempts < 10 
          ? setTimeout(() => checkReady(attempts + 1), 500)
          : reject(new Error('CDP timeout'))
        );
    };
    setTimeout(() => checkReady(), 500);
  });
}

2. Browser Service Integration (src/browser/service.ts - modify)

import { isWSL2, findWindowsBrowser, spawnWindowsBrowser } from './wsl2.js';

async function ensureBrowserRunning(profile: BrowserProfile) {
  const config = loadConfig();
  
  if (config.browser?.wsl2?.enabled && isWSL2()) {
    const isReachable = await isCdpReachable(profile.cdpUrl);
    
    if (!isReachable && config.browser.wsl2.autoStart) {
      const browserPath = config.browser.wsl2.browsers?.[0] || findWindowsBrowser();
      if (!browserPath) throw new Error('No Windows browser found');
      
      await spawnWindowsBrowser(
        browserPath,
        config.browser.wsl2.cdpPort || 9223,
        config.browser.wsl2.userDataDir || 
          `C:\\Users\\${process.env.USER}\\AppData\\Local\\OpenClaw\\browser-profile`
      );
    }
  }
}

3. Config Schema (src/browser/config.ts - modify)

interface BrowserConfig {
  // ... existing fields
  wsl2?: {
    enabled: boolean;
    autoStart?: boolean;
    browsers?: string[];
    cdpPort?: number;
    userDataDir?: string;
  };
}

Files Changed

File Action
src/browser/wsl2.ts Create - WSL2 detection + browser spawn
src/browser/config.ts Modify - Add wsl2 config schema
src/browser/service.ts Modify - Add ensureBrowserRunning() call
docs/platforms/windows.md Update - Document WSL2 setup

Testing

# WSL2 environment
openclaw browser open "https://example.com"
# → Should auto-spawn Edge/Chrome and open URL

openclaw browser snapshot --mode role
# → Should return accessibility tree

Tested: OpenClaw v2026.2.13, WSL2 Ubuntu 22.04 + Windows 11, Edge 144.x

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions