browser-cdp 0.6.1 → 0.6.3

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/README.md CHANGED
@@ -37,6 +37,14 @@ browser-cdp pick '<message>'
37
37
  # Stream browser console output (network errors, exceptions, logs)
38
38
  browser-cdp console [--duration=SECONDS]
39
39
 
40
+ # Stream network requests/responses
41
+ browser-cdp network [--filter=PATTERN] [--json] [--errors] [--duration=SECONDS]
42
+
43
+ # Manage cookies (export/import/clear)
44
+ browser-cdp cookies export [--path=FILE]
45
+ browser-cdp cookies import <file>
46
+ browser-cdp cookies clear
47
+
40
48
  # Show page performance metrics
41
49
  browser-cdp insights [--json]
42
50
 
@@ -89,6 +97,32 @@ browser-cdp console
89
97
  # Stream console for 10 seconds
90
98
  browser-cdp console --duration=10
91
99
 
100
+ # Stream network traffic
101
+ browser-cdp network
102
+ # Then navigate to see requests
103
+
104
+ # Filter to API calls only
105
+ browser-cdp network --filter=api
106
+
107
+ # Only show failed requests (4xx/5xx)
108
+ browser-cdp network --errors
109
+
110
+ # JSON output for parsing
111
+ browser-cdp network --json --duration=5 | jq '.url'
112
+
113
+ # Export cookies to JSON file
114
+ browser-cdp cookies export
115
+ # Returns: cookies.json with all cookies
116
+
117
+ # Export to custom file
118
+ browser-cdp cookies export --path session.json
119
+
120
+ # Import cookies from file
121
+ browser-cdp cookies import session.json
122
+
123
+ # Clear all cookies
124
+ browser-cdp cookies clear
125
+
92
126
  # Get page performance insights
93
127
  browser-cdp insights
94
128
  # Returns: TTFB, First Paint, FCP, DOM loaded, resources, memory
@@ -103,6 +137,56 @@ browser-cdp lighthouse
103
137
  browser-cdp close
104
138
  ```
105
139
 
140
+ ## Cookies Command
141
+
142
+ The `cookies` command provides session persistence for authenticated workflows:
143
+
144
+ ### Export Cookies
145
+
146
+ Save your browser cookies to a JSON file for later use:
147
+
148
+ ```bash
149
+ browser-cdp cookies export # Saves to cookies.json
150
+ browser-cdp cookies export --path auth.json # Save to specific file
151
+ ```
152
+
153
+ Output format:
154
+ ```json
155
+ [
156
+ {
157
+ "name": "session_id",
158
+ "value": "abc123...",
159
+ "domain": "example.com",
160
+ "path": "/",
161
+ "httpOnly": true,
162
+ "secure": true,
163
+ "sameSite": "Strict",
164
+ "expires": 1735689600
165
+ }
166
+ ]
167
+ ```
168
+
169
+ ### Import Cookies
170
+
171
+ Load previously exported cookies into the browser:
172
+
173
+ ```bash
174
+ browser-cdp cookies import auth.json
175
+ ```
176
+
177
+ Useful for:
178
+ - Resuming authenticated sessions across browser restarts
179
+ - Sharing sessions across team members
180
+ - Preserving login state for automation workflows
181
+
182
+ ### Clear Cookies
183
+
184
+ Delete all cookies from the browser:
185
+
186
+ ```bash
187
+ browser-cdp cookies clear
188
+ ```
189
+
106
190
  ## Pre-started Browser
107
191
 
108
192
  If you already have a browser running with CDP enabled, the CLI will connect to it:
package/cli.js CHANGED
@@ -20,6 +20,8 @@ const commands = {
20
20
  pdf: "./src/pdf.js",
21
21
  pick: "./src/pick.js",
22
22
  console: "./src/console.js",
23
+ network: "./src/network.js",
24
+ cookies: "./src/cookies.js",
23
25
  insights: "./src/insights.js",
24
26
  lighthouse: "./src/lighthouse.js",
25
27
  };
@@ -39,6 +41,8 @@ function printUsage() {
39
41
  console.log(" pdf Export current page as PDF");
40
42
  console.log(" pick '<message>' Interactive element picker");
41
43
  console.log(" console Stream browser console output");
44
+ console.log(" network Stream network requests/responses");
45
+ console.log(" cookies Export/import/clear browser cookies");
42
46
  console.log(" insights Show page performance metrics");
43
47
  console.log(" lighthouse Run Lighthouse audit");
44
48
  console.log("");
@@ -53,6 +57,8 @@ function printUsage() {
53
57
  console.log(" browser-cdp eval 'document.title'");
54
58
  console.log(" browser-cdp dom > page.html");
55
59
  console.log(" browser-cdp console --duration=10");
60
+ console.log(" browser-cdp network --filter=api");
61
+ console.log(" browser-cdp cookies export --path session.json");
56
62
  console.log(" browser-cdp insights --json");
57
63
  process.exit(0);
58
64
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-cdp",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "Browser automation via Chrome DevTools Protocol - control Chrome, Brave, Edge with real browser profiles",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cookies.js ADDED
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, writeFileSync } from "node:fs";
4
+ import { chromium } from "playwright";
5
+ import { DEFAULT_PORT, getActivePage } from "./utils.js";
6
+
7
+ const args = process.argv.slice(2);
8
+ const subcommand = args[0];
9
+ const showHelp = args.includes("--help") || args.includes("-h");
10
+
11
+ function printUsage() {
12
+ console.log("Usage: cookies <subcommand> [options]");
13
+ console.log("");
14
+ console.log("Subcommands:");
15
+ console.log(" export Export cookies to JSON file");
16
+ console.log(" import <file> Import cookies from JSON file");
17
+ console.log(" clear Clear all cookies from browser");
18
+ console.log("");
19
+ console.log("Options:");
20
+ console.log(" --path <file> Output file path for export (default: cookies.json)");
21
+ console.log("");
22
+ console.log("Examples:");
23
+ console.log(" cookies export");
24
+ console.log(" cookies export --path session.json");
25
+ console.log(" cookies import session.json");
26
+ console.log(" cookies clear");
27
+ process.exit(0);
28
+ }
29
+
30
+ if (!subcommand || showHelp) {
31
+ printUsage();
32
+ }
33
+
34
+ if (!["export", "import", "clear"].includes(subcommand)) {
35
+ console.error(`Unknown subcommand: ${subcommand}`);
36
+ console.log("Available: export, import, clear");
37
+ process.exit(1);
38
+ }
39
+
40
+ const browser = await chromium.connectOverCDP(`http://localhost:${DEFAULT_PORT}`);
41
+ const contexts = browser.contexts();
42
+ const context = contexts[0];
43
+
44
+ if (!context) {
45
+ console.error("No browser context found");
46
+ process.exit(1);
47
+ }
48
+
49
+ const pages = context.pages();
50
+ const page = getActivePage(pages);
51
+
52
+ if (!page) {
53
+ console.error("No active tab found");
54
+ process.exit(1);
55
+ }
56
+
57
+ const cdp = await page.context().newCDPSession(page);
58
+
59
+ if (subcommand === "export") {
60
+ const pathIdx = args.findIndex((a) => a === "--path");
61
+ const outputFile = pathIdx !== -1 ? args[pathIdx + 1] : "cookies.json";
62
+
63
+ const { cookies } = await cdp.send("Network.getCookies");
64
+ writeFileSync(outputFile, JSON.stringify(cookies, null, 2));
65
+ console.log(`Exported ${cookies.length} cookie(s) to ${outputFile}`);
66
+ } else if (subcommand === "import") {
67
+ const importFile = args[1];
68
+
69
+ if (!importFile) {
70
+ console.error("Error: import requires a file path");
71
+ process.exit(1);
72
+ }
73
+
74
+ const cookies = JSON.parse(readFileSync(importFile, "utf8"));
75
+
76
+ if (!Array.isArray(cookies)) {
77
+ console.error("Error: Cookie file must contain a JSON array");
78
+ process.exit(1);
79
+ }
80
+
81
+ await cdp.send("Network.setCookies", { cookies });
82
+ console.log(`Imported ${cookies.length} cookie(s) from ${importFile}`);
83
+ } else if (subcommand === "clear") {
84
+ await cdp.send("Network.clearBrowserCookies");
85
+ console.log("Cleared all cookies");
86
+ }
87
+
88
+ await browser.close();
package/src/network.js ADDED
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { chromium } from "playwright";
4
+ import { DEFAULT_PORT, getActivePage, formatTime, resetColor } from "./utils.js";
5
+
6
+ const args = process.argv.slice(2);
7
+ const filterPattern = args.find((a) => a.startsWith("--filter="))?.split("=")[1];
8
+ const jsonOutput = args.includes("--json");
9
+ const errorsOnly = args.includes("--errors");
10
+ const duration = args.find((a) => a.startsWith("--duration="));
11
+ const durationMs = duration ? parseInt(duration.split("=")[1]) * 1000 : null;
12
+ const showHelp = args.includes("--help") || args.includes("-h");
13
+
14
+ if (showHelp) {
15
+ console.log("Usage: network.js [options]");
16
+ console.log("\nStream network requests and responses in real-time.");
17
+ console.log("\nOptions:");
18
+ console.log(" --filter=PATTERN Filter URLs by regex pattern");
19
+ console.log(" --json Output as JSON for piping");
20
+ console.log(" --errors Only show failed requests (4xx/5xx)");
21
+ console.log(" --duration=N Stop after N seconds (default: run until Ctrl+C)");
22
+ console.log("\nExamples:");
23
+ console.log(" network.js # Stream all network traffic");
24
+ console.log(" network.js --filter=api # Only show URLs containing 'api'");
25
+ console.log(" network.js --errors # Only show failed requests");
26
+ console.log(" network.js --json # JSON output for jq/parsing");
27
+ console.log(" network.js --duration=10 # Capture for 10 seconds");
28
+ process.exit(0);
29
+ }
30
+
31
+ const browser = await chromium.connectOverCDP(`http://localhost:${DEFAULT_PORT}`);
32
+ const contexts = browser.contexts();
33
+ const context = contexts[0];
34
+
35
+ if (!context) {
36
+ console.error("No browser context found");
37
+ process.exit(1);
38
+ }
39
+
40
+ const pages = context.pages();
41
+ const page = getActivePage(pages);
42
+
43
+ if (!page) {
44
+ console.error("No active tab found");
45
+ process.exit(1);
46
+ }
47
+
48
+ console.error(`Connected to: ${page.url()}`);
49
+
50
+ const cdp = await page.context().newCDPSession(page);
51
+ await cdp.send("Network.enable");
52
+
53
+ const requests = new Map();
54
+
55
+ const statusColors = {
56
+ success: "\x1b[32m", // green for 2xx
57
+ redirect: "\x1b[33m", // yellow for 3xx
58
+ error: "\x1b[31m", // red for 4xx/5xx
59
+ request: "\x1b[36m", // cyan for requests
60
+ };
61
+
62
+ function matchesFilter(url) {
63
+ if (!filterPattern) return true;
64
+ try {
65
+ return new RegExp(filterPattern).test(url);
66
+ } catch {
67
+ return url.includes(filterPattern);
68
+ }
69
+ }
70
+
71
+ cdp.on("Network.requestWillBeSent", ({ requestId, request, timestamp }) => {
72
+ requests.set(requestId, { method: request.method, url: request.url, timestamp });
73
+
74
+ if (!matchesFilter(request.url)) return;
75
+ if (errorsOnly) return;
76
+
77
+ if (jsonOutput) {
78
+ console.log(
79
+ JSON.stringify({
80
+ type: "request",
81
+ timestamp: new Date(timestamp * 1000).toISOString(),
82
+ method: request.method,
83
+ url: request.url,
84
+ requestId,
85
+ })
86
+ );
87
+ } else {
88
+ console.log(`${statusColors.request}[${formatTime()}] → ${request.method} ${request.url}${resetColor}`);
89
+ }
90
+ });
91
+
92
+ cdp.on("Network.responseReceived", ({ requestId, response, timestamp, type }) => {
93
+ const req = requests.get(requestId);
94
+
95
+ if (!matchesFilter(response.url)) return;
96
+ if (errorsOnly && response.status < 400) return;
97
+
98
+ if (jsonOutput) {
99
+ console.log(
100
+ JSON.stringify({
101
+ type: "response",
102
+ timestamp: new Date(timestamp * 1000).toISOString(),
103
+ status: response.status,
104
+ statusText: response.statusText,
105
+ url: response.url,
106
+ mimeType: response.mimeType,
107
+ resourceType: type,
108
+ requestId,
109
+ method: req?.method,
110
+ })
111
+ );
112
+ } else {
113
+ let color = statusColors.success;
114
+ if (response.status >= 400) {
115
+ color = statusColors.error;
116
+ } else if (response.status >= 300) {
117
+ color = statusColors.redirect;
118
+ }
119
+
120
+ const method = req ? `${req.method} ` : "";
121
+ console.log(`${color}[${formatTime()}] ← ${response.status} ${method}${response.url}${resetColor}`);
122
+ }
123
+ });
124
+
125
+ cdp.on("Network.loadingFailed", ({ requestId, errorText, blockedReason, canceled }) => {
126
+ const req = requests.get(requestId);
127
+
128
+ if (req && !matchesFilter(req.url)) return;
129
+
130
+ if (jsonOutput) {
131
+ console.log(
132
+ JSON.stringify({
133
+ type: "failed",
134
+ timestamp: new Date().toISOString(),
135
+ url: req?.url,
136
+ method: req?.method,
137
+ error: errorText,
138
+ blockedReason,
139
+ canceled,
140
+ requestId,
141
+ })
142
+ );
143
+ } else {
144
+ const reason = blockedReason ? ` (${blockedReason})` : "";
145
+ const canceledStr = canceled ? " [CANCELED]" : "";
146
+ console.log(
147
+ `${statusColors.error}[${formatTime()}] ✗ ${errorText}${reason}${canceledStr} - ${req?.url || "unknown"}${resetColor}`
148
+ );
149
+ }
150
+
151
+ if (requestId) {
152
+ requests.delete(requestId);
153
+ }
154
+ });
155
+
156
+ console.error(`Listening for network activity... (Ctrl+C to stop)`);
157
+
158
+ if (durationMs) {
159
+ await new Promise((r) => setTimeout(r, durationMs));
160
+ await browser.close();
161
+ } else {
162
+ // Keep running until interrupted
163
+ process.on("SIGINT", async () => {
164
+ console.error("\nStopping...");
165
+ await browser.close();
166
+ process.exit(0);
167
+ });
168
+
169
+ // Keep the process alive
170
+ await new Promise(() => {});
171
+ }