cbrowser 6.3.1 → 6.4.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/browser.d.ts +43 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +365 -0
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +294 -2
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +61 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +358 -0
- package/dist/daemon.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +103 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -10,11 +10,12 @@ const browser_js_1 = require("./browser.js");
|
|
|
10
10
|
const personas_js_1 = require("./personas.js");
|
|
11
11
|
const types_js_1 = require("./types.js");
|
|
12
12
|
const mcp_server_js_1 = require("./mcp-server.js");
|
|
13
|
+
const daemon_js_1 = require("./daemon.js");
|
|
13
14
|
function showHelp() {
|
|
14
15
|
console.log(`
|
|
15
16
|
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
16
|
-
║ CBrowser CLI v6.
|
|
17
|
-
║ AI-powered browser automation with
|
|
17
|
+
║ CBrowser CLI v6.4.0 ║
|
|
18
|
+
║ AI-powered browser automation with performance regression detection ║
|
|
18
19
|
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
19
20
|
|
|
20
21
|
NAVIGATION
|
|
@@ -147,6 +148,27 @@ PERFORMANCE
|
|
|
147
148
|
--budget-fcp <ms> FCP budget (default: 1800)
|
|
148
149
|
--budget-cls <score> CLS budget (default: 0.1)
|
|
149
150
|
|
|
151
|
+
PERFORMANCE REGRESSION (v6.4.0)
|
|
152
|
+
perf-baseline save <url> Capture and save performance baseline
|
|
153
|
+
--name <name> Human-readable name for baseline
|
|
154
|
+
--runs <n> Number of runs to average (default: 3)
|
|
155
|
+
Examples:
|
|
156
|
+
cbrowser perf-baseline save "https://example.com" --name "homepage"
|
|
157
|
+
cbrowser perf-baseline save "https://example.com/checkout" --runs 5
|
|
158
|
+
|
|
159
|
+
perf-baseline list List all saved baselines
|
|
160
|
+
perf-baseline show <name> Show baseline details
|
|
161
|
+
perf-baseline delete <name> Delete a baseline
|
|
162
|
+
|
|
163
|
+
perf-regression <url> <baseline> Compare current performance against baseline
|
|
164
|
+
--threshold-lcp <n> Max LCP increase % (default: 20)
|
|
165
|
+
--threshold-cls <n> Max CLS increase (default: 0.1)
|
|
166
|
+
--threshold-fcp <n> Max FCP increase % (default: 20)
|
|
167
|
+
--output <file> Save JSON report to file
|
|
168
|
+
Examples:
|
|
169
|
+
cbrowser perf-regression "https://example.com" homepage
|
|
170
|
+
cbrowser perf-regression "https://example.com" homepage --threshold-lcp 30
|
|
171
|
+
|
|
150
172
|
NETWORK / HAR
|
|
151
173
|
har start Start recording HAR
|
|
152
174
|
har stop [output] Stop and save HAR file
|
|
@@ -259,6 +281,16 @@ MCP SERVER (v5.0.0)
|
|
|
259
281
|
mcp-server Start CBrowser as MCP server for Claude integration
|
|
260
282
|
Use with Claude Desktop or other MCP-compatible clients
|
|
261
283
|
|
|
284
|
+
DAEMON MODE (v6.4.0)
|
|
285
|
+
daemon start Start background daemon (keeps browser running)
|
|
286
|
+
--port <port> Daemon port (default: 9222)
|
|
287
|
+
--timeout <min> Idle timeout in minutes (default: 30)
|
|
288
|
+
daemon stop Stop the running daemon
|
|
289
|
+
daemon status Check if daemon is running
|
|
290
|
+
daemon run Run daemon in foreground (internal use)
|
|
291
|
+
Note: When daemon is running, all commands automatically connect to it
|
|
292
|
+
instead of launching a new browser - much faster for iteration!
|
|
293
|
+
|
|
262
294
|
STORAGE & CLEANUP
|
|
263
295
|
storage Show storage usage statistics
|
|
264
296
|
cleanup Clean up old files
|
|
@@ -661,6 +693,90 @@ async function main() {
|
|
|
661
693
|
await (0, mcp_server_js_1.startMcpServer)();
|
|
662
694
|
return;
|
|
663
695
|
}
|
|
696
|
+
// Daemon mode commands
|
|
697
|
+
if (command === "daemon") {
|
|
698
|
+
const subCommand = args[0];
|
|
699
|
+
const port = parseInt(options.port) || 9222;
|
|
700
|
+
switch (subCommand) {
|
|
701
|
+
case "start": {
|
|
702
|
+
console.log("🚀 Starting CBrowser daemon...");
|
|
703
|
+
const result = await (0, daemon_js_1.startDaemon)(port);
|
|
704
|
+
console.log(result.success ? `✓ ${result.message}` : `✗ ${result.message}`);
|
|
705
|
+
process.exit(result.success ? 0 : 1);
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
case "stop": {
|
|
709
|
+
console.log("🛑 Stopping CBrowser daemon...");
|
|
710
|
+
const result = await (0, daemon_js_1.stopDaemon)();
|
|
711
|
+
console.log(result.success ? `✓ ${result.message}` : `✗ ${result.message}`);
|
|
712
|
+
process.exit(0);
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
case "status": {
|
|
716
|
+
const status = await (0, daemon_js_1.getDaemonStatus)();
|
|
717
|
+
console.log(status);
|
|
718
|
+
process.exit(0);
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
case "run": {
|
|
722
|
+
// Internal: run daemon in foreground
|
|
723
|
+
console.log("🔧 Running daemon in foreground mode...");
|
|
724
|
+
const browserType = options.browser === "firefox" ? "firefox"
|
|
725
|
+
: options.browser === "webkit" ? "webkit"
|
|
726
|
+
: "chromium";
|
|
727
|
+
// runDaemonServer will merge with defaults internally
|
|
728
|
+
await (0, daemon_js_1.runDaemonServer)({
|
|
729
|
+
browser: browserType,
|
|
730
|
+
headless: options.headless !== false && options.headless !== "false",
|
|
731
|
+
}, port);
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
default:
|
|
735
|
+
console.error(`Unknown daemon command: ${subCommand}`);
|
|
736
|
+
console.error("Use: daemon start | daemon stop | daemon status");
|
|
737
|
+
process.exit(1);
|
|
738
|
+
}
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
// Check if daemon is running and use it for supported commands
|
|
742
|
+
const daemonRunning = await (0, daemon_js_1.isDaemonRunning)();
|
|
743
|
+
if (daemonRunning && ["navigate", "click", "fill", "screenshot", "extract", "run"].includes(command)) {
|
|
744
|
+
console.log("🔌 Connected to running daemon");
|
|
745
|
+
let daemonCommand = command;
|
|
746
|
+
let daemonArgs = {};
|
|
747
|
+
switch (command) {
|
|
748
|
+
case "navigate":
|
|
749
|
+
daemonArgs = { url: args[0] };
|
|
750
|
+
break;
|
|
751
|
+
case "click":
|
|
752
|
+
daemonArgs = { selector: args[0] };
|
|
753
|
+
break;
|
|
754
|
+
case "fill":
|
|
755
|
+
daemonArgs = { selector: args[0], value: args[1] };
|
|
756
|
+
break;
|
|
757
|
+
case "screenshot":
|
|
758
|
+
daemonArgs = { path: args[0] };
|
|
759
|
+
break;
|
|
760
|
+
case "extract":
|
|
761
|
+
daemonArgs = { what: args[0] };
|
|
762
|
+
break;
|
|
763
|
+
case "run":
|
|
764
|
+
daemonArgs = { command: args.join(" ") };
|
|
765
|
+
break;
|
|
766
|
+
}
|
|
767
|
+
const result = await (0, daemon_js_1.sendToDaemon)(daemonCommand, daemonArgs);
|
|
768
|
+
if (result.success) {
|
|
769
|
+
console.log("✓ Command executed via daemon");
|
|
770
|
+
if (result.result) {
|
|
771
|
+
console.log(JSON.stringify(result.result, null, 2));
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
console.error(`✗ Daemon error: ${result.error}`);
|
|
776
|
+
process.exit(1);
|
|
777
|
+
}
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
664
780
|
// Parse browser type
|
|
665
781
|
const browserType = options.browser === "firefox" ? "firefox"
|
|
666
782
|
: options.browser === "webkit" ? "webkit"
|
|
@@ -2551,6 +2667,182 @@ async function main() {
|
|
|
2551
2667
|
}
|
|
2552
2668
|
break;
|
|
2553
2669
|
}
|
|
2670
|
+
// =========================================================================
|
|
2671
|
+
// Performance Regression Detection (Tier 6 - v6.4.0)
|
|
2672
|
+
// =========================================================================
|
|
2673
|
+
case "perf-baseline": {
|
|
2674
|
+
const subcommand = args[0];
|
|
2675
|
+
const fs = await import("fs");
|
|
2676
|
+
switch (subcommand) {
|
|
2677
|
+
case "save": {
|
|
2678
|
+
const url = args[1];
|
|
2679
|
+
if (!url) {
|
|
2680
|
+
console.error("Usage: cbrowser perf-baseline save <url> [--name <name>] [--runs <n>]");
|
|
2681
|
+
process.exit(1);
|
|
2682
|
+
}
|
|
2683
|
+
console.log(`\n📊 Capturing performance baseline for: ${url}`);
|
|
2684
|
+
const baselineOptions = {
|
|
2685
|
+
headless,
|
|
2686
|
+
name: options.name,
|
|
2687
|
+
runs: options.runs ? parseInt(options.runs) : 3,
|
|
2688
|
+
};
|
|
2689
|
+
console.log(` Running ${baselineOptions.runs} measurement(s)...`);
|
|
2690
|
+
const baseline = await (0, browser_js_1.capturePerformanceBaseline)(url, baselineOptions);
|
|
2691
|
+
console.log(`\n✅ Baseline saved: ${baseline.name}`);
|
|
2692
|
+
console.log(` ID: ${baseline.id}`);
|
|
2693
|
+
console.log(` URL: ${baseline.url}`);
|
|
2694
|
+
console.log(` Timestamp: ${new Date(baseline.timestamp).toLocaleString()}`);
|
|
2695
|
+
console.log(`\n📈 Metrics (averaged over ${baseline.runsAveraged} runs):`);
|
|
2696
|
+
if (baseline.metrics.lcp !== undefined) {
|
|
2697
|
+
console.log(` LCP: ${baseline.metrics.lcp.toFixed(0)}ms (${baseline.metrics.lcpRating})`);
|
|
2698
|
+
}
|
|
2699
|
+
if (baseline.metrics.fcp !== undefined) {
|
|
2700
|
+
console.log(` FCP: ${baseline.metrics.fcp.toFixed(0)}ms`);
|
|
2701
|
+
}
|
|
2702
|
+
if (baseline.metrics.cls !== undefined) {
|
|
2703
|
+
console.log(` CLS: ${baseline.metrics.cls.toFixed(3)} (${baseline.metrics.clsRating})`);
|
|
2704
|
+
}
|
|
2705
|
+
if (baseline.metrics.ttfb !== undefined) {
|
|
2706
|
+
console.log(` TTFB: ${baseline.metrics.ttfb.toFixed(0)}ms`);
|
|
2707
|
+
}
|
|
2708
|
+
if (baseline.metrics.tti !== undefined) {
|
|
2709
|
+
console.log(` TTI: ${baseline.metrics.tti.toFixed(0)}ms`);
|
|
2710
|
+
}
|
|
2711
|
+
if (baseline.metrics.transferSize !== undefined) {
|
|
2712
|
+
console.log(` Transfer: ${(baseline.metrics.transferSize / 1024).toFixed(1)}KB`);
|
|
2713
|
+
}
|
|
2714
|
+
break;
|
|
2715
|
+
}
|
|
2716
|
+
case "list": {
|
|
2717
|
+
const baselines = (0, browser_js_1.listPerformanceBaselines)();
|
|
2718
|
+
if (baselines.length === 0) {
|
|
2719
|
+
console.log("\n📊 No performance baselines saved yet.");
|
|
2720
|
+
console.log(" Use: cbrowser perf-baseline save <url> --name <name>");
|
|
2721
|
+
}
|
|
2722
|
+
else {
|
|
2723
|
+
console.log(`\n📊 Performance Baselines (${baselines.length}):\n`);
|
|
2724
|
+
for (const b of baselines) {
|
|
2725
|
+
const date = new Date(b.timestamp).toLocaleDateString();
|
|
2726
|
+
const lcp = b.metrics.lcp ? `LCP: ${b.metrics.lcp.toFixed(0)}ms` : "";
|
|
2727
|
+
console.log(` ${b.name}`);
|
|
2728
|
+
console.log(` ID: ${b.id}`);
|
|
2729
|
+
console.log(` URL: ${b.url}`);
|
|
2730
|
+
console.log(` Date: ${date} | ${lcp}`);
|
|
2731
|
+
console.log("");
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
break;
|
|
2735
|
+
}
|
|
2736
|
+
case "show": {
|
|
2737
|
+
const name = args[1];
|
|
2738
|
+
if (!name) {
|
|
2739
|
+
console.error("Usage: cbrowser perf-baseline show <name|id>");
|
|
2740
|
+
process.exit(1);
|
|
2741
|
+
}
|
|
2742
|
+
const baseline = (0, browser_js_1.loadPerformanceBaseline)(name);
|
|
2743
|
+
if (!baseline) {
|
|
2744
|
+
console.error(`Baseline not found: ${name}`);
|
|
2745
|
+
process.exit(1);
|
|
2746
|
+
}
|
|
2747
|
+
console.log(`\n📊 Performance Baseline: ${baseline.name}`);
|
|
2748
|
+
console.log(` ID: ${baseline.id}`);
|
|
2749
|
+
console.log(` URL: ${baseline.url}`);
|
|
2750
|
+
console.log(` Timestamp: ${new Date(baseline.timestamp).toLocaleString()}`);
|
|
2751
|
+
console.log(` Runs Averaged: ${baseline.runsAveraged}`);
|
|
2752
|
+
console.log(`\n📈 Metrics:`);
|
|
2753
|
+
console.log(JSON.stringify(baseline.metrics, null, 2));
|
|
2754
|
+
console.log(`\n🖥️ Environment:`);
|
|
2755
|
+
console.log(JSON.stringify(baseline.environment, null, 2));
|
|
2756
|
+
break;
|
|
2757
|
+
}
|
|
2758
|
+
case "delete": {
|
|
2759
|
+
const name = args[1];
|
|
2760
|
+
if (!name) {
|
|
2761
|
+
console.error("Usage: cbrowser perf-baseline delete <name|id>");
|
|
2762
|
+
process.exit(1);
|
|
2763
|
+
}
|
|
2764
|
+
const deleted = (0, browser_js_1.deletePerformanceBaseline)(name);
|
|
2765
|
+
if (deleted) {
|
|
2766
|
+
console.log(`\n✅ Deleted baseline: ${name}`);
|
|
2767
|
+
}
|
|
2768
|
+
else {
|
|
2769
|
+
console.error(`Baseline not found: ${name}`);
|
|
2770
|
+
process.exit(1);
|
|
2771
|
+
}
|
|
2772
|
+
break;
|
|
2773
|
+
}
|
|
2774
|
+
default:
|
|
2775
|
+
console.error("Usage: cbrowser perf-baseline <save|list|show|delete>");
|
|
2776
|
+
console.error("");
|
|
2777
|
+
console.error("Subcommands:");
|
|
2778
|
+
console.error(" save <url> Capture and save performance baseline");
|
|
2779
|
+
console.error(" list List all saved baselines");
|
|
2780
|
+
console.error(" show <name> Show baseline details");
|
|
2781
|
+
console.error(" delete <name> Delete a baseline");
|
|
2782
|
+
process.exit(1);
|
|
2783
|
+
}
|
|
2784
|
+
break;
|
|
2785
|
+
}
|
|
2786
|
+
case "perf-regression": {
|
|
2787
|
+
const url = args[0];
|
|
2788
|
+
const baselineName = args[1];
|
|
2789
|
+
if (!url || !baselineName) {
|
|
2790
|
+
console.error("Usage: cbrowser perf-regression <url> <baseline-name> [options]");
|
|
2791
|
+
console.error("");
|
|
2792
|
+
console.error("Options:");
|
|
2793
|
+
console.error(" --threshold-lcp <n> Max LCP increase % (default: 20)");
|
|
2794
|
+
console.error(" --threshold-cls <n> Max CLS increase absolute (default: 0.1)");
|
|
2795
|
+
console.error(" --threshold-fcp <n> Max FCP increase % (default: 20)");
|
|
2796
|
+
console.error(" --threshold-ttfb <n> Max TTFB increase % (default: 30)");
|
|
2797
|
+
console.error(" --output <file> Save JSON report to file");
|
|
2798
|
+
console.error("");
|
|
2799
|
+
console.error("Examples:");
|
|
2800
|
+
console.error(" cbrowser perf-regression https://example.com homepage");
|
|
2801
|
+
console.error(" cbrowser perf-regression https://example.com homepage --threshold-lcp 30");
|
|
2802
|
+
process.exit(1);
|
|
2803
|
+
}
|
|
2804
|
+
console.log(`\n🔍 Checking for performance regressions...`);
|
|
2805
|
+
console.log(` URL: ${url}`);
|
|
2806
|
+
console.log(` Baseline: ${baselineName}`);
|
|
2807
|
+
const thresholds = {};
|
|
2808
|
+
if (options["threshold-lcp"])
|
|
2809
|
+
thresholds.lcp = parseInt(options["threshold-lcp"]);
|
|
2810
|
+
if (options["threshold-cls"])
|
|
2811
|
+
thresholds.cls = parseFloat(options["threshold-cls"]);
|
|
2812
|
+
if (options["threshold-fcp"])
|
|
2813
|
+
thresholds.fcp = parseInt(options["threshold-fcp"]);
|
|
2814
|
+
if (options["threshold-ttfb"])
|
|
2815
|
+
thresholds.ttfb = parseInt(options["threshold-ttfb"]);
|
|
2816
|
+
if (options["threshold-tti"])
|
|
2817
|
+
thresholds.tti = parseInt(options["threshold-tti"]);
|
|
2818
|
+
if (options["threshold-tbt"])
|
|
2819
|
+
thresholds.tbt = parseInt(options["threshold-tbt"]);
|
|
2820
|
+
const regressionOptions = {
|
|
2821
|
+
headless,
|
|
2822
|
+
thresholds: Object.keys(thresholds).length > 0 ? thresholds : undefined,
|
|
2823
|
+
};
|
|
2824
|
+
try {
|
|
2825
|
+
const result = await (0, browser_js_1.detectPerformanceRegression)(url, baselineName, regressionOptions);
|
|
2826
|
+
// Print formatted report
|
|
2827
|
+
const report = (0, browser_js_1.formatPerformanceRegressionReport)(result);
|
|
2828
|
+
console.log("\n" + report);
|
|
2829
|
+
// Save JSON report if requested
|
|
2830
|
+
const fs = await import("fs");
|
|
2831
|
+
if (options.output) {
|
|
2832
|
+
fs.writeFileSync(options.output, JSON.stringify(result, null, 2));
|
|
2833
|
+
console.log(`\n📄 JSON report saved: ${options.output}`);
|
|
2834
|
+
}
|
|
2835
|
+
// Exit with error code if regressions found
|
|
2836
|
+
if (!result.passed) {
|
|
2837
|
+
process.exit(1);
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
catch (error) {
|
|
2841
|
+
console.error(`\n❌ Error: ${error.message}`);
|
|
2842
|
+
process.exit(1);
|
|
2843
|
+
}
|
|
2844
|
+
break;
|
|
2845
|
+
}
|
|
2554
2846
|
default:
|
|
2555
2847
|
console.error(`Unknown command: ${command}`);
|
|
2556
2848
|
console.error("Run 'cbrowser help' for usage");
|