fix(daemon): preserve hyphenated browser flags in arg serialization and lazy daemon startup (#2405)
When executing CLI subcommands with custom Chromium startup options (such as `--use-fake-device-for-media-stream` or `--use-file-for-fake-audio-capture=...`), two independent bugs previously prevented flags from reaching `puppeteer.launch`: 1. Yargs Value Collision: `serializeArgs` emitted options and values as detached token pairs (`['--chrome-arg', '--use-fake-device...']`). When initializing the spawned daemon process, `yargs` interpreted the hyphenated trailing token as an unrelated command option, stripping the value from `--chrome-arg`. 2. Lazy Startup Parameter Dropping: When invoking action subcommands against an inactive session, auto-start initialization called `start([], sessionId)`. This hardcoded empty array silently discarded all browser configuration flags passed on the invocation command line. Fix: - Enforce strict `--key=value` syntax in `serializeArgs`, compelling `yargs` in the child daemon process to parse hyphenated payloads strictly as string values. - Forward `serializeArgs(cliOptions, argv)` during lazy subcommand auto-start so implicit daemon instantiations inherit all requested browser boot capabilities. Includes comprehensive unit test coverage in `tests/daemon/utils.test.ts` validating `--key=value` formatting across scalar and array option serialization. Fixes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/2406 --------- Co-authored-by: Alex Rudenko <alexrudenko@chromium.org>
This commit is contained in:
@@ -263,7 +263,7 @@ for (const [commandName, commandDef] of Object.entries(commands)) {
|
||||
const sessionId = argv.sessionId as string;
|
||||
try {
|
||||
if (!isDaemonRunning(sessionId)) {
|
||||
await start([], sessionId);
|
||||
await start(serializeArgs(cliOptions, argv), sessionId);
|
||||
}
|
||||
|
||||
const commandArgs: Record<string, unknown> = {};
|
||||
|
||||
+2
-2
@@ -128,10 +128,10 @@ export function serializeArgs(
|
||||
}
|
||||
} else if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
args.push(`--${kebabKey}`, String(item));
|
||||
args.push(`--${kebabKey}=${String(item)}`);
|
||||
}
|
||||
} else {
|
||||
args.push(`--${kebabKey}`, String(value));
|
||||
args.push(`--${kebabKey}=${String(value)}`);
|
||||
}
|
||||
}
|
||||
return args;
|
||||
|
||||
@@ -26,7 +26,7 @@ describe('serializeArgs', () => {
|
||||
$0: 'test',
|
||||
} as unknown as ParsedArguments;
|
||||
const result = serializeArgs(options, argv);
|
||||
assert.deepStrictEqual(result, ['--baz', 'value']);
|
||||
assert.deepStrictEqual(result, ['--baz=value']);
|
||||
});
|
||||
|
||||
it('should handle boolean values', () => {
|
||||
@@ -41,15 +41,24 @@ describe('serializeArgs', () => {
|
||||
assert.deepStrictEqual(result, ['--foo', '--no-bar']);
|
||||
});
|
||||
|
||||
it('should handle array values', () => {
|
||||
const options: Record<string, YargsOptions> = {foo: {}};
|
||||
it('should handle array values including hyphenated flags', () => {
|
||||
const options: Record<string, YargsOptions> = {foo: {}, chromeArg: {}};
|
||||
const argv = {
|
||||
foo: ['val1', 'val2'],
|
||||
chromeArg: [
|
||||
'--use-fake-device-for-media-stream',
|
||||
'--use-file-for-fake-audio-capture=/tmp/test.wav',
|
||||
],
|
||||
_: [],
|
||||
$0: 'test',
|
||||
} as unknown as ParsedArguments;
|
||||
const result = serializeArgs(options, argv);
|
||||
assert.deepStrictEqual(result, ['--foo', 'val1', '--foo', 'val2']);
|
||||
assert.deepStrictEqual(result, [
|
||||
'--foo=val1',
|
||||
'--foo=val2',
|
||||
'--chrome-arg=--use-fake-device-for-media-stream',
|
||||
'--chrome-arg=--use-file-for-fake-audio-capture=/tmp/test.wav',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle primitive values', () => {
|
||||
@@ -61,7 +70,7 @@ describe('serializeArgs', () => {
|
||||
$0: 'test',
|
||||
} as unknown as ParsedArguments;
|
||||
const result = serializeArgs(options, argv);
|
||||
assert.deepStrictEqual(result, ['--foo', 'string', '--bar', '42']);
|
||||
assert.deepStrictEqual(result, ['--foo=string', '--bar=42']);
|
||||
});
|
||||
|
||||
it('should convert camelCase keys to kebab-case', () => {
|
||||
@@ -77,8 +86,7 @@ describe('serializeArgs', () => {
|
||||
} as unknown as ParsedArguments;
|
||||
const result = serializeArgs(options, argv);
|
||||
assert.deepStrictEqual(result, [
|
||||
'--camel-case-key',
|
||||
'value1',
|
||||
'--camel-case-key=value1',
|
||||
'--another-key',
|
||||
]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user