chore: mirror cli options w/ env vars (#685)

Fixes https://github.com/microsoft/playwright-mcp/issues/639
This commit is contained in:
Pavel Feldman 2025-07-17 10:19:18 -07:00 committed by GitHub
parent 9526910864
commit fe0c0ffffe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 72 additions and 12 deletions

View File

@ -28,7 +28,7 @@ export type CLIOptions = {
blockedOrigins?: string[];
blockServiceWorkers?: boolean;
browser?: string;
caps?: string;
caps?: string[];
cdpEndpoint?: string;
config?: string;
device?: string;
@ -38,7 +38,7 @@ export type CLIOptions = {
ignoreHttpsErrors?: boolean;
isolated?: boolean;
imageResponses?: 'allow' | 'omit';
sandbox: boolean;
sandbox?: boolean;
outputDir?: string;
port?: number;
proxyBypass?: string;
@ -89,15 +89,19 @@ export async function resolveConfig(config: Config): Promise<FullConfig> {
export async function resolveCLIConfig(cliOptions: CLIOptions): Promise<FullConfig> {
const configInFile = await loadConfig(cliOptions.config);
const cliOverrides = await configFromCLIOptions(cliOptions);
const result = mergeConfig(mergeConfig(defaultConfig, configInFile), cliOverrides);
const envOverrides = configFromEnv();
const cliOverrides = configFromCLIOptions(cliOptions);
let result = defaultConfig;
result = mergeConfig(result, configInFile);
result = mergeConfig(result, envOverrides);
result = mergeConfig(result, cliOverrides);
// Derive artifact output directory from config.outputDir
if (result.saveTrace)
result.browser.launchOptions.tracesDir = path.join(result.outputDir, 'traces');
return result;
}
export async function configFromCLIOptions(cliOptions: CLIOptions): Promise<Config> {
export function configFromCLIOptions(cliOptions: CLIOptions): Config {
let browserName: 'chromium' | 'firefox' | 'webkit' | undefined;
let channel: string | undefined;
switch (cliOptions.browser) {
@ -181,7 +185,7 @@ export async function configFromCLIOptions(cliOptions: CLIOptions): Promise<Conf
port: cliOptions.port,
host: cliOptions.host,
},
capabilities: cliOptions.caps?.split(',').map((c: string) => c.trim() as ToolCapability),
capabilities: cliOptions.caps as ToolCapability[],
network: {
allowedOrigins: cliOptions.allowedOrigins,
blockedOrigins: cliOptions.blockedOrigins,
@ -194,6 +198,36 @@ export async function configFromCLIOptions(cliOptions: CLIOptions): Promise<Conf
return result;
}
function configFromEnv(): Config {
const options: CLIOptions = {};
options.allowedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_ALLOWED_ORIGINS);
options.blockedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_BLOCKED_ORIGINS);
options.blockServiceWorkers = envToBoolean(process.env.PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS);
options.browser = envToString(process.env.PLAYWRIGHT_MCP_BROWSER);
options.caps = commaSeparatedList(process.env.PLAYWRIGHT_MCP_CAPS);
options.cdpEndpoint = envToString(process.env.PLAYWRIGHT_MCP_CDP_ENDPOINT);
options.config = envToString(process.env.PLAYWRIGHT_MCP_CONFIG);
options.device = envToString(process.env.PLAYWRIGHT_MCP_DEVICE);
options.executablePath = envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH);
options.headless = envToBoolean(process.env.PLAYWRIGHT_MCP_HEADLESS);
options.host = envToString(process.env.PLAYWRIGHT_MCP_HOST);
options.ignoreHttpsErrors = envToBoolean(process.env.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);
options.isolated = envToBoolean(process.env.PLAYWRIGHT_MCP_ISOLATED);
if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES === 'omit')
options.imageResponses = 'omit';
options.sandbox = envToBoolean(process.env.PLAYWRIGHT_MCP_SANDBOX);
options.outputDir = envToString(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR);
options.port = envToNumber(process.env.PLAYWRIGHT_MCP_PORT);
options.proxyBypass = envToString(process.env.PLAYWRIGHT_MCP_PROXY_BYPASS);
options.proxyServer = envToString(process.env.PLAYWRIGHT_MCP_PROXY_SERVER);
options.saveTrace = envToBoolean(process.env.PLAYWRIGHT_MCP_SAVE_TRACE);
options.storageState = envToString(process.env.PLAYWRIGHT_MCP_STORAGE_STATE);
options.userAgent = envToString(process.env.PLAYWRIGHT_MCP_USER_AGENT);
options.userDataDir = envToString(process.env.PLAYWRIGHT_MCP_USER_DATA_DIR);
options.viewportSize = envToString(process.env.PLAYWRIGHT_MCP_VIEWPORT_SIZE);
return configFromCLIOptions(options);
}
async function loadConfig(configFile: string | undefined): Promise<Config> {
if (!configFile)
return {};
@ -251,3 +285,33 @@ function mergeConfig(base: FullConfig, overrides: Config): FullConfig {
},
} as FullConfig;
}
export function semicolonSeparatedList(value: string | undefined): string[] | undefined {
if (!value)
return undefined;
return value.split(';').map(v => v.trim());
}
export function commaSeparatedList(value: string | undefined): string[] | undefined {
if (!value)
return undefined;
return value.split(',').map(v => v.trim());
}
function envToNumber(value: string | undefined): number | undefined {
if (!value)
return undefined;
return +value;
}
function envToBoolean(value: string | undefined): boolean | undefined {
if (value === 'true' || value === '1')
return true;
if (value === 'false' || value === '0')
return false;
return undefined;
}
function envToString(value: string | undefined): string | undefined {
return value ? value.trim() : undefined;
}

View File

@ -19,7 +19,7 @@ import { program, Option } from 'commander';
import { startTraceViewerServer } from 'playwright-core/lib/server';
import { startHttpServer, startHttpTransport, startStdioTransport } from './transport.js';
import { resolveCLIConfig } from './config.js';
import { commaSeparatedList, resolveCLIConfig, semicolonSeparatedList } from './config.js';
import { Server } from './server.js';
import { packageJSON } from './package.js';
@ -30,7 +30,7 @@ program
.option('--blocked-origins <origins>', 'semicolon-separated list of origins to block the browser from requesting. Blocklist is evaluated before allowlist. If used without the allowlist, requests not matching the blocklist are still allowed.', semicolonSeparatedList)
.option('--block-service-workers', 'block service workers')
.option('--browser <browser>', 'browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.')
.option('--caps <caps>', 'comma-separated list of additional capabilities to enable, possible values: vision, pdf.')
.option('--caps <caps>', 'comma-separated list of additional capabilities to enable, possible values: vision, pdf.', commaSeparatedList)
.option('--cdp-endpoint <endpoint>', 'CDP endpoint to connect to.')
.option('--config <path>', 'path to the configuration file.')
.option('--device <device>', 'device to emulate, for example: "iPhone 15"')
@ -77,8 +77,4 @@ program
}
});
function semicolonSeparatedList(value: string): string[] {
return value.split(';').map(v => v.trim());
}
void program.parseAsync(process.argv);