@guanzhu.me/pw-cli 0.0.9 → 0.0.11
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 +43 -23
- package/package.json +1 -1
- package/src/utils.js +24 -1
package/bin/pw-cli.js
CHANGED
|
@@ -603,17 +603,32 @@ EXAMPLES
|
|
|
603
603
|
// run-script — delegates to our CDP-based executor
|
|
604
604
|
// ---------------------------------------------------------------------------
|
|
605
605
|
async function handleRunScript(rawArgv) {
|
|
606
|
-
const { getConnection
|
|
606
|
+
const { getConnection } = require('../src/browser-manager');
|
|
607
607
|
const { execScript } = require('../src/executor');
|
|
608
608
|
|
|
609
609
|
// parse: pw-cli [global-opts] run-script <file> [script-args...]
|
|
610
610
|
const rsIdx = rawArgv.indexOf('run-script');
|
|
611
611
|
const afterRs = rawArgv.slice(rsIdx + 1);
|
|
612
612
|
const globalBefore = rawArgv.slice(0, rsIdx);
|
|
613
|
+
const options = parsePwCliGlobalOptions(globalBefore);
|
|
614
|
+
|
|
615
|
+
// Separate flags (before script path) from script path + script args
|
|
616
|
+
const flags = [];
|
|
617
|
+
let restIdx = 0;
|
|
618
|
+
for (let i = 0; i < afterRs.length; i++) {
|
|
619
|
+
if (afterRs[i].startsWith('-')) {
|
|
620
|
+
flags.push(afterRs[i]);
|
|
621
|
+
} else {
|
|
622
|
+
restIdx = i;
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
// Merge any flags after run-script into options
|
|
627
|
+
const afterOptions = parsePwCliGlobalOptions(flags);
|
|
628
|
+
Object.assign(options, afterOptions);
|
|
613
629
|
|
|
614
|
-
|
|
615
|
-
const
|
|
616
|
-
const [scriptPath, ...scriptArgs] = afterRs;
|
|
630
|
+
const positionals = afterRs.slice(restIdx);
|
|
631
|
+
const [scriptPath, ...scriptArgs] = positionals;
|
|
617
632
|
|
|
618
633
|
if (!scriptPath) {
|
|
619
634
|
process.stderr.write('pw-cli: run-script requires a script path\n\nUsage: pw-cli run-script <file.js> [args...]\n');
|
|
@@ -624,12 +639,12 @@ async function handleRunScript(rawArgv) {
|
|
|
624
639
|
process.exit(3);
|
|
625
640
|
}
|
|
626
641
|
|
|
627
|
-
const conn = await getConnection(
|
|
642
|
+
const conn = await getConnection(options);
|
|
628
643
|
try {
|
|
629
644
|
const result = await execScript(scriptPath, scriptArgs, conn);
|
|
630
645
|
if (result !== undefined) console.log(result);
|
|
631
646
|
} catch (err) {
|
|
632
|
-
process.stderr.write(`pw-cli: ${err.message}\n`);
|
|
647
|
+
process.stderr.write(`pw-cli: ${err.message || err}\n`);
|
|
633
648
|
process.exit(1);
|
|
634
649
|
} finally {
|
|
635
650
|
await conn.browser.close();
|
|
@@ -693,6 +708,12 @@ async function main() {
|
|
|
693
708
|
const rawArgv = process.argv.slice(2);
|
|
694
709
|
const { command, session } = getCommandAndSession(rawArgv);
|
|
695
710
|
|
|
711
|
+
if (hasFlag(rawArgv, '--version', '-V')) {
|
|
712
|
+
const pkg = require('../package.json');
|
|
713
|
+
console.log(`${pkg.name}@${pkg.version}`);
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
|
|
696
717
|
if (!command || command === 'help' || (rawArgv.length === 1 && hasFlag(rawArgv, '--help', '-h'))) {
|
|
697
718
|
printMainHelp();
|
|
698
719
|
return;
|
|
@@ -724,14 +745,15 @@ async function main() {
|
|
|
724
745
|
}
|
|
725
746
|
|
|
726
747
|
// Ensures a browser is reachable via CDP; if not, spawns playwright-cli open first.
|
|
748
|
+
// Returns the CDP port number.
|
|
727
749
|
async function ensureBrowserRunning() {
|
|
728
750
|
const { getPlaywrightCliCdpPort } = require('../src/browser-manager');
|
|
729
751
|
const { probeCDP } = require('../src/utils');
|
|
730
752
|
const { readState } = require('../src/state');
|
|
731
753
|
const cliPort = getPlaywrightCliCdpPort();
|
|
732
|
-
if (cliPort && await probeCDP(cliPort, 2000)) return;
|
|
754
|
+
if (cliPort && await probeCDP(cliPort, 2000)) return cliPort;
|
|
733
755
|
const state = readState();
|
|
734
|
-
if (state && await probeCDP(state.port, 2000)) return;
|
|
756
|
+
if (state && await probeCDP(state.port, 2000)) return state.port;
|
|
735
757
|
// No browser reachable — start one via playwright-cli
|
|
736
758
|
const { spawnSync } = require('child_process');
|
|
737
759
|
const res = spawnSync(process.execPath, [cliPath, 'open', '--headed', '--persistent', '--profile', DEFAULT_PROFILE], {
|
|
@@ -742,30 +764,31 @@ async function main() {
|
|
|
742
764
|
process.stderr.write('pw-cli: failed to open browser\n');
|
|
743
765
|
process.exit(res.status || 1);
|
|
744
766
|
}
|
|
767
|
+
// After spawning, re-detect the port
|
|
768
|
+
const newCliPort = getPlaywrightCliCdpPort();
|
|
769
|
+
if (newCliPort) return newCliPort;
|
|
770
|
+
const newState = readState();
|
|
771
|
+
return newState ? newState.port : null;
|
|
745
772
|
}
|
|
746
773
|
|
|
747
|
-
// ── goto: navigate the
|
|
774
|
+
// ── goto: navigate the active tab (detected via CDP /json/list) ──────────
|
|
748
775
|
if (command === 'goto') {
|
|
749
776
|
const gotoIdx = rawArgv.indexOf('goto');
|
|
750
777
|
const afterGoto = rawArgv.slice(gotoIdx + 1);
|
|
751
778
|
const rawUrl = afterGoto.find(a => !a.startsWith('-'));
|
|
752
779
|
if (rawUrl) {
|
|
753
780
|
const fullUrl = /^https?:\/\//.test(rawUrl) ? rawUrl : `https://${rawUrl}`;
|
|
754
|
-
const
|
|
781
|
+
const cdpPort = await ensureBrowserRunning();
|
|
782
|
+
const { fetchActivePageUrl } = require('../src/utils');
|
|
783
|
+
const activeUrl = await fetchActivePageUrl(cdpPort);
|
|
755
784
|
const navCode = `async (page, context) => {
|
|
756
785
|
const pages = context.pages();
|
|
757
786
|
let target = pages[pages.length - 1] || page;
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
const match = pages.find(p => p.url() === saved.url);
|
|
761
|
-
if (match) target = match;
|
|
762
|
-
} catch {}
|
|
787
|
+
${activeUrl ? `const match = pages.find(p => p.url() === ${JSON.stringify(activeUrl)});
|
|
788
|
+
if (match) target = match;` : ''}
|
|
763
789
|
await target.goto(${JSON.stringify(fullUrl)}, { waitUntil: 'domcontentloaded', timeout: 0 });
|
|
764
|
-
|
|
765
|
-
try { require('fs').writeFileSync(${activeTabFile}, JSON.stringify({ url: resultUrl })); } catch {}
|
|
766
|
-
return resultUrl;
|
|
790
|
+
return target.url();
|
|
767
791
|
}`;
|
|
768
|
-
await ensureBrowserRunning();
|
|
769
792
|
await handleRunCode(['run-code', navCode]);
|
|
770
793
|
return;
|
|
771
794
|
}
|
|
@@ -812,13 +835,10 @@ async function main() {
|
|
|
812
835
|
const rawUrlArg = afterOpen.find(a => !a.startsWith('-') && /^(https?:\/\/|[a-zA-Z0-9]([a-zA-Z0-9-]*\.)+[a-zA-Z]{2,})/.test(a));
|
|
813
836
|
const urlArg = rawUrlArg && !/^https?:\/\//.test(rawUrlArg) ? `https://${rawUrlArg}` : rawUrlArg;
|
|
814
837
|
if (urlArg) {
|
|
815
|
-
const activeTabFile = JSON.stringify(path.join(PW_CLI_DIR, 'active-tab.json'));
|
|
816
838
|
const navCode = `async page => {
|
|
817
839
|
const newPage = await page.context().newPage();
|
|
818
840
|
await newPage.goto(${JSON.stringify(urlArg)}, { waitUntil: 'domcontentloaded', timeout: 0 });
|
|
819
|
-
|
|
820
|
-
try { require('fs').writeFileSync(${activeTabFile}, JSON.stringify({ url: resultUrl })); } catch {}
|
|
821
|
-
return resultUrl;
|
|
841
|
+
return newPage.url();
|
|
822
842
|
}`;
|
|
823
843
|
await ensureBrowserRunning();
|
|
824
844
|
await handleRunCode(['run-code', navCode]);
|
package/package.json
CHANGED
package/src/utils.js
CHANGED
|
@@ -52,4 +52,27 @@ function sleep(ms) {
|
|
|
52
52
|
return new Promise(r => setTimeout(r, ms));
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Fetch the URL of the most recently active page tab via Chrome's /json/list endpoint.
|
|
57
|
+
* Chrome returns targets ordered by most-recently-activated first.
|
|
58
|
+
* Returns the URL string, or null if no page target found.
|
|
59
|
+
*/
|
|
60
|
+
function fetchActivePageUrl(port) {
|
|
61
|
+
return new Promise(resolve => {
|
|
62
|
+
const req = http.get(`http://127.0.0.1:${port}/json/list`, { timeout: 3000 }, res => {
|
|
63
|
+
let data = '';
|
|
64
|
+
res.on('data', chunk => { data += chunk; });
|
|
65
|
+
res.on('end', () => {
|
|
66
|
+
try {
|
|
67
|
+
const targets = JSON.parse(data);
|
|
68
|
+
const page = targets.find(t => t.type === 'page');
|
|
69
|
+
resolve(page ? page.url : null);
|
|
70
|
+
} catch { resolve(null); }
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
req.on('error', () => resolve(null));
|
|
74
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { readStdin, die, probeCDP, findFreePort, sleep, fetchActivePageUrl };
|