cbrowser 19.4.2 → 19.5.0
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/cli.js +122 -26
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +8 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +137 -24
- package/dist/daemon.js.map +1 -1
- package/dist/terminal-safe.d.ts +12 -0
- package/dist/terminal-safe.d.ts.map +1 -0
- package/dist/terminal-safe.js +46 -0
- package/dist/terminal-safe.js.map +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -29,6 +29,7 @@ import { getStatusInfo, formatStatus, getDataDir, getPaths } from "./config.js";
|
|
|
29
29
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
30
30
|
import { resolveCookieDomain, hostFromUrl } from "./utils.js";
|
|
31
31
|
import { dirname, join, resolve } from "node:path";
|
|
32
|
+
import { guardConsole, guardTerminalOutput } from "./terminal-safe.js";
|
|
32
33
|
import { VideoCaptureSession } from "./recording/engine.js";
|
|
33
34
|
import { changeSignal, parseManifest } from "./recording/types.js";
|
|
34
35
|
import { printEnterpriseStatus } from "./stealth/index.js";
|
|
@@ -339,10 +340,16 @@ PERFORMANCE REGRESSION (v6.4.0)
|
|
|
339
340
|
cbrowser perf-regression "https://example.com" homepage --sensitivity strict
|
|
340
341
|
cbrowser perf-regression "https://example.com" homepage --threshold-lcp 30
|
|
341
342
|
|
|
342
|
-
NETWORK / HAR
|
|
343
|
-
|
|
343
|
+
NETWORK / HAR (need the daemon: each command is its own process)
|
|
344
|
+
daemon start Start the daemon first; it holds the browser between commands
|
|
345
|
+
har start Start recording HAR in the daemon's browser
|
|
344
346
|
har stop [output] Stop and save HAR file
|
|
345
|
-
network list List
|
|
347
|
+
network list List network requests the daemon's browser captured
|
|
348
|
+
network clear Clear that list
|
|
349
|
+
console List the daemon tab's console messages (errors, warnings, logs)
|
|
350
|
+
--filter <type> all | error | warning | log | info (default all)
|
|
351
|
+
--clear Empty the console buffer after reading
|
|
352
|
+
--json Print the raw messages as JSON
|
|
346
353
|
|
|
347
354
|
VISUAL REGRESSION (v2.5.0)
|
|
348
355
|
visual save <name> Save baseline screenshot
|
|
@@ -2612,6 +2619,12 @@ async function main() {
|
|
|
2612
2619
|
const outputOpts = detectOutputOptions(options);
|
|
2613
2620
|
setOutputOptions(outputOpts);
|
|
2614
2621
|
installOutputWrappers();
|
|
2622
|
+
// Page-derived text (console messages, titles, extracted text) reaches stdout in
|
|
2623
|
+
// many places; escape sequences and bidi overrides in it come out as visible
|
|
2624
|
+
// escapes instead of acting on the terminal. (2026-10-12)
|
|
2625
|
+
guardTerminalOutput(process.stdout);
|
|
2626
|
+
guardTerminalOutput(process.stderr);
|
|
2627
|
+
guardConsole();
|
|
2615
2628
|
// --json-output must never leave stdout empty. Only two commands in this file
|
|
2616
2629
|
// call emitJson, so without this every other command printed nothing at all and
|
|
2617
2630
|
// still exited 0 — a CI step piping to jq saw empty input beside a success code.
|
|
@@ -4381,32 +4394,122 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
4381
4394
|
// =========================================================================
|
|
4382
4395
|
case "har": {
|
|
4383
4396
|
const subcommand = args[0];
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4397
|
+
if (subcommand !== "start" && subcommand !== "stop") {
|
|
4398
|
+
console.error("Usage: cbrowser har [start|stop]");
|
|
4399
|
+
break;
|
|
4400
|
+
}
|
|
4401
|
+
// A recording has to span several commands, and each cbrowser command is
|
|
4402
|
+
// its own process: only the daemon's browser outlives them. Recorded here,
|
|
4403
|
+
// start, navigate and stop were three processes and every HAR was empty,
|
|
4404
|
+
// with or without a daemon, while this printed "started" and "saved".
|
|
4405
|
+
// (2026-10-12)
|
|
4406
|
+
if (!(await isDaemonRunning())) {
|
|
4407
|
+
console.error("Error: HAR recording spans several commands, and each cbrowser command is its own process,");
|
|
4408
|
+
console.error(" so nothing records between them. Start the daemon first:");
|
|
4409
|
+
console.error(" cbrowser daemon start -> cbrowser har start -> navigate/click... -> cbrowser har stop [file]");
|
|
4410
|
+
process.exit(1);
|
|
4411
|
+
}
|
|
4412
|
+
const response = subcommand === "start"
|
|
4413
|
+
? await sendToDaemon("harStart", {})
|
|
4414
|
+
: await sendToDaemon("harStop", {});
|
|
4415
|
+
if (!response.success) {
|
|
4416
|
+
console.error(`✗ Daemon error: ${response.error}`);
|
|
4417
|
+
process.exit(1);
|
|
4418
|
+
}
|
|
4419
|
+
if (subcommand === "start") {
|
|
4420
|
+
console.log("✓ HAR recording started in the daemon");
|
|
4421
|
+
console.log(" Navigate and interact with pages, then run 'cbrowser har stop'");
|
|
4422
|
+
}
|
|
4423
|
+
else {
|
|
4424
|
+
// The daemon writes only its own har directory; a chosen path is written here,
|
|
4425
|
+
// with this process's permissions.
|
|
4426
|
+
const { file, har } = response.result;
|
|
4427
|
+
if (args[1]) {
|
|
4428
|
+
const out = resolve(args[1]);
|
|
4429
|
+
writeFileSync(out, har);
|
|
4430
|
+
console.log(`✓ HAR saved: ${out}`);
|
|
4390
4431
|
}
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
const filename = await browser.exportHar(output);
|
|
4394
|
-
console.log(`✓ HAR saved: ${filename}`);
|
|
4395
|
-
break;
|
|
4432
|
+
else {
|
|
4433
|
+
console.log(`✓ HAR saved: ${file}`);
|
|
4396
4434
|
}
|
|
4397
|
-
default:
|
|
4398
|
-
console.error("Usage: cbrowser har [start|stop]");
|
|
4399
4435
|
}
|
|
4400
|
-
|
|
4436
|
+
process.exit(0);
|
|
4401
4437
|
}
|
|
4402
4438
|
// =========================================================================
|
|
4403
4439
|
// Network
|
|
4404
4440
|
// =========================================================================
|
|
4441
|
+
case "console": {
|
|
4442
|
+
// The daemon tab's console (log, info, warning, error, uncaught exceptions,
|
|
4443
|
+
// failed resources), as get_console_messages returns it over MCP. Served by
|
|
4444
|
+
// the daemon: a fresh process has no tab and would always list nothing.
|
|
4445
|
+
const filter = typeof options.filter === "string" ? options.filter : "all";
|
|
4446
|
+
if (!["all", "error", "warning", "log", "info"].includes(filter)) {
|
|
4447
|
+
console.error(`Error: --filter must be one of all, error, warning, log, info (got "${filter}")`);
|
|
4448
|
+
process.exit(1);
|
|
4449
|
+
}
|
|
4450
|
+
if (!(await isDaemonRunning())) {
|
|
4451
|
+
console.error("Error: the console log only spans commands in the daemon's browser; each cbrowser command is");
|
|
4452
|
+
console.error(" its own process. Start the daemon first:");
|
|
4453
|
+
console.error(" cbrowser daemon start -> navigate/click... -> cbrowser console [--filter error] [--clear] [--json]");
|
|
4454
|
+
process.exit(1);
|
|
4455
|
+
}
|
|
4456
|
+
const response = await sendToDaemon("consoleMessages", { filter, clear: options.clear === true });
|
|
4457
|
+
if (!response.success) {
|
|
4458
|
+
console.error(`✗ Daemon error: ${response.error}`);
|
|
4459
|
+
process.exit(1);
|
|
4460
|
+
}
|
|
4461
|
+
const out = response.result;
|
|
4462
|
+
if (options.json === true) {
|
|
4463
|
+
console.log(JSON.stringify(out, null, 2));
|
|
4464
|
+
}
|
|
4465
|
+
else if (out.count === 0) {
|
|
4466
|
+
console.log(filter === "all" ? "No console messages" : `No ${filter} console messages`);
|
|
4467
|
+
}
|
|
4468
|
+
else {
|
|
4469
|
+
console.log(`\n🖥 Console (${out.count}${out.dropped ? `, ${out.dropped} older dropped` : ""}):\n`);
|
|
4470
|
+
for (const m of out.messages) {
|
|
4471
|
+
const where = m.url ? ` (${m.url}${m.lineNumber !== undefined ? `:${m.lineNumber}` : ""})` : "";
|
|
4472
|
+
console.log(` [${m.type}${m.source === "pageerror" ? ", uncaught" : ""}] ${m.text}${where}`);
|
|
4473
|
+
console.log(` on ${m.pageUrl}`);
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
if (options.clear === true)
|
|
4477
|
+
console.log("✓ Console cleared");
|
|
4478
|
+
process.exit(0);
|
|
4479
|
+
}
|
|
4405
4480
|
case "network": {
|
|
4406
4481
|
const subcommand = args[0];
|
|
4482
|
+
if (subcommand !== "list" && subcommand !== "clear") {
|
|
4483
|
+
console.error("Usage: cbrowser network [list|clear]");
|
|
4484
|
+
break;
|
|
4485
|
+
}
|
|
4486
|
+
// Same reason as har: the request log lives in a browser, and only the
|
|
4487
|
+
// daemon's browser outlives a command. A fresh process always listed
|
|
4488
|
+
// nothing and said "Navigate to a page first", even right after a
|
|
4489
|
+
// navigate. (2026-10-12)
|
|
4490
|
+
if (!(await isDaemonRunning())) {
|
|
4491
|
+
console.error("Error: the network log only spans commands in the daemon's browser; each cbrowser command is");
|
|
4492
|
+
console.error(" its own process. Start the daemon first:");
|
|
4493
|
+
console.error(" cbrowser daemon start -> navigate/click... -> cbrowser network list");
|
|
4494
|
+
process.exit(1);
|
|
4495
|
+
}
|
|
4496
|
+
if (subcommand === "clear") {
|
|
4497
|
+
const cleared = await sendToDaemon("networkClear", {});
|
|
4498
|
+
if (!cleared.success) {
|
|
4499
|
+
console.error(`✗ Daemon error: ${cleared.error}`);
|
|
4500
|
+
process.exit(1);
|
|
4501
|
+
}
|
|
4502
|
+
console.log("✓ Network history cleared");
|
|
4503
|
+
process.exit(0);
|
|
4504
|
+
}
|
|
4505
|
+
const listed = await sendToDaemon("networkList", {});
|
|
4506
|
+
if (!listed.success) {
|
|
4507
|
+
console.error(`✗ Daemon error: ${listed.error}`);
|
|
4508
|
+
process.exit(1);
|
|
4509
|
+
}
|
|
4407
4510
|
switch (subcommand) {
|
|
4408
4511
|
case "list": {
|
|
4409
|
-
const requests =
|
|
4512
|
+
const requests = listed.result.requests;
|
|
4410
4513
|
if (requests.length === 0) {
|
|
4411
4514
|
console.log("No network requests captured");
|
|
4412
4515
|
console.log("Navigate to a page first to capture requests");
|
|
@@ -4423,15 +4526,8 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
4423
4526
|
}
|
|
4424
4527
|
break;
|
|
4425
4528
|
}
|
|
4426
|
-
case "clear": {
|
|
4427
|
-
browser.clearNetworkHistory();
|
|
4428
|
-
console.log("✓ Network history cleared");
|
|
4429
|
-
break;
|
|
4430
|
-
}
|
|
4431
|
-
default:
|
|
4432
|
-
console.error("Usage: cbrowser network [list|clear]");
|
|
4433
4529
|
}
|
|
4434
|
-
|
|
4530
|
+
process.exit(0);
|
|
4435
4531
|
}
|
|
4436
4532
|
// =========================================================================
|
|
4437
4533
|
// Visual Regression (Tier 2)
|