@mcp-use/cli 3.2.0-canary.6 → 3.2.0-canary.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/screenshot.d.ts +15 -0
- package/dist/commands/screenshot.d.ts.map +1 -1
- package/dist/index.cjs +144 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +144 -39
- package/dist/index.js.map +1 -1
- package/dist/utils/cdp-screenshot.d.ts +21 -10
- package/dist/utils/cdp-screenshot.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -13,7 +13,16 @@ interface ScreenshotOptions {
|
|
|
13
13
|
delay?: string;
|
|
14
14
|
quiet?: boolean;
|
|
15
15
|
timeout: string;
|
|
16
|
+
cdpUrl?: string;
|
|
17
|
+
header?: string[];
|
|
16
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Curl-style `Key: Value` parser. Splits on the first `:` so values may
|
|
21
|
+
* contain colons, and trims both sides so `Authorization:Bearer xyz` and
|
|
22
|
+
* `Authorization: Bearer xyz` are equivalent.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseHeaderArg(raw: string): [string, string];
|
|
25
|
+
export declare function parseHeaderArgs(args: string[]): Record<string, string>;
|
|
17
26
|
/**
|
|
18
27
|
* Inspect a tool's `_meta` for the UI resource URI it renders, if any. Falls back
|
|
19
28
|
* to the OpenAI Apps `openai/outputTemplate` key for cross-ecosystem compatibility.
|
|
@@ -38,6 +47,12 @@ export interface CaptureToolScreenshotOptions {
|
|
|
38
47
|
timeoutMs?: number;
|
|
39
48
|
inspector?: string;
|
|
40
49
|
quiet?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Pre-existing CDP WebSocket URL. When set, the screenshot is captured via
|
|
52
|
+
* the remote browser instead of spawning a local Chrome. The inspector URL
|
|
53
|
+
* must be reachable from that remote browser.
|
|
54
|
+
*/
|
|
55
|
+
cdpUrl?: string;
|
|
41
56
|
}
|
|
42
57
|
export interface CaptureToolScreenshotResult {
|
|
43
58
|
outputPath: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/commands/screenshot.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAkBjD,UAAU,iBAAiB;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/commands/screenshot.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAkBjD,UAAU,iBAAiB;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAa5D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOtE;AAaD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,SAAS,GAAG,IAAI,GAC3D,MAAM,GAAG,IAAI,CAUf;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,UAAU,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,2BAA2B,EACnC,OAAO,GAAE,4BAAiC,GACzC,OAAO,CAAC,2BAA2B,CAAC,CA0DtC;AAqLD;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,OAAa,GAAG,MAAM,CAKzD;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMhE;AA2CD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,GAC7B,OAAO,CAAC,IAAI,CAAC,CA0If;AAED,wBAAgB,uBAAuB,IAAI,OAAO,CA4DjD"}
|
package/dist/index.cjs
CHANGED
|
@@ -2958,52 +2958,67 @@ function waitForDevToolsUrl(child, timeoutMs = 5e3) {
|
|
|
2958
2958
|
});
|
|
2959
2959
|
}
|
|
2960
2960
|
async function captureScreenshot(opts) {
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
"--headless=new",
|
|
2964
|
-
"--remote-debugging-port=0",
|
|
2965
|
-
`--user-data-dir=${userDataDir}`,
|
|
2966
|
-
"--no-first-run",
|
|
2967
|
-
"--no-default-browser-check",
|
|
2968
|
-
"--disable-extensions",
|
|
2969
|
-
"--disable-gpu",
|
|
2970
|
-
"--hide-scrollbars",
|
|
2971
|
-
"--mute-audio",
|
|
2972
|
-
`--window-size=${opts.width},${opts.height}`,
|
|
2973
|
-
"about:blank"
|
|
2974
|
-
];
|
|
2975
|
-
const child = (0, import_node_child_process8.spawn)(opts.chromePath, chromeArgs, {
|
|
2976
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
2977
|
-
});
|
|
2978
|
-
child.stdout?.resume();
|
|
2961
|
+
let userDataDir;
|
|
2962
|
+
let child;
|
|
2979
2963
|
let cdp;
|
|
2980
2964
|
let cleanedUp = false;
|
|
2981
2965
|
const cleanup = () => {
|
|
2982
2966
|
if (cleanedUp) return;
|
|
2983
2967
|
cleanedUp = true;
|
|
2984
2968
|
cdp?.close();
|
|
2985
|
-
if (!child.killed) {
|
|
2969
|
+
if (child && !child.killed) {
|
|
2986
2970
|
try {
|
|
2987
2971
|
child.kill("SIGTERM");
|
|
2988
2972
|
} catch {
|
|
2989
2973
|
}
|
|
2974
|
+
const localChild = child;
|
|
2990
2975
|
const killTimer = setTimeout(() => {
|
|
2991
|
-
if (!
|
|
2976
|
+
if (!localChild.killed) {
|
|
2992
2977
|
try {
|
|
2993
|
-
|
|
2978
|
+
localChild.kill("SIGKILL");
|
|
2994
2979
|
} catch {
|
|
2995
2980
|
}
|
|
2996
2981
|
}
|
|
2997
2982
|
}, 2e3);
|
|
2998
2983
|
killTimer.unref();
|
|
2999
2984
|
}
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
2985
|
+
if (userDataDir) {
|
|
2986
|
+
try {
|
|
2987
|
+
(0, import_node_fs6.rmSync)(userDataDir, { recursive: true, force: true });
|
|
2988
|
+
} catch {
|
|
2989
|
+
}
|
|
3003
2990
|
}
|
|
3004
2991
|
};
|
|
3005
2992
|
try {
|
|
3006
|
-
|
|
2993
|
+
let wsUrl;
|
|
2994
|
+
if (opts.cdpUrl) {
|
|
2995
|
+
wsUrl = opts.cdpUrl;
|
|
2996
|
+
} else {
|
|
2997
|
+
if (!opts.chromePath) {
|
|
2998
|
+
throw new Error(
|
|
2999
|
+
"captureScreenshot requires either `cdpUrl` or `chromePath`"
|
|
3000
|
+
);
|
|
3001
|
+
}
|
|
3002
|
+
userDataDir = (0, import_node_fs6.mkdtempSync)(import_node_path4.default.join(import_node_os5.default.tmpdir(), "mcp-use-chrome-"));
|
|
3003
|
+
const chromeArgs = [
|
|
3004
|
+
"--headless=new",
|
|
3005
|
+
"--remote-debugging-port=0",
|
|
3006
|
+
`--user-data-dir=${userDataDir}`,
|
|
3007
|
+
"--no-first-run",
|
|
3008
|
+
"--no-default-browser-check",
|
|
3009
|
+
"--disable-extensions",
|
|
3010
|
+
"--disable-gpu",
|
|
3011
|
+
"--hide-scrollbars",
|
|
3012
|
+
"--mute-audio",
|
|
3013
|
+
`--window-size=${opts.width},${opts.height}`,
|
|
3014
|
+
"about:blank"
|
|
3015
|
+
];
|
|
3016
|
+
child = (0, import_node_child_process8.spawn)(opts.chromePath, chromeArgs, {
|
|
3017
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3018
|
+
});
|
|
3019
|
+
child.stdout?.resume();
|
|
3020
|
+
wsUrl = await waitForDevToolsUrl(child);
|
|
3021
|
+
}
|
|
3007
3022
|
const ws = new import_ws.default(wsUrl);
|
|
3008
3023
|
await new Promise((resolve2, reject) => {
|
|
3009
3024
|
const onOpen = () => {
|
|
@@ -3018,14 +3033,47 @@ async function captureScreenshot(opts) {
|
|
|
3018
3033
|
ws.once("error", onError);
|
|
3019
3034
|
});
|
|
3020
3035
|
cdp = new CdpClient(ws);
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3036
|
+
let sessionId;
|
|
3037
|
+
if (opts.cdpUrl) {
|
|
3038
|
+
const attachPromise = new Promise((resolve2, reject) => {
|
|
3039
|
+
const timer = setTimeout(
|
|
3040
|
+
() => reject(
|
|
3041
|
+
new Error(
|
|
3042
|
+
"Timed out waiting for Target.attachedToTarget event from remote CDP"
|
|
3043
|
+
)
|
|
3044
|
+
),
|
|
3045
|
+
1e4
|
|
3046
|
+
);
|
|
3047
|
+
const onMessage = (data) => {
|
|
3048
|
+
try {
|
|
3049
|
+
const msg = JSON.parse(data.toString());
|
|
3050
|
+
if (msg.method === "Target.attachedToTarget" && msg.params?.targetInfo?.type === "page" && typeof msg.params.sessionId === "string") {
|
|
3051
|
+
clearTimeout(timer);
|
|
3052
|
+
ws.off("message", onMessage);
|
|
3053
|
+
resolve2(msg.params.sessionId);
|
|
3054
|
+
}
|
|
3055
|
+
} catch {
|
|
3056
|
+
}
|
|
3057
|
+
};
|
|
3058
|
+
ws.on("message", onMessage);
|
|
3059
|
+
});
|
|
3060
|
+
await cdp.send("Target.setAutoAttach", {
|
|
3061
|
+
autoAttach: true,
|
|
3062
|
+
waitForDebuggerOnStart: false,
|
|
3063
|
+
flatten: true
|
|
3064
|
+
});
|
|
3065
|
+
sessionId = await attachPromise;
|
|
3066
|
+
} else {
|
|
3067
|
+
const { targetId } = await cdp.send(
|
|
3068
|
+
"Target.createTarget",
|
|
3069
|
+
{ url: "about:blank" }
|
|
3070
|
+
);
|
|
3071
|
+
const attach = await cdp.send(
|
|
3072
|
+
"Target.attachToTarget",
|
|
3073
|
+
{ targetId, flatten: true }
|
|
3074
|
+
);
|
|
3075
|
+
sessionId = attach.sessionId;
|
|
3076
|
+
}
|
|
3029
3077
|
await cdp.send("Page.enable", {}, sessionId);
|
|
3030
3078
|
await cdp.send(
|
|
3031
3079
|
"Emulation.setDeviceMetricsOverride",
|
|
@@ -3192,6 +3240,31 @@ function resolveChromePath() {
|
|
|
3192
3240
|
}
|
|
3193
3241
|
|
|
3194
3242
|
// src/commands/screenshot.ts
|
|
3243
|
+
function parseHeaderArg(raw) {
|
|
3244
|
+
const idx = raw.indexOf(":");
|
|
3245
|
+
if (idx === -1) {
|
|
3246
|
+
throw new Error(
|
|
3247
|
+
`Invalid --header value "${raw}". Expected "Key: Value" (e.g. "Authorization: Bearer xyz").`
|
|
3248
|
+
);
|
|
3249
|
+
}
|
|
3250
|
+
const key = raw.slice(0, idx).trim();
|
|
3251
|
+
const value = raw.slice(idx + 1).trim();
|
|
3252
|
+
if (!key) {
|
|
3253
|
+
throw new Error(`Invalid --header value "${raw}". Header name is empty.`);
|
|
3254
|
+
}
|
|
3255
|
+
return [key, value];
|
|
3256
|
+
}
|
|
3257
|
+
function parseHeaderArgs(args) {
|
|
3258
|
+
const headers = {};
|
|
3259
|
+
for (const raw of args) {
|
|
3260
|
+
const [key, value] = parseHeaderArg(raw);
|
|
3261
|
+
headers[key] = value;
|
|
3262
|
+
}
|
|
3263
|
+
return headers;
|
|
3264
|
+
}
|
|
3265
|
+
function collectHeader(value, previous = []) {
|
|
3266
|
+
return previous.concat([value]);
|
|
3267
|
+
}
|
|
3195
3268
|
function detectToolResourceUri(tool) {
|
|
3196
3269
|
if (!tool) return null;
|
|
3197
3270
|
const meta = tool._meta;
|
|
@@ -3205,7 +3278,7 @@ async function captureToolScreenshot(inputs, options = {}) {
|
|
|
3205
3278
|
const theme = options.theme ?? "light";
|
|
3206
3279
|
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
3207
3280
|
const delayMs = options.delayMs ?? 0;
|
|
3208
|
-
const chromePath = resolveChromePath();
|
|
3281
|
+
const chromePath = options.cdpUrl ? void 0 : resolveChromePath();
|
|
3209
3282
|
const view = extractViewName(inputs.resourceUri);
|
|
3210
3283
|
const devOptions = {
|
|
3211
3284
|
width: String(width),
|
|
@@ -3241,6 +3314,7 @@ async function captureToolScreenshot(inputs, options = {}) {
|
|
|
3241
3314
|
timeoutMs,
|
|
3242
3315
|
outputPath,
|
|
3243
3316
|
chromePath,
|
|
3317
|
+
cdpUrl: options.cdpUrl,
|
|
3244
3318
|
delayMs: Number.isFinite(delayMs) && delayMs > 0 ? delayMs : 0,
|
|
3245
3319
|
bundle
|
|
3246
3320
|
});
|
|
@@ -3386,7 +3460,7 @@ function parseDimension(raw, name) {
|
|
|
3386
3460
|
return n;
|
|
3387
3461
|
}
|
|
3388
3462
|
var AD_HOC_SESSION_NAME = "__screenshot_ad_hoc__";
|
|
3389
|
-
async function resolveSessionForScreenshot(options) {
|
|
3463
|
+
async function resolveSessionForScreenshot(options, headers) {
|
|
3390
3464
|
if (options.session) {
|
|
3391
3465
|
const result2 = await getOrRestoreSession(options.session);
|
|
3392
3466
|
return result2?.session ?? null;
|
|
@@ -3395,6 +3469,7 @@ async function resolveSessionForScreenshot(options) {
|
|
|
3395
3469
|
const client = new import_client2.MCPClient();
|
|
3396
3470
|
client.addServer(AD_HOC_SESSION_NAME, {
|
|
3397
3471
|
url: options.mcp,
|
|
3472
|
+
...headers ? { headers } : {},
|
|
3398
3473
|
clientInfo: getCliClientInfo()
|
|
3399
3474
|
});
|
|
3400
3475
|
try {
|
|
@@ -3422,6 +3497,27 @@ async function screenshotCommand(options, argsList) {
|
|
|
3422
3497
|
exitCode = 1;
|
|
3423
3498
|
return;
|
|
3424
3499
|
}
|
|
3500
|
+
let headers;
|
|
3501
|
+
if (options.header && options.header.length > 0) {
|
|
3502
|
+
if (!options.mcp) {
|
|
3503
|
+
console.error(
|
|
3504
|
+
formatError(
|
|
3505
|
+
"--header is only supported with --mcp <url>. Saved sessions (use --session) carry their own auth from `mcp-use client connect`."
|
|
3506
|
+
)
|
|
3507
|
+
);
|
|
3508
|
+
exitCode = 1;
|
|
3509
|
+
return;
|
|
3510
|
+
}
|
|
3511
|
+
try {
|
|
3512
|
+
headers = parseHeaderArgs(options.header);
|
|
3513
|
+
} catch (err) {
|
|
3514
|
+
console.error(
|
|
3515
|
+
formatError(err instanceof Error ? err.message : String(err))
|
|
3516
|
+
);
|
|
3517
|
+
exitCode = 1;
|
|
3518
|
+
return;
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3425
3521
|
try {
|
|
3426
3522
|
resolveChromePath();
|
|
3427
3523
|
} catch (err) {
|
|
@@ -3435,7 +3531,7 @@ async function screenshotCommand(options, argsList) {
|
|
|
3435
3531
|
const height = parseDimension(options.height, "height");
|
|
3436
3532
|
const navTimeout = parseInt(options.timeout, 10) || 3e4;
|
|
3437
3533
|
const delayMs = options.delay ? parseInt(options.delay, 10) : 0;
|
|
3438
|
-
const session = await resolveSessionForScreenshot(options);
|
|
3534
|
+
const session = await resolveSessionForScreenshot(options, headers);
|
|
3439
3535
|
if (!session) {
|
|
3440
3536
|
exitCode = 1;
|
|
3441
3537
|
return;
|
|
@@ -3501,7 +3597,8 @@ async function screenshotCommand(options, argsList) {
|
|
|
3501
3597
|
delayMs,
|
|
3502
3598
|
timeoutMs: navTimeout,
|
|
3503
3599
|
inspector: options.inspector,
|
|
3504
|
-
quiet: options.quiet
|
|
3600
|
+
quiet: options.quiet,
|
|
3601
|
+
cdpUrl: options.cdpUrl
|
|
3505
3602
|
}
|
|
3506
3603
|
);
|
|
3507
3604
|
console.log(
|
|
@@ -3532,7 +3629,12 @@ function createScreenshotCommand() {
|
|
|
3532
3629
|
"Saved session name (from `mcp-use client connect`). Defaults to the active session."
|
|
3533
3630
|
).option(
|
|
3534
3631
|
"--mcp <url>",
|
|
3535
|
-
"Ad-hoc MCP server URL (escape hatch). Used only when no --session and no active saved session. No authentication."
|
|
3632
|
+
"Ad-hoc MCP server URL (escape hatch). Used only when no --session and no active saved session. No authentication unless --header is supplied."
|
|
3633
|
+
).option(
|
|
3634
|
+
"-H, --header <header>",
|
|
3635
|
+
'HTTP header to send to the --mcp <url> server, formatted "Key: Value". Repeatable. Use to pass an Authorization bearer token or other auth headers when screenshotting an authenticated MCP server.',
|
|
3636
|
+
collectHeader,
|
|
3637
|
+
[]
|
|
3536
3638
|
).option(
|
|
3537
3639
|
"--theme <light|dark>",
|
|
3538
3640
|
"Color scheme to render the view in.",
|
|
@@ -3547,7 +3649,10 @@ function createScreenshotCommand() {
|
|
|
3547
3649
|
"--delay <ms>",
|
|
3548
3650
|
"Extra wait after readiness, to let chart animations / async layouts settle.",
|
|
3549
3651
|
"0"
|
|
3550
|
-
).option("--timeout <ms>", "Navigation + readiness timeout in ms.", "30000").option(
|
|
3652
|
+
).option("--timeout <ms>", "Navigation + readiness timeout in ms.", "30000").option(
|
|
3653
|
+
"--cdp-url <url>",
|
|
3654
|
+
"Connect to an existing CDP WebSocket (ws:// or wss://) instead of spawning local Chrome. Useful for hosted browsers like Notte."
|
|
3655
|
+
).option("--quiet", "Suppress dev-server output.").action(async (args, opts) => {
|
|
3551
3656
|
await screenshotCommand(opts, args);
|
|
3552
3657
|
});
|
|
3553
3658
|
}
|