-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcdp-cli.js
More file actions
141 lines (124 loc) · 5.28 KB
/
Copy pathcdp-cli.js
File metadata and controls
141 lines (124 loc) · 5.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/env node
'use strict';
const fs = require('fs');
const BASE = process.env.CDP_BRIDGE_BASE || 'http://127.0.0.1:1232';
async function get(path) {
const r = await fetch(`${BASE}${path}`);
return r.json();
}
async function post(path, body) {
const r = await fetch(`${BASE}${path}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
return r.json();
}
function readStdin() {
return new Promise((resolve) => {
const chunks = [];
process.stdin.on('data', (c) => chunks.push(c));
process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf8').trimEnd()));
});
}
// Resolves a CLI arg that may be:
// '-' → read raw text from stdin, then JSON.parse
// '@<path>' → read file at <path>, then JSON.parse
// else → JSON.parse directly (or {} if missing)
async function resolveJSON(str) {
if (!str) return {};
if (str === '-') return JSON.parse(await readStdin());
if (str.startsWith('@')) return JSON.parse(fs.readFileSync(str.slice(1), 'utf8'));
return JSON.parse(str);
}
// Same as resolveJSON but returns the raw string (for JS expressions in --eval).
async function resolveText(str) {
if (!str) return '';
if (str === '-') return readStdin();
if (str.startsWith('@')) return fs.readFileSync(str.slice(1), 'utf8').trimEnd();
return str;
}
async function main() {
const args = process.argv.slice(2);
const cmd = args[0];
if (cmd === '--tabs') {
const r = await get('/tabs');
if (r.error) { console.error('Error:', r.error); process.exit(1); }
console.log(r.result.map(t => `${t.tabId}: ${t.title}`).join('\n'));
} else if (cmd === '--exec') {
const [tabId, method, paramsArg] = args.slice(1);
const r = await post('/command', {
tabId: parseInt(tabId),
method,
params: await resolveJSON(paramsArg),
});
if (r.error) { console.error('Error:', r.error); process.exit(1); }
console.log(JSON.stringify(r.result, null, 2));
} else if (cmd === '--eval') {
const [tabId, exprArg, ...rest] = args.slice(1);
let expression;
if (exprArg === '-' || (exprArg && exprArg.startsWith('@'))) {
expression = await resolveText(exprArg);
} else {
expression = [exprArg, ...rest].filter(Boolean).join(' ');
}
const r = await post('/command', {
tabId: parseInt(tabId),
method: 'Runtime.evaluate',
params: { expression, returnByValue: true },
});
if (r.error) { console.error('Error:', r.error); process.exit(1); }
console.log(r.result?.result?.value ?? JSON.stringify(r.result));
} else if (cmd === '--palette') {
const r = await get('/palette');
if (r.error) { console.error('Error:', r.error); process.exit(1); }
for (const a of r.actions) {
const argNames = (a.args || []).map(x => (x.required ? x.name : x.name + '?')).join(', ');
console.log(`${a.name}(${argNames})`);
console.log(` ${a.summary}`);
}
} else if (cmd === '--do') {
const [tabId, action, argsArg] = args.slice(1);
const r = await post('/action', {
tabId: parseInt(tabId),
action,
args: await resolveJSON(argsArg),
});
if (r.error) { console.error('Error:', r.error); process.exit(1); }
console.log(JSON.stringify(r.result, null, 2));
} else if (cmd === '--network') {
const [tabId, filtersArg] = args.slice(1);
const r = await post('/network', {
tabId: parseInt(tabId),
...(await resolveJSON(filtersArg)),
});
if (r.error) { console.error('Error:', r.error); process.exit(1); }
console.log(JSON.stringify(r.result, null, 2));
} else if (cmd === '--cookies') {
const urls = args.slice(1);
const r = await post('/cookies', { urls: urls.length ? urls : ['https://x.com'] });
if (r.error) { console.error('Error:', r.error); process.exit(1); }
console.log(JSON.stringify(r.result, null, 2));
} else if (cmd === '--network-clear') {
const [tabId] = args.slice(1);
const r = await post('/network/clear', { tabId: tabId == null ? undefined : parseInt(tabId) });
if (r.error) { console.error('Error:', r.error); process.exit(1); }
console.log(JSON.stringify(r.result, null, 2));
} else {
console.log('Usage:');
console.log(' cdp-cli --tabs List tabs');
console.log(' cdp-cli --palette List palette commands');
console.log(' cdp-cli --do <id> <action> [JSON|-|@file] Run a palette action');
console.log(' cdp-cli --exec <id> <method> [JSON|-|@file] Run raw CDP method');
console.log(' cdp-cli --eval <id> <js|-|@file> Run JS in tab');
console.log(' cdp-cli --cookies [url...] Dump all cookies (incl. HttpOnly)');
console.log(' cdp-cli --network <id> [JSON|-|@file] Show recorded network requests');
console.log(' cdp-cli --network-clear [id] Clear recorded network requests');
console.log('');
console.log(' JSON arg formats:');
console.log(" inline: '{\"url\":\"https://...\"}' (simple cases only on Windows)");
console.log(' file: @args.json (avoids all shell escaping)');
console.log(' stdin: - (pipe JSON via PowerShell heredoc)');
}
}
main().catch(err => { console.error(err.message); process.exit(1); });