豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit e3c59bc

Browse files
authored
feat: support --user-data-dir with --auto-connect (#654)
This PR integrates `--user-data-dir` with the `--auto-connect` feature. If `--user-data-dir` is provided as well `--auto-connect`, the specified dir is used to locate a running browser server.
1 parent 6ab6d85 commit e3c59bc

File tree

5 files changed

+89
-10
lines changed

5 files changed

+89
-10
lines changed

src/browser.ts

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@ import type {
1414
ChromeReleaseChannel,
1515
LaunchOptions,
1616
Target,
17+
BrowsersChromeReleaseChannel,
18+
} from './third_party/index.js';
19+
import {
20+
puppeteer,
21+
resolveDefaultUserDataDir,
22+
detectBrowserPlatform,
23+
BrowserEnum,
1724
} from './third_party/index.js';
18-
import {puppeteer} from './third_party/index.js';
1925

2026
let browser: Browser | undefined;
2127

@@ -49,6 +55,7 @@ export async function ensureBrowserConnected(options: {
4955
wsHeaders?: Record<string, string>;
5056
devtools: boolean;
5157
channel?: Channel;
58+
userDataDir?: string;
5259
}) {
5360
const {channel} = options;
5461
if (browser?.connected) {
@@ -68,15 +75,57 @@ export async function ensureBrowserConnected(options: {
6875
}
6976
} else if (options.browserURL) {
7077
connectOptions.browserURL = options.browserURL;
71-
} else if (channel) {
72-
const puppeteerChannel =
73-
channel !== 'stable'
74-
? (`chrome-${channel}` as ChromeReleaseChannel)
75-
: 'chrome';
76-
connectOptions.channel = puppeteerChannel;
78+
} else if (channel || options.userDataDir) {
79+
let userDataDir = options.userDataDir;
80+
if (!userDataDir) {
81+
if (!channel) {
82+
throw new Error('Channel must be provided if userDataDir is missing');
83+
}
84+
const platform = detectBrowserPlatform();
85+
if (!platform) {
86+
throw new Error('Could not detect required browser platform');
87+
}
88+
userDataDir = resolveDefaultUserDataDir(
89+
BrowserEnum.CHROME,
90+
platform,
91+
(channel === 'stable'
92+
? 'chrome'
93+
: `chrome-${channel}`) as BrowsersChromeReleaseChannel,
94+
);
95+
}
96+
97+
// TODO: re-expose this logic via Puppeteer.
98+
const portPath = path.join(userDataDir, 'DevToolsActivePort');
99+
try {
100+
const fileContent = await fs.promises.readFile(portPath, 'utf8');
101+
const [rawPort, rawPath] = fileContent
102+
.split('\n')
103+
.map(line => {
104+
return line.trim();
105+
})
106+
.filter(line => {
107+
return !!line;
108+
});
109+
if (!rawPort || !rawPath) {
110+
throw new Error(`Invalid DevToolsActivePort '${fileContent}' found`);
111+
}
112+
const port = parseInt(rawPort, 10);
113+
if (isNaN(port) || port <= 0 || port > 65535) {
114+
throw new Error(`Invalid port '${rawPort}' found`);
115+
}
116+
const browserWSEndpoint = `ws://127.0.0.1:${port}${rawPath}`;
117+
connectOptions.browserWSEndpoint = browserWSEndpoint;
118+
} catch (error) {
119+
throw new Error(
120+
`Could not connect to Chrome in ${userDataDir}. Check if Chrome is running and remote debugging is enabled.`,
121+
{
122+
cause: error,
123+
},
124+
);
125+
}
77126
} else {
78127
throw new Error(
79-
'Either browserURL, wsEndpoint or channel must be provided',
128+
'Either browserURL, wsEndpoint, channel or userDataDir must be provided',
80129
);
81130
}
82131

src/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const cliOptions = {
1212
type: 'boolean',
1313
description:
1414
'If specified, automatically connects to a browser (Chrome 145+) running in the user data directory identified by the channel param.',
15-
conflicts: ['isolated', 'executablePath', 'userDataDir'],
15+
conflicts: ['isolated', 'executablePath'],
1616
default: false,
1717
coerce: (value: boolean | undefined) => {
1818
if (!value) {

src/main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ async function getContext(): Promise<McpContext> {
6161
wsHeaders: args.wsHeaders,
6262
// Important: only pass channel, if autoConnect is true.
6363
channel: args.autoConnect ? (args.channel as Channel) : undefined,
64+
userDataDir: args.userDataDir,
6465
devtools,
6566
})
6667
: await ensureBrowserLaunched({

src/third_party/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,9 @@ export {
2929
export {default as puppeteer} from 'puppeteer-core';
3030
export type * from 'puppeteer-core';
3131
export type {CdpPage} from 'puppeteer-core/internal/cdp/Page.js';
32+
export {
33+
resolveDefaultUserDataDir,
34+
detectBrowserPlatform,
35+
Browser as BrowserEnum,
36+
type ChromeReleaseChannel as BrowsersChromeReleaseChannel,
37+
} from '@puppeteer/browsers';

tests/browser.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {describe, it} from 'node:test';
1111

1212
import {executablePath} from 'puppeteer';
1313

14-
import {launch} from '../src/browser.js';
14+
import {ensureBrowserConnected, launch} from '../src/browser.js';
1515

1616
describe('browser', () => {
1717
it('cannot launch multiple times with the same profile', async () => {
@@ -73,4 +73,27 @@ describe('browser', () => {
7373
await browser.close();
7474
}
7575
});
76+
it('connects to an existing browser with userDataDir', async () => {
77+
const tmpDir = os.tmpdir();
78+
const folderPath = path.join(tmpDir, `temp-folder-${crypto.randomUUID()}`);
79+
const browser = await launch({
80+
headless: true,
81+
isolated: false,
82+
userDataDir: folderPath,
83+
executablePath: executablePath(),
84+
devtools: false,
85+
args: ['--remote-debugging-port=0'],
86+
});
87+
try {
88+
const connectedBrowser = await ensureBrowserConnected({
89+
userDataDir: folderPath,
90+
devtools: false,
91+
});
92+
assert.ok(connectedBrowser);
93+
assert.ok(connectedBrowser.isConnected());
94+
connectedBrowser.disconnect();
95+
} finally {
96+
await browser.close();
97+
}
98+
});
7699
});

0 commit comments

Comments
 (0)