Skip to content

Commit c3ea721

Browse files
committed
chore: implement chrome-devtools CLI
1 parent a453332 commit c3ea721

5 files changed

Lines changed: 968 additions & 2 deletions

File tree

src/bin/chrome-devtools.ts

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* @license
5+
* Copyright 2026 Google LLC
6+
* SPDX-License-Identifier: Apache-2.0
7+
*/
8+
9+
import process from 'node:process';
10+
11+
import yargs, {type Options, type PositionalOptions} from 'yargs';
12+
import {hideBin} from 'yargs/helpers';
13+
14+
import {startDaemon, stopDaemon, sendCommand} from '../daemon/client.js';
15+
import {isDaemonRunning} from '../daemon/utils.js';
16+
import {VERSION} from '../version.js';
17+
18+
import {commands} from './cliDefinitions.js';
19+
import {generateCustomHelp} from './customHelp.js';
20+
21+
const argv = hideBin(process.argv);
22+
23+
if (argv.length === 0 || argv[0] === '--custom-help') {
24+
console.log(generateCustomHelp(VERSION, commands));
25+
process.exit(0);
26+
}
27+
28+
const y = yargs(argv)
29+
.scriptName('chrome-devtools')
30+
.showHelpOnFail(true)
31+
.demandCommand()
32+
.version(VERSION)
33+
.strict()
34+
.help(true);
35+
36+
y.command(
37+
'start',
38+
'Start or restart chrome-devtools-mcp',
39+
y => y.help(false), // Disable help for start command to avoid parsing issues with passed args
40+
async () => {
41+
if (isDaemonRunning()) {
42+
await stopDaemon();
43+
}
44+
// Extract args after 'start'
45+
const startIndex = process.argv.indexOf('start');
46+
const args = startIndex !== -1 ? process.argv.slice(startIndex + 1) : [];
47+
await startDaemon([...args, '--via-cli']);
48+
},
49+
);
50+
51+
y.command('status', 'Checks if chrome-devtools-mcp is running', async () => {
52+
if (isDaemonRunning()) {
53+
console.log('chrome-devtools-mcp daemon is running.');
54+
} else {
55+
console.log('chrome-devtools-mcp daemon is not running');
56+
}
57+
});
58+
59+
y.command('stop', 'Stop chrome-devtools-mcp if any', async () => {
60+
await stopDaemon();
61+
});
62+
63+
for (const [commandName, commandDef] of Object.entries(commands)) {
64+
const args = commandDef.args;
65+
const requiredArgNames = Object.keys(args).filter(
66+
name => args[name].required,
67+
);
68+
69+
let commandStr = commandName;
70+
for (const arg of requiredArgNames) {
71+
commandStr += ` <${arg}>`;
72+
}
73+
74+
y.command(
75+
commandStr,
76+
commandDef.description,
77+
y => {
78+
for (const [argName, opt] of Object.entries(args)) {
79+
const type =
80+
opt.type === 'integer' || opt.type === 'number'
81+
? 'number'
82+
: opt.type === 'boolean'
83+
? 'boolean'
84+
: opt.type === 'array'
85+
? 'array'
86+
: 'string';
87+
88+
if (opt.required) {
89+
const options: PositionalOptions = {
90+
describe: opt.description,
91+
type: type as PositionalOptions['type'],
92+
};
93+
if (opt.default !== undefined) {
94+
options.default = opt.default;
95+
}
96+
if (opt.enum) {
97+
options.choices = opt.enum as Array<string | number>;
98+
}
99+
y.positional(argName, options);
100+
} else {
101+
const options: Options = {
102+
describe: opt.description,
103+
type: type as Options['type'],
104+
};
105+
if (opt.default !== undefined) {
106+
options.default = opt.default;
107+
}
108+
if (opt.enum) {
109+
options.choices = opt.enum as Array<string | number>;
110+
}
111+
y.option(argName, options);
112+
}
113+
}
114+
},
115+
async argv => {
116+
try {
117+
if (!isDaemonRunning()) {
118+
await startDaemon(['--via-cli']);
119+
}
120+
121+
const commandArgs: Record<string, unknown> = {};
122+
for (const argName of Object.keys(args)) {
123+
if (argName in argv) {
124+
commandArgs[argName] = argv[argName];
125+
}
126+
}
127+
128+
const response = await sendCommand({
129+
method: 'invoke_tool',
130+
tool: commandName,
131+
args: commandArgs,
132+
});
133+
134+
if (response.success) {
135+
console.log(response.result);
136+
} else {
137+
console.error('Error:', response.error);
138+
process.exit(1);
139+
}
140+
} catch (error) {
141+
console.error('Failed to execute command:', error);
142+
process.exit(1);
143+
}
144+
},
145+
);
146+
}
147+
148+
await y.parse();

0 commit comments

Comments
 (0)