mcpbrowser 0.3.28 → 0.3.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcpbrowser",
3
- "version": "0.3.28",
3
+ "version": "0.3.29",
4
4
  "mcpName": "io.github.cherchyk/mcpbrowser",
5
5
  "type": "module",
6
6
  "description": "MCP browser server - fetch web pages using real Chrome/Edge/Brave browser. Handles authentication, SSO, CAPTCHAs, and anti-bot protection. Browser automation for AI assistants.",
@@ -9,8 +9,9 @@ import puppeteer from 'puppeteer-core';
9
9
  import { existsSync } from "fs";
10
10
  import os from "os";
11
11
  import path from "path";
12
- import { spawn } from "child_process";
12
+ import { spawn, execSync } from "child_process";
13
13
  import logger from '../core/logger.js';
14
+ import { isWSL, wslToWindowsPath, windowsPathToWSL } from '../utils.js';
14
15
 
15
16
  /**
16
17
  * Base class for all Chromium-based browsers
@@ -49,6 +50,18 @@ export class ChromiumBrowser extends BaseBrowser {
49
50
  const platform = os.platform();
50
51
  const home = os.homedir();
51
52
 
53
+ // In WSL the browser is a Windows process, so the user-data-dir
54
+ // must reside on a Windows-accessible filesystem (e.g. /mnt/c/…).
55
+ if (isWSL()) {
56
+ const windowsLocalAppData = this._getWindowsLocalAppData();
57
+ if (windowsLocalAppData) {
58
+ // windowsLocalAppData is already a WSL path like /mnt/c/Users/.../AppData/Local
59
+ return path.join(windowsLocalAppData, `MCPBrowser/${this.config.userDataDirName}`);
60
+ }
61
+ // Fallback: predictable location on C: drive
62
+ return `/mnt/c/MCPBrowserData/${this.config.userDataDirName}`;
63
+ }
64
+
52
65
  if (platform === "win32") {
53
66
  return path.join(home, `AppData/Local/MCPBrowser/${this.config.userDataDirName}`);
54
67
  } else if (platform === "darwin") {
@@ -74,6 +87,37 @@ export class ChromiumBrowser extends BaseBrowser {
74
87
  }
75
88
  }
76
89
 
90
+ /**
91
+ * Resolve the Windows %LOCALAPPDATA% directory as a WSL path.
92
+ * Cached per-process. Returns null on failure.
93
+ * @returns {string|null} WSL-style path (e.g. /mnt/c/Users/.../AppData/Local)
94
+ * @private
95
+ */
96
+ _getWindowsLocalAppData() {
97
+ // Use a class-level cache so we only shell out once
98
+ if (ChromiumBrowser._wslLocalAppData !== undefined) {
99
+ return ChromiumBrowser._wslLocalAppData;
100
+ }
101
+
102
+ try {
103
+ const raw = execSync('cmd.exe /c echo %LOCALAPPDATA%', {
104
+ encoding: 'utf8',
105
+ timeout: 5000,
106
+ stdio: ['pipe', 'pipe', 'pipe'],
107
+ }).trim();
108
+
109
+ if (raw && !raw.includes('%LOCALAPPDATA%')) {
110
+ ChromiumBrowser._wslLocalAppData = windowsPathToWSL(raw);
111
+ return ChromiumBrowser._wslLocalAppData;
112
+ }
113
+ } catch {
114
+ // cmd.exe not available or timed out
115
+ }
116
+
117
+ ChromiumBrowser._wslLocalAppData = null;
118
+ return null;
119
+ }
120
+
77
121
  /**
78
122
  * Find the browser executable path
79
123
  * @returns {string|undefined}
@@ -109,14 +153,24 @@ export class ChromiumBrowser extends BaseBrowser {
109
153
  }
110
154
 
111
155
  const userDataDir = this.getDefaultUserDataDir();
156
+
157
+ // When running inside WSL the browser executable is a Windows process.
158
+ // It does not understand WSL paths (/mnt/c/…), so convert to a native
159
+ // Windows path for the --user-data-dir argument.
160
+ const userDataDirArg = isWSL() ? wslToWindowsPath(userDataDir) : userDataDir;
161
+
112
162
  const args = [
113
163
  `--remote-debugging-port=${this.config.port}`,
114
- `--user-data-dir=${userDataDir}`,
164
+ `--user-data-dir=${userDataDirArg}`,
115
165
  '--no-first-run',
116
166
  '--no-default-browser-check'
117
167
  ];
118
168
 
119
169
  logger.info(`Launching ${this.config.name} with remote debugging on port ${this.config.port}...`);
170
+ if (isWSL()) {
171
+ logger.info(`WSL detected – launching Windows browser at ${execPath}`);
172
+ logger.info(` user-data-dir (Windows): ${userDataDirArg}`);
173
+ }
120
174
 
121
175
  const child = spawn(execPath, args, {
122
176
  detached: true,
@@ -181,3 +235,6 @@ export class ChromiumBrowser extends BaseBrowser {
181
235
  this.browser = null;
182
236
  }
183
237
  }
238
+
239
+ // Static cache for WSL %LOCALAPPDATA% lookup (undefined = not yet resolved)
240
+ ChromiumBrowser._wslLocalAppData = undefined;
@@ -5,9 +5,11 @@
5
5
 
6
6
  import { ChromiumBrowser } from './ChromiumBrowser.js';
7
7
  import os from "os";
8
+ import { isWSL } from '../utils.js';
8
9
 
9
10
  /**
10
11
  * Get platform-specific default paths where Brave browser is typically installed.
12
+ * When running under WSL, Windows-side paths (via /mnt/c/) are also included.
11
13
  * @returns {string[]} Array of possible Brave executable paths for the current platform
12
14
  */
13
15
  function getDefaultBravePaths() {
@@ -24,13 +26,21 @@ function getDefaultBravePaths() {
24
26
  "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
25
27
  ];
26
28
  } else {
27
- return [
29
+ const paths = [
28
30
  "/usr/bin/brave",
29
31
  "/usr/bin/brave-browser",
30
32
  "/usr/bin/brave-browser-stable",
31
33
  "/opt/brave.com/brave/brave-browser",
32
34
  "/snap/bin/brave",
33
35
  ];
36
+ // In WSL, also look for Windows-side Brave via /mnt/c/
37
+ if (isWSL()) {
38
+ paths.push(
39
+ "/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe",
40
+ "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe",
41
+ );
42
+ }
43
+ return paths;
34
44
  }
35
45
  }
36
46
 
@@ -5,9 +5,11 @@
5
5
 
6
6
  import { ChromiumBrowser } from './ChromiumBrowser.js';
7
7
  import os from "os";
8
+ import { isWSL } from '../utils.js';
8
9
 
9
10
  /**
10
11
  * Get platform-specific default paths where Chrome is typically installed.
12
+ * When running under WSL, Windows-side paths (via /mnt/c/) are also included.
11
13
  * @returns {string[]} Array of possible Chrome executable paths for the current platform
12
14
  */
13
15
  function getDefaultChromePaths() {
@@ -24,11 +26,19 @@ function getDefaultChromePaths() {
24
26
  "/Applications/Chromium.app/Contents/MacOS/Chromium",
25
27
  ];
26
28
  } else {
27
- return [
29
+ const paths = [
28
30
  "/usr/bin/google-chrome",
29
31
  "/usr/bin/chromium-browser",
30
32
  "/usr/bin/chromium",
31
33
  ];
34
+ // In WSL, also look for Windows-side Chrome via /mnt/c/
35
+ if (isWSL()) {
36
+ paths.push(
37
+ "/mnt/c/Program Files/Google/Chrome/Application/chrome.exe",
38
+ "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
39
+ );
40
+ }
41
+ return paths;
32
42
  }
33
43
  }
34
44
 
@@ -5,9 +5,11 @@
5
5
 
6
6
  import { ChromiumBrowser } from './ChromiumBrowser.js';
7
7
  import os from "os";
8
+ import { isWSL } from '../utils.js';
8
9
 
9
10
  /**
10
11
  * Get platform-specific default paths where Edge browser is typically installed.
12
+ * When running under WSL, Windows-side paths (via /mnt/c/) are also included.
11
13
  * @returns {string[]} Array of possible Edge executable paths for the current platform
12
14
  */
13
15
  function getDefaultEdgePaths() {
@@ -23,13 +25,21 @@ function getDefaultEdgePaths() {
23
25
  "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
24
26
  ];
25
27
  } else {
26
- return [
28
+ const paths = [
27
29
  "/usr/bin/microsoft-edge",
28
30
  "/usr/bin/microsoft-edge-stable",
29
31
  "/usr/bin/microsoft-edge-beta",
30
32
  "/usr/bin/microsoft-edge-dev",
31
33
  "/opt/microsoft/msedge/msedge",
32
34
  ];
35
+ // In WSL, also look for Windows-side Edge via /mnt/c/
36
+ if (isWSL()) {
37
+ paths.push(
38
+ "/mnt/c/Program Files/Microsoft/Edge/Application/msedge.exe",
39
+ "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe",
40
+ );
41
+ }
42
+ return paths;
33
43
  }
34
44
  }
35
45
 
package/src/utils.js CHANGED
@@ -2,6 +2,65 @@
2
2
  * Utility functions for MCPBrowser
3
3
  */
4
4
 
5
+ import { readFileSync } from 'fs';
6
+ import os from 'os';
7
+
8
+ // --- WSL Detection & Path Helpers ---
9
+
10
+ let _isWSL = null;
11
+
12
+ /**
13
+ * Detect if running inside Windows Subsystem for Linux (WSL).
14
+ * Result is cached after first call.
15
+ * @returns {boolean}
16
+ */
17
+ export function isWSL() {
18
+ if (_isWSL === null) {
19
+ if (os.platform() !== 'linux') {
20
+ _isWSL = false;
21
+ } else {
22
+ try {
23
+ const version = readFileSync('/proc/version', 'utf8');
24
+ _isWSL = /microsoft|wsl/i.test(version);
25
+ } catch {
26
+ _isWSL = false;
27
+ }
28
+ }
29
+ }
30
+ return _isWSL;
31
+ }
32
+
33
+ /**
34
+ * Convert a WSL mount path to a Windows path.
35
+ * e.g. /mnt/c/Program Files/Google → C:\Program Files\Google
36
+ * Non-WSL paths are returned unchanged.
37
+ * @param {string} wslPath
38
+ * @returns {string}
39
+ */
40
+ export function wslToWindowsPath(wslPath) {
41
+ const match = wslPath.match(/^\/mnt\/([a-zA-Z])\/(.*)/);
42
+ if (match) {
43
+ const drive = match[1].toUpperCase();
44
+ const rest = match[2].replace(/\//g, '\\');
45
+ return `${drive}:\\${rest}`;
46
+ }
47
+ return wslPath;
48
+ }
49
+
50
+ /**
51
+ * Convert a Windows path to a WSL mount path.
52
+ * e.g. C:\Users\foo → /mnt/c/Users/foo
53
+ * @param {string} windowsPath
54
+ * @returns {string}
55
+ */
56
+ export function windowsPathToWSL(windowsPath) {
57
+ return windowsPath
58
+ .replace(/\\/g, '/')
59
+ .replace(/^([A-Za-z]):/, (_, d) => `/mnt/${d.toLowerCase()}`);
60
+ }
61
+
62
+ // --- General Utilities ---
63
+
5
64
  /**
6
65
  * Truncate a string to a maximum length, adding "... [truncated]" if truncated.
7
66
  * @param {string} str - The string to truncate