@guanzhu.me/pw-cli 0.0.12 → 0.0.14
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/bin/pw-cli.js +22 -13
- package/package.json +1 -1
- package/src/browser-manager.js +49 -16
- package/src/cli.js +16 -7
- package/src/launch-daemon.js +37 -9
package/bin/pw-cli.js
CHANGED
|
@@ -291,6 +291,20 @@ run-script example:
|
|
|
291
291
|
`.trim() + '\n');
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
+
function getRunScriptHelpText() {
|
|
295
|
+
return `Usage:
|
|
296
|
+
pw-cli run-script <file.js> [args...]
|
|
297
|
+
|
|
298
|
+
What the script receives:
|
|
299
|
+
- Playwright globals: page, context, browser, playwright
|
|
300
|
+
- Script args array: args
|
|
301
|
+
- CommonJS globals: require, module, exports, __filename, __dirname
|
|
302
|
+
|
|
303
|
+
Example:
|
|
304
|
+
pw-cli run-script ./scripts/extract-links.js --url https://example.com --output links.json
|
|
305
|
+
`;
|
|
306
|
+
}
|
|
307
|
+
|
|
294
308
|
// Management commands that don't need a running browser
|
|
295
309
|
const MGMT_COMMANDS = new Set([
|
|
296
310
|
'open', 'close', 'list', 'kill-all', 'close-all', 'delete-data',
|
|
@@ -644,19 +658,7 @@ async function handleRunScript(rawArgv) {
|
|
|
644
658
|
const [scriptPath, ...scriptArgs] = positionals;
|
|
645
659
|
|
|
646
660
|
if (!scriptPath) {
|
|
647
|
-
process.stderr.write(`pw-cli: run-script requires a script path
|
|
648
|
-
|
|
649
|
-
Usage:
|
|
650
|
-
pw-cli run-script <file.js> [args...]
|
|
651
|
-
|
|
652
|
-
What the script receives:
|
|
653
|
-
- Playwright globals: page, context, browser, playwright
|
|
654
|
-
- Script args array: args
|
|
655
|
-
- CommonJS globals: require, module, exports, __filename, __dirname
|
|
656
|
-
|
|
657
|
-
Example:
|
|
658
|
-
pw-cli run-script ./scripts/extract-links.js --url https://example.com --output links.json
|
|
659
|
-
`);
|
|
661
|
+
process.stderr.write(`pw-cli: run-script requires a script path\n\n${getRunScriptHelpText()}`);
|
|
660
662
|
process.exit(1);
|
|
661
663
|
}
|
|
662
664
|
if (!fs.existsSync(path.resolve(scriptPath))) {
|
|
@@ -744,6 +746,13 @@ async function main() {
|
|
|
744
746
|
return;
|
|
745
747
|
}
|
|
746
748
|
|
|
749
|
+
if (command === 'run-scirpt') {
|
|
750
|
+
process.stderr.write('pw-cli: unknown command: run-scirpt\n');
|
|
751
|
+
process.stderr.write('Did you mean: run-script?\n\n');
|
|
752
|
+
process.stdout.write(getRunScriptHelpText());
|
|
753
|
+
process.exit(1);
|
|
754
|
+
}
|
|
755
|
+
|
|
747
756
|
// ── queue: batch actions and run them together ────────────────────────────
|
|
748
757
|
if (command === 'queue') {
|
|
749
758
|
await handleQueue(rawArgv);
|
package/package.json
CHANGED
package/src/browser-manager.js
CHANGED
|
@@ -5,8 +5,9 @@ const path = require('path');
|
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const crypto = require('crypto');
|
|
8
|
+
const { execSync } = require('child_process');
|
|
8
9
|
const { readState, writeState, clearState, getProfileDir } = require('./state');
|
|
9
|
-
const { probeCDP, findFreePort, sleep } = require('./utils');
|
|
10
|
+
const { probeCDP, findFreePort, sleep, fetchActivePageUrl } = require('./utils');
|
|
10
11
|
|
|
11
12
|
const DAEMON_SCRIPT = path.join(__dirname, 'launch-daemon.js');
|
|
12
13
|
|
|
@@ -43,6 +44,49 @@ function getPlaywrightCliCdpPort(sessionName = 'default') {
|
|
|
43
44
|
return session?.resolvedConfig?.browser?.launchOptions?.cdpPort || null;
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
function loadPlaywright() {
|
|
48
|
+
try {
|
|
49
|
+
return require('playwright');
|
|
50
|
+
} catch {}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const globalRoot = execSync('npm root -g', {
|
|
54
|
+
encoding: 'utf8',
|
|
55
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
56
|
+
}).trim();
|
|
57
|
+
const cliPlaywrightPath = path.join(globalRoot, '@playwright', 'cli', 'node_modules', 'playwright');
|
|
58
|
+
return require(cliPlaywrightPath);
|
|
59
|
+
} catch {}
|
|
60
|
+
|
|
61
|
+
throw new Error('playwright is not installed. Run: npm install -g playwright');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function pickPage(pages, activeUrl) {
|
|
65
|
+
if (!pages || pages.length === 0) return null;
|
|
66
|
+
if (activeUrl) {
|
|
67
|
+
const matchingPages = pages.filter(page => {
|
|
68
|
+
try {
|
|
69
|
+
return page.url() === activeUrl;
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
if (matchingPages.length > 0) {
|
|
75
|
+
return matchingPages[matchingPages.length - 1];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return pages[pages.length - 1];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function resolveContextAndPage(browser, cdpPort) {
|
|
82
|
+
const contexts = browser.contexts();
|
|
83
|
+
const context = contexts.length > 0 ? contexts[0] : await browser.newContext();
|
|
84
|
+
const pages = context.pages();
|
|
85
|
+
const activeUrl = cdpPort ? await fetchActivePageUrl(cdpPort) : null;
|
|
86
|
+
const page = pickPage(pages, activeUrl) || await context.newPage();
|
|
87
|
+
return { context, page };
|
|
88
|
+
}
|
|
89
|
+
|
|
46
90
|
// ---------------------------------------------------------------------------
|
|
47
91
|
// Our own CDP-based browser launcher (fallback when playwright-cli not running)
|
|
48
92
|
// ---------------------------------------------------------------------------
|
|
@@ -104,12 +148,7 @@ async function launchBrowser({ headless = false, profile = 'default', port: pref
|
|
|
104
148
|
// getConnection — tries playwright-cli browser first, then our own
|
|
105
149
|
// ---------------------------------------------------------------------------
|
|
106
150
|
async function getConnection({ headless = false, profile = 'default', port: preferredPort = 9223 } = {}) {
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
playwright = require('playwright');
|
|
110
|
-
} catch {
|
|
111
|
-
throw new Error('playwright is not installed. Run: npm install -g playwright');
|
|
112
|
-
}
|
|
151
|
+
const playwright = loadPlaywright();
|
|
113
152
|
|
|
114
153
|
// 1. Try to reuse playwright-cli's browser via its CDP port
|
|
115
154
|
const cliCdpPort = getPlaywrightCliCdpPort();
|
|
@@ -118,10 +157,7 @@ async function getConnection({ headless = false, profile = 'default', port: pref
|
|
|
118
157
|
if (alive) {
|
|
119
158
|
try {
|
|
120
159
|
const browser = await playwright.chromium.connectOverCDP(`http://127.0.0.1:${cliCdpPort}`);
|
|
121
|
-
const
|
|
122
|
-
const context = contexts.length > 0 ? contexts[0] : await browser.newContext();
|
|
123
|
-
const pages = context.pages();
|
|
124
|
-
const page = pages.length > 0 ? pages[0] : await context.newPage();
|
|
160
|
+
const { context, page } = await resolveContextAndPage(browser, cliCdpPort);
|
|
125
161
|
return { browser, context, page, playwright };
|
|
126
162
|
} catch {
|
|
127
163
|
// fall through to own browser
|
|
@@ -150,10 +186,7 @@ async function getConnection({ headless = false, profile = 'default', port: pref
|
|
|
150
186
|
}
|
|
151
187
|
|
|
152
188
|
const browser = await playwright.chromium.connectOverCDP(cdpUrl);
|
|
153
|
-
const
|
|
154
|
-
const context = contexts.length > 0 ? contexts[0] : await browser.newContext();
|
|
155
|
-
const pages = context.pages();
|
|
156
|
-
const page = pages.length > 0 ? pages[0] : await context.newPage();
|
|
189
|
+
const { context, page } = await resolveContextAndPage(browser, state ? state.port : null);
|
|
157
190
|
|
|
158
191
|
return { browser, context, page, playwright };
|
|
159
192
|
}
|
|
@@ -175,4 +208,4 @@ async function killBrowser() {
|
|
|
175
208
|
return true;
|
|
176
209
|
}
|
|
177
210
|
|
|
178
|
-
module.exports = { getConnection, killBrowser, getPlaywrightCliCdpPort };
|
|
211
|
+
module.exports = { getConnection, killBrowser, getPlaywrightCliCdpPort, pickPage };
|
package/src/cli.js
CHANGED
|
@@ -51,12 +51,8 @@ async function cmdRunCode(rest, opts) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (!scriptPath) {
|
|
57
|
-
die(`No script path provided.
|
|
58
|
-
|
|
59
|
-
Usage:
|
|
54
|
+
function getRunScriptHelp() {
|
|
55
|
+
return `Usage:
|
|
60
56
|
pw-cli run-script <file.js> [args...]
|
|
61
57
|
|
|
62
58
|
What the script receives:
|
|
@@ -65,7 +61,13 @@ What the script receives:
|
|
|
65
61
|
- CommonJS globals: require, module, exports, __filename, __dirname
|
|
66
62
|
|
|
67
63
|
Example:
|
|
68
|
-
pw-cli run-script ./scripts/extract-links.js --url https://example.com --output links.json
|
|
64
|
+
pw-cli run-script ./scripts/extract-links.js --url https://example.com --output links.json`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function cmdRunScript(rest, opts) {
|
|
68
|
+
const [scriptPath, ...scriptArgs] = rest;
|
|
69
|
+
if (!scriptPath) {
|
|
70
|
+
die(`No script path provided.\n\n${getRunScriptHelp()}`);
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
const conn = await getConnection(opts);
|
|
@@ -156,6 +158,13 @@ async function run(argv) {
|
|
|
156
158
|
return;
|
|
157
159
|
}
|
|
158
160
|
|
|
161
|
+
if (command === 'run-scirpt') {
|
|
162
|
+
console.error('Unknown command: run-scirpt');
|
|
163
|
+
console.error('Did you mean: run-script?\n');
|
|
164
|
+
console.log(getRunScriptHelp());
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
|
|
159
168
|
try {
|
|
160
169
|
switch (command) {
|
|
161
170
|
case 'run-code':
|
package/src/launch-daemon.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
// Args: --profile-dir <dir> --port <port> [--headless]
|
|
7
7
|
|
|
8
8
|
const args = process.argv.slice(2);
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { execSync } = require('child_process');
|
|
9
11
|
|
|
10
12
|
function getArg(name) {
|
|
11
13
|
const i = args.indexOf(name);
|
|
@@ -16,22 +18,48 @@ const profileDir = getArg('--profile-dir');
|
|
|
16
18
|
const port = parseInt(getArg('--port') || '9222', 10);
|
|
17
19
|
const headless = args.includes('--headless');
|
|
18
20
|
|
|
21
|
+
function loadPlaywright() {
|
|
22
|
+
try {
|
|
23
|
+
return require('playwright');
|
|
24
|
+
} catch {}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const globalRoot = execSync('npm root -g', {
|
|
28
|
+
encoding: 'utf8',
|
|
29
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
30
|
+
}).trim();
|
|
31
|
+
const cliPlaywrightPath = path.join(globalRoot, '@playwright', 'cli', 'node_modules', 'playwright');
|
|
32
|
+
return require(cliPlaywrightPath);
|
|
33
|
+
} catch {}
|
|
34
|
+
|
|
35
|
+
throw new Error('playwright not found - run: npm install -g playwright');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function formatLaunchError(error) {
|
|
39
|
+
const message = error && error.message ? error.message : String(error);
|
|
40
|
+
if (message.includes("Executable doesn't exist")) {
|
|
41
|
+
return [
|
|
42
|
+
'Playwright browser executable is not installed for the selected engine.',
|
|
43
|
+
'pw-cli now prefers your local Chrome install for fallback launches.',
|
|
44
|
+
'Run one of the following commands first:',
|
|
45
|
+
' pw-cli open',
|
|
46
|
+
' pw-cli install-browser',
|
|
47
|
+
' npx playwright install chromium',
|
|
48
|
+
].join('\n');
|
|
49
|
+
}
|
|
50
|
+
return message;
|
|
51
|
+
}
|
|
52
|
+
|
|
19
53
|
if (!profileDir) {
|
|
20
54
|
process.stderr.write('ERROR:missing --profile-dir\n');
|
|
21
55
|
process.exit(1);
|
|
22
56
|
}
|
|
23
57
|
|
|
24
58
|
(async () => {
|
|
25
|
-
let playwright;
|
|
26
|
-
try {
|
|
27
|
-
playwright = require('playwright');
|
|
28
|
-
} catch (e) {
|
|
29
|
-
process.stdout.write(`ERROR:playwright not found - run: npm install -g playwright\n`);
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
59
|
try {
|
|
60
|
+
const playwright = loadPlaywright();
|
|
34
61
|
const context = await playwright.chromium.launchPersistentContext(profileDir, {
|
|
62
|
+
channel: 'chrome',
|
|
35
63
|
headless,
|
|
36
64
|
args: [`--remote-debugging-port=${port}`],
|
|
37
65
|
ignoreDefaultArgs: ['--enable-automation'],
|
|
@@ -59,7 +87,7 @@ if (!profileDir) {
|
|
|
59
87
|
// Prevent premature exit
|
|
60
88
|
process.stdin.resume();
|
|
61
89
|
} catch (e) {
|
|
62
|
-
process.stdout.write(`ERROR:${e
|
|
90
|
+
process.stdout.write(`ERROR:${formatLaunchError(e)}\n`);
|
|
63
91
|
process.exit(1);
|
|
64
92
|
}
|
|
65
93
|
})();
|