豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit 2216b3a

Browse files
authored
chore: implement chrome-devtools CLI (#1079)
Note that the implementation for response handling and daemon start/stop is still a placeholder. The proper handling will be done in follow-ups. The `generateCustomHelp` function is also fully experimental and it is likely to get removed before the release. The goal of this PR is to scaffold the e2e workflow for early manual testing. Automated tests will be also added in a follow-up. Note this does not expose the CLI as a binary on the package.
1 parent e5973fd commit 2216b3a

File tree

5 files changed

+1010
-2
lines changed

5 files changed

+1010
-2
lines changed

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)