feat: 新增配置中心支持环境变量与 .env 文件配置
This commit is contained in:
25
.env
Normal file
25
.env
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# ── Gemini Skill 环境变量 / Environment Variables ──
|
||||||
|
|
||||||
|
# Chrome / Chromium 可执行文件路径(不设则需手动启动 Chrome)
|
||||||
|
# Path to Chrome/Chromium executable (if not set, you need to start Chrome manually)
|
||||||
|
CHROME_PATH=
|
||||||
|
|
||||||
|
# CDP 远程调试端口
|
||||||
|
# CDP remote debugging port
|
||||||
|
CHROME_DEBUG_PORT=
|
||||||
|
|
||||||
|
# Chrome 用户数据目录(保持登录态、cookies 等)
|
||||||
|
# Chrome user data directory (persists login session, cookies, etc.)
|
||||||
|
CHROME_USER_DATA_DIR=
|
||||||
|
|
||||||
|
# 是否无头模式(true / false)
|
||||||
|
# Headless mode (true / false)
|
||||||
|
CHROME_HEADLESS=
|
||||||
|
|
||||||
|
# CDP 协议超时时间(毫秒)
|
||||||
|
# CDP protocol timeout (milliseconds)
|
||||||
|
CHROME_PROTOCOL_TIMEOUT=
|
||||||
|
|
||||||
|
# 截图 / 图片输出目录
|
||||||
|
# Screenshot / image output directory
|
||||||
|
OUTPUT_DIR=
|
||||||
@@ -14,9 +14,8 @@
|
|||||||
import puppeteerCore from 'puppeteer-core';
|
import puppeteerCore from 'puppeteer-core';
|
||||||
import { addExtra } from 'puppeteer-extra';
|
import { addExtra } from 'puppeteer-extra';
|
||||||
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
|
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
|
||||||
import { homedir } from 'node:os';
|
|
||||||
import { join } from 'node:path';
|
|
||||||
import { createConnection } from 'node:net';
|
import { createConnection } from 'node:net';
|
||||||
|
import config from './config.js';
|
||||||
|
|
||||||
// ── 用 puppeteer-extra 包装 puppeteer-core,注入 stealth 插件 ──
|
// ── 用 puppeteer-extra 包装 puppeteer-core,注入 stealth 插件 ──
|
||||||
const puppeteer = addExtra(puppeteerCore);
|
const puppeteer = addExtra(puppeteerCore);
|
||||||
@@ -25,14 +24,6 @@ puppeteer.use(StealthPlugin());
|
|||||||
// ── 模块级单例:跨调用复用同一个浏览器 ──
|
// ── 模块级单例:跨调用复用同一个浏览器 ──
|
||||||
let _browser = null;
|
let _browser = null;
|
||||||
|
|
||||||
/** 默认配置 */
|
|
||||||
const DEFAULTS = {
|
|
||||||
port: 9222,
|
|
||||||
userDataDir: join(homedir(), '.gemini-skill', 'chrome-data'),
|
|
||||||
headless: false,
|
|
||||||
protocolTimeout: 60_000,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 探测指定端口是否有 Chrome 在监听
|
* 探测指定端口是否有 Chrome 在监听
|
||||||
* @param {number} port
|
* @param {number} port
|
||||||
@@ -99,7 +90,7 @@ async function connectToChrome(port) {
|
|||||||
const browser = await puppeteer.connect({
|
const browser = await puppeteer.connect({
|
||||||
browserURL,
|
browserURL,
|
||||||
defaultViewport: null,
|
defaultViewport: null,
|
||||||
protocolTimeout: DEFAULTS.protocolTimeout,
|
protocolTimeout: config.chromeProtocolTimeout,
|
||||||
});
|
});
|
||||||
console.log('[browser] connected to existing Chrome on port', port);
|
console.log('[browser] connected to existing Chrome on port', port);
|
||||||
return browser;
|
return browser;
|
||||||
@@ -125,7 +116,7 @@ async function launchChrome({ executablePath, port, userDataDir, headless }) {
|
|||||||
`--remote-debugging-port=${port}`,
|
`--remote-debugging-port=${port}`,
|
||||||
],
|
],
|
||||||
ignoreDefaultArgs: ['--enable-automation'],
|
ignoreDefaultArgs: ['--enable-automation'],
|
||||||
protocolTimeout: DEFAULTS.protocolTimeout,
|
protocolTimeout: config.chromeProtocolTimeout,
|
||||||
});
|
});
|
||||||
console.log('[browser] launched Chrome, pid:', browser.process()?.pid, 'port:', port, 'dataDir:', userDataDir);
|
console.log('[browser] launched Chrome, pid:', browser.process()?.pid, 'port:', port, 'dataDir:', userDataDir);
|
||||||
return browser;
|
return browser;
|
||||||
@@ -176,10 +167,10 @@ async function findOrCreateGeminiPage(browser) {
|
|||||||
*/
|
*/
|
||||||
export async function ensureBrowser(opts = {}) {
|
export async function ensureBrowser(opts = {}) {
|
||||||
const {
|
const {
|
||||||
executablePath,
|
executablePath = config.chromePath,
|
||||||
port = DEFAULTS.port,
|
port = config.chromeDebugPort,
|
||||||
userDataDir = DEFAULTS.userDataDir,
|
userDataDir = config.chromeUserDataDir,
|
||||||
headless = DEFAULTS.headless,
|
headless = config.chromeHeadless,
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
// 1. 复用已有连接
|
// 1. 复用已有连接
|
||||||
|
|||||||
63
src/config.js
Normal file
63
src/config.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* config.js — 统一配置中心
|
||||||
|
*
|
||||||
|
* 所有可配置项集中在这里,从环境变量读取,提供合理默认值。
|
||||||
|
* 其他模块一律从 config 取值,不自己硬编码。
|
||||||
|
*
|
||||||
|
* 环境变量来源(优先级从高到低):
|
||||||
|
* 1. 进程环境变量(process.env)
|
||||||
|
* 2. .env 文件(需调用方自行加载,如 dotenv)
|
||||||
|
* 3. 代码默认值
|
||||||
|
*/
|
||||||
|
import { homedir } from 'node:os';
|
||||||
|
import { join, resolve } from 'node:path';
|
||||||
|
|
||||||
|
const env = process.env;
|
||||||
|
|
||||||
|
/** 辅助:读取布尔型环境变量 */
|
||||||
|
function envBool(key, fallback) {
|
||||||
|
const val = env[key];
|
||||||
|
if (val === undefined || val === '') return fallback;
|
||||||
|
return val === 'true' || val === '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 辅助:读取数字型环境变量 */
|
||||||
|
function envInt(key, fallback) {
|
||||||
|
const val = env[key];
|
||||||
|
if (val === undefined || val === '') return fallback;
|
||||||
|
const n = parseInt(val, 10);
|
||||||
|
return Number.isNaN(n) ? fallback : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 辅助:读取字符串环境变量 */
|
||||||
|
function envStr(key, fallback) {
|
||||||
|
const val = env[key];
|
||||||
|
return (val !== undefined && val !== '') ? val : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 导出配置 ──
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
/** Chrome / Chromium 可执行文件路径(不设则需手动启动 Chrome) */
|
||||||
|
chromePath: envStr('CHROME_PATH', undefined),
|
||||||
|
|
||||||
|
/** CDP 远程调试端口 */
|
||||||
|
chromeDebugPort: envInt('CHROME_DEBUG_PORT', 9222),
|
||||||
|
|
||||||
|
/** Chrome 用户数据目录 */
|
||||||
|
chromeUserDataDir: envStr(
|
||||||
|
'CHROME_USER_DATA_DIR',
|
||||||
|
join(homedir(), '.gemini-skill', 'chrome-data'),
|
||||||
|
),
|
||||||
|
|
||||||
|
/** 是否无头模式 */
|
||||||
|
chromeHeadless: envBool('CHROME_HEADLESS', false),
|
||||||
|
|
||||||
|
/** CDP 协议超时时间(ms) */
|
||||||
|
chromeProtocolTimeout: envInt('CHROME_PROTOCOL_TIMEOUT', 60_000),
|
||||||
|
|
||||||
|
/** 截图 / 图片输出目录 */
|
||||||
|
outputDir: envStr('OUTPUT_DIR', resolve('output')),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
10
src/demo.js
10
src/demo.js
@@ -7,18 +7,18 @@
|
|||||||
* chrome --remote-debugging-port=9222 --user-data-dir="~/.gemini-skill/chrome-data"
|
* chrome --remote-debugging-port=9222 --user-data-dir="~/.gemini-skill/chrome-data"
|
||||||
* node src/demo.js
|
* node src/demo.js
|
||||||
*
|
*
|
||||||
* 方式 2:让 skill 自动启动 Chrome
|
* 方式 2:通过环境变量让 skill 自动启动 Chrome
|
||||||
* CHROME_PATH="C:/Program Files/Google/Chrome/Application/chrome.exe" node src/demo.js
|
* CHROME_PATH="C:/Program Files/Google/Chrome/Application/chrome.exe" node src/demo.js
|
||||||
|
*
|
||||||
|
* 所有配置项见 .env,可直接编辑或通过命令行设环境变量。
|
||||||
*/
|
*/
|
||||||
import { createGeminiSession, disconnect } from './index.js';
|
import { createGeminiSession, disconnect } from './index.js';
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
console.log('=== Gemini Skill Demo ===\n');
|
console.log('=== Gemini Skill Demo ===\n');
|
||||||
|
|
||||||
// 创建会话(自动 connect 或 launch)
|
// 创建会话(配置自动从环境变量读取,也可以传 opts 覆盖)
|
||||||
const { ops } = await createGeminiSession({
|
const { ops } = await createGeminiSession();
|
||||||
executablePath: process.env.CHROME_PATH || undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 探测页面状态
|
// 1. 探测页面状态
|
||||||
|
|||||||
10
src/index.js
10
src/index.js
@@ -23,11 +23,13 @@ export { disconnect, close };
|
|||||||
* 2. 无 Chrome + 提供了 executablePath → 自动 launch
|
* 2. 无 Chrome + 提供了 executablePath → 自动 launch
|
||||||
* 3. 无 Chrome + 无 executablePath → 报错并提示手动启动
|
* 3. 无 Chrome + 无 executablePath → 报错并提示手动启动
|
||||||
*
|
*
|
||||||
|
* 所有参数均可通过环境变量配置(见 .env),opts 传参优先级更高。
|
||||||
|
*
|
||||||
* @param {object} [opts]
|
* @param {object} [opts]
|
||||||
* @param {string} [opts.executablePath] - Chrome 路径(可选,仅自动启动时需要)
|
* @param {string} [opts.executablePath] - Chrome 路径(env: CHROME_PATH)
|
||||||
* @param {number} [opts.port=9222] - 调试端口
|
* @param {number} [opts.port] - 调试端口(env: CHROME_DEBUG_PORT,默认 9222)
|
||||||
* @param {string} [opts.userDataDir] - 用户数据目录(默认 ~/.gemini-skill/chrome-data)
|
* @param {string} [opts.userDataDir] - 用户数据目录(env: CHROME_USER_DATA_DIR)
|
||||||
* @param {boolean} [opts.headless=false]
|
* @param {boolean} [opts.headless] - 无头模式(env: CHROME_HEADLESS,默认 false)
|
||||||
* @returns {Promise<{ops: ReturnType<typeof createOps>, page: import('puppeteer-core').Page, browser: import('puppeteer-core').Browser}>}
|
* @returns {Promise<{ops: ReturnType<typeof createOps>, page: import('puppeteer-core').Page, browser: import('puppeteer-core').Browser}>}
|
||||||
*/
|
*/
|
||||||
export async function createGeminiSession(opts = {}) {
|
export async function createGeminiSession(opts = {}) {
|
||||||
|
|||||||
Reference in New Issue
Block a user