Merge remote-tracking branch 'upstream/main'

# Conflicts:
#	README.md
#	cli/src/connection.rs
#	cli/src/flags.rs
#	cli/src/main.rs
#	src/browser.ts
#	src/protocol.ts
This commit is contained in:
leeguooooo
2026-02-25 10:06:20 +09:00
22 changed files with 1467 additions and 59 deletions
+39 -1
View File
@@ -16,7 +16,7 @@ import {
} from 'playwright-core';
import path from 'node:path';
import os from 'node:os';
import { existsSync, mkdirSync, rmSync, readFileSync } from 'node:fs';
import { existsSync, mkdirSync, rmSync, readFileSync, statSync } from 'node:fs';
import { writeFile, mkdir } from 'node:fs/promises';
import type { LaunchCommand, TraceEvent } from './types.js';
import { type RefMap, type EnhancedSnapshot, getEnhancedSnapshot, parseRef } from './snapshot.js';
@@ -160,6 +160,7 @@ export class BrowserManager {
private contextTimezoneId: string | undefined = undefined;
private contextHeaders: Record<string, string> | undefined = undefined;
private contextUserAgent: string | undefined = undefined;
private downloadPath: string | null = null;
/**
* Set the persistent color scheme preference.
@@ -1567,6 +1568,17 @@ export class BrowserManager {
}
this.logStealthPolicy('launch policy', options.browser ?? 'chromium');
if (options.downloadPath) {
this.downloadPath = options.downloadPath;
}
if (this.downloadPath && (cdpEndpoint || options.autoConnect)) {
const warning =
"--download-path is ignored when connecting via CDP or auto-connect (downloads use the remote browser's configuration)";
this.launchWarnings.push(warning);
console.error(`[WARN] ${warning}`);
}
if (cdpEndpoint) {
await this.connectViaCDP(cdpEndpoint);
return;
@@ -1578,6 +1590,13 @@ export class BrowserManager {
}
// Cloud browser providers require explicit opt-in via -p flag or AGENT_BROWSER_PROVIDER env var
// -p flag takes precedence over AGENT_BROWSER_PROVIDER.
if (this.downloadPath && provider) {
const warning =
"--download-path is ignored when using a cloud provider (downloads use the remote browser's configuration)";
this.launchWarnings.push(warning);
console.error(`[WARN] ${warning}`);
}
if (provider === 'browserbase') {
await this.connectToBrowserbase();
return;
@@ -1593,6 +1612,23 @@ export class BrowserManager {
return;
}
if (this.downloadPath) {
const resolved = path.resolve(this.downloadPath);
const stat = statSync(resolved, { throwIfNoEntry: false });
if (stat && !stat.isDirectory()) {
throw new Error(`Download path is not a directory: ${resolved}`);
}
if (!stat) {
try {
mkdirSync(resolved, { recursive: true });
} catch (e: unknown) {
const msg = e instanceof Error ? e.message : String(e);
throw new Error(`Cannot create download directory '${resolved}': ${msg}`);
}
}
this.downloadPath = resolved;
}
const browserType = options.browser ?? 'chromium';
if (hasExtensions && browserType !== 'chromium') {
throw new Error('Extensions are only supported in Chromium');
@@ -1682,6 +1718,7 @@ export class BrowserManager {
...(options.proxy && { proxy: options.proxy }),
ignoreHTTPSErrors: options.ignoreHTTPSErrors ?? false,
...(this.colorScheme && { colorScheme: this.colorScheme }),
...(this.downloadPath && { downloadsPath: this.downloadPath }),
}
);
this.isPersistentContext = true;
@@ -1692,6 +1729,7 @@ export class BrowserManager {
executablePath: options.executablePath,
...(chromeChannel && { channel: chromeChannel }),
args: baseArgs,
...(this.downloadPath && { downloadsPath: this.downloadPath }),
});
this.cdpEndpoint = null;