@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guanzhu.me/pw-cli",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "description": "Persistent Playwright browser CLI with headed defaults, profile support, queueing, and script execution",
5
5
  "bin": {
6
6
  "pw-cli": "./bin/pw-cli.js"
@@ -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
- let playwright;
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 contexts = browser.contexts();
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 contexts = browser.contexts();
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
- async function cmdRunScript(rest, opts) {
55
- const [scriptPath, ...scriptArgs] = rest;
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':
@@ -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.message}\n`);
90
+ process.stdout.write(`ERROR:${formatLaunchError(e)}\n`);
63
91
  process.exit(1);
64
92
  }
65
93
  })();