cbrowser 19.4.3 → 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 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";
@@ -345,6 +346,10 @@ NETWORK / HAR (need the daemon: each command is its own process)
345
346
  har stop [output] Stop and save HAR file
346
347
  network list List network requests the daemon's browser captured
347
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
348
353
 
349
354
  VISUAL REGRESSION (v2.5.0)
350
355
  visual save <name> Save baseline screenshot
@@ -2614,6 +2619,12 @@ async function main() {
2614
2619
  const outputOpts = detectOutputOptions(options);
2615
2620
  setOutputOptions(outputOpts);
2616
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();
2617
2628
  // --json-output must never leave stdout empty. Only two commands in this file
2618
2629
  // call emitJson, so without this every other command printed nothing at all and
2619
2630
  // still exited 0 — a CI step piping to jq saw empty input beside a success code.
@@ -4427,6 +4438,45 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
4427
4438
  // =========================================================================
4428
4439
  // Network
4429
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
+ }
4430
4480
  case "network": {
4431
4481
  const subcommand = args[0];
4432
4482
  if (subcommand !== "list" && subcommand !== "clear") {