fix(cursor): allow enforcing images for cursor --image-responses=allow (#478)

Fixes https://github.com/microsoft/playwright-mcp/issues/449
This commit is contained in:
Pavel Feldman 2025-05-27 01:25:09 -07:00 committed by GitHub
parent 1051ea810a
commit 9e5ffd2ccf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 18 additions and 14 deletions

4
config.d.ts vendored
View File

@ -117,7 +117,7 @@ export type Config = {
}; };
/** /**
* Do not send image responses to the client. * Whether to send image responses to the client. Can be "allow", "omit", or "auto". Defaults to "auto", which sends images if the client can display them.
*/ */
noImageResponses?: boolean; imageResponses?: 'allow' | 'omit' | 'auto';
}; };

View File

@ -38,7 +38,7 @@ export type CLIOptions = {
host?: string; host?: string;
ignoreHttpsErrors?: boolean; ignoreHttpsErrors?: boolean;
isolated?: boolean; isolated?: boolean;
imageResponses: boolean; imageResponses?: 'allow' | 'omit' | 'auto';
sandbox: boolean; sandbox: boolean;
outputDir?: string; outputDir?: string;
port?: number; port?: number;
@ -188,13 +188,9 @@ export async function configFromCLIOptions(cliOptions: CLIOptions): Promise<Conf
}, },
saveTrace: cliOptions.saveTrace, saveTrace: cliOptions.saveTrace,
outputDir: cliOptions.outputDir, outputDir: cliOptions.outputDir,
imageResponses: cliOptions.imageResponses,
}; };
if (!cliOptions.imageResponses) {
// --no-image-responses was passed, disable image responses
result.noImageResponses = true;
}
return result; return result;
} }

View File

@ -92,8 +92,7 @@ export class Connection {
await new Promise<void>(resolve => { await new Promise<void>(resolve => {
this.server.oninitialized = () => resolve(); this.server.oninitialized = () => resolve();
}); });
if (this.server.getClientVersion()?.name.includes('cursor')) this.context.clientVersion = this.server.getClientVersion();
this.context.config.noImageResponses = true;
} }
async close() { async close() {

View File

@ -48,12 +48,21 @@ export class Context {
private _modalStates: (ModalState & { tab: Tab })[] = []; private _modalStates: (ModalState & { tab: Tab })[] = [];
private _pendingAction: PendingAction | undefined; private _pendingAction: PendingAction | undefined;
private _downloads: { download: playwright.Download, finished: boolean, outputFile: string }[] = []; private _downloads: { download: playwright.Download, finished: boolean, outputFile: string }[] = [];
clientVersion: { name: string; version: string; } | undefined;
constructor(tools: Tool[], config: FullConfig) { constructor(tools: Tool[], config: FullConfig) {
this.tools = tools; this.tools = tools;
this.config = config; this.config = config;
} }
clientSupportsImages(): boolean {
if (this.config.imageResponses === 'allow')
return true;
if (this.config.imageResponses === 'omit')
return false;
return !this.clientVersion?.name.includes('cursor');
}
modalStates(): ModalState[] { modalStates(): ModalState[] {
return this._modalStates; return this._modalStates;
} }

View File

@ -40,7 +40,7 @@ program
.option('--host <host>', 'host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.') .option('--host <host>', 'host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.')
.option('--ignore-https-errors', 'ignore https errors') .option('--ignore-https-errors', 'ignore https errors')
.option('--isolated', 'keep the browser profile in memory, do not save it to disk.') .option('--isolated', 'keep the browser profile in memory, do not save it to disk.')
.option('--no-image-responses', 'do not send image responses to the client.') .option('--image-responses <mode>', 'whether to send image responses to the client. Can be "allow", "omit", or "auto". Defaults to "auto", which sends images if the client can display them.')
.option('--no-sandbox', 'disable the sandbox for all process types that are normally sandboxed.') .option('--no-sandbox', 'disable the sandbox for all process types that are normally sandboxed.')
.option('--output-dir <path>', 'path to the directory for output files.') .option('--output-dir <path>', 'path to the directory for output files.')
.option('--port <port>', 'port to listen on for SSE transport.') .option('--port <port>', 'port to listen on for SSE transport.')

View File

@ -64,7 +64,7 @@ const screenshot = defineTool({
else else
code.push(`await page.screenshot(${javascript.formatObject(options)});`); code.push(`await page.screenshot(${javascript.formatObject(options)});`);
const includeBase64 = !context.config.noImageResponses; const includeBase64 = context.clientSupportsImages();
const action = async () => { const action = async () => {
const screenshot = locator ? await locator.screenshot(options) : await tab.page.screenshot(options); const screenshot = locator ? await locator.screenshot(options) : await tab.page.screenshot(options);
return { return {

View File

@ -172,12 +172,12 @@ test('browser_take_screenshot (filename: "output.jpeg")', async ({ startClient,
expect(files[0]).toMatch(/^output\.jpeg$/); expect(files[0]).toMatch(/^output\.jpeg$/);
}); });
test('browser_take_screenshot (noImageResponses)', async ({ startClient, server }, testInfo) => { test('browser_take_screenshot (imageResponses=omit)', async ({ startClient, server }, testInfo) => {
const outputDir = testInfo.outputPath('output'); const outputDir = testInfo.outputPath('output');
const client = await startClient({ const client = await startClient({
config: { config: {
outputDir, outputDir,
noImageResponses: true, imageResponses: 'omit',
}, },
}); });