feat: 新增 debugOpts 支持配置 Puppeteer 信号处理行为,优化 demo 中 Ctrl+C 处理逻辑以保持浏览器可复用

This commit is contained in:
WJZ_P
2026-03-16 01:50:06 +08:00
parent aa1fc46875
commit 9b6fa6f829
3 changed files with 40 additions and 4 deletions

View File

@@ -286,9 +286,19 @@ async function connectBrowser(port) {
* @param {number} opts.port
* @param {string} opts.userDataDir
* @param {boolean} opts.headless
* @param {object} [opts.debugOpts] - 调试/信号控制选项
* @param {boolean} [opts.debugOpts.handleSIGINT=true] - Puppeteer 是否在 SIGINT 时自动关闭浏览器
* @param {boolean} [opts.debugOpts.handleSIGTERM=true] - Puppeteer 是否在 SIGTERM 时自动关闭浏览器
* @param {boolean} [opts.debugOpts.handleSIGHUP=true] - Puppeteer 是否在 SIGHUP 时自动关闭浏览器
* @returns {Promise<import('puppeteer-core').Browser>}
*/
async function launchBrowser({ executablePath, port, userDataDir, headless }) {
async function launchBrowser({ executablePath, port, userDataDir, headless, debugOpts = {} }) {
const {
handleSIGINT = true,
handleSIGTERM = true,
handleSIGHUP = true,
} = debugOpts;
const browser = await puppeteer.launch({
executablePath,
headless,
@@ -300,6 +310,9 @@ async function launchBrowser({ executablePath, port, userDataDir, headless }) {
],
ignoreDefaultArgs: ['--enable-automation'],
protocolTimeout: config.browserProtocolTimeout,
handleSIGINT,
handleSIGTERM,
handleSIGHUP,
});
console.log('[browser] launched, pid:', browser.process()?.pid, 'port:', port, 'path:', executablePath);
return browser;
@@ -349,6 +362,7 @@ async function findOrCreateGeminiPage(browser) {
* @param {number} [opts.port] - 调试端口env: BROWSER_DEBUG_PORT默认 9222
* @param {string} [opts.userDataDir] - 用户数据目录env: BROWSER_USER_DATA_DIR不传则多级兜底
* @param {boolean} [opts.headless] - 无头模式env: BROWSER_HEADLESS默认 false
* @param {object} [opts.debugOpts] - 调试/信号控制选项(透传给 Puppeteer launch
* @returns {Promise<{browser: import('puppeteer-core').Browser, page: import('puppeteer-core').Page}>}
*/
export async function ensureBrowser(opts = {}) {
@@ -357,6 +371,7 @@ export async function ensureBrowser(opts = {}) {
port = config.browserDebugPort,
userDataDir = resolveUserDataDir(),
headless = config.browserHeadless,
debugOpts,
} = opts;
// 1. 复用已有连接
@@ -393,7 +408,7 @@ export async function ensureBrowser(opts = {}) {
}
try {
_browser = await launchBrowser({ executablePath: resolvedPath, port, userDataDir, headless });
_browser = await launchBrowser({ executablePath: resolvedPath, port, userDataDir, headless, debugOpts });
} catch (err) {
// 大概率是用户数据目录被正在运行的浏览器锁住了
if (err.message?.includes('EPERM') || err.message?.includes('lock') || err.message?.includes('already')) {

View File

@@ -50,8 +50,18 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
* 创建会话,如果因浏览器目录被锁而失败,自动杀掉全部浏览器进程后重试一次
*/
async function createSessionWithRetry() {
// 禁止 Puppeteer 在 Ctrl+C 等信号时自动杀浏览器进程,
// 由 demo 自己处理 SIGINT → disconnect浏览器保持运行可复用。
const opts = {
debugOpts: {
handleSIGINT: false,
handleSIGTERM: false,
handleSIGHUP: false,
},
};
try {
return await createGeminiSession();
return await createGeminiSession(opts);
} catch (err) {
const msg = err.message || '';
const isLocked = msg.includes('EPERM') || msg.includes('lock') || msg.includes('already');
@@ -67,7 +77,7 @@ async function createSessionWithRetry() {
await sleep(2000);
// 重试一次,还失败就直接抛出
return await createGeminiSession();
return await createGeminiSession(opts);
}
}
@@ -77,6 +87,13 @@ async function main() {
// 创建会话:自带杀进程重试逻辑
const { ops } = await createSessionWithRetry();
// ── Ctrl+C 时只断开连接,不杀浏览器进程(下次可复用) ──
process.on('SIGINT', () => {
console.log('\n[demo] Ctrl+C 收到,断开浏览器连接(浏览器保持运行)...');
disconnect();
process.exit(0);
});
try {
// 1. 探测页面状态
console.log('[1] 探测页面元素...');

View File

@@ -30,6 +30,10 @@ export { disconnect, close };
* @param {number} [opts.port] - 调试端口env: BROWSER_DEBUG_PORT默认 9222
* @param {string} [opts.userDataDir] - 用户数据目录env: BROWSER_USER_DATA_DIR
* @param {boolean} [opts.headless] - 无头模式env: BROWSER_HEADLESS默认 false
* @param {object} [opts.debugOpts] - 调试/信号控制选项(透传给 Puppeteer launch
* @param {boolean} [opts.debugOpts.handleSIGINT=true] - Puppeteer 是否在 SIGINT 时自动关闭浏览器
* @param {boolean} [opts.debugOpts.handleSIGTERM=true] - Puppeteer 是否在 SIGTERM 时自动关闭浏览器
* @param {boolean} [opts.debugOpts.handleSIGHUP=true] - Puppeteer 是否在 SIGHUP 时自动关闭浏览器
* @returns {Promise<{ops: ReturnType<typeof createOps>, page: import('puppeteer-core').Page, browser: import('puppeteer-core').Browser}>}
*/
export async function createGeminiSession(opts = {}) {