@guanzhu.me/pw-cli 0.0.15 → 0.0.16
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 +37 -0
- package/package.json +1 -1
- package/src/fast-send.js +123 -0
package/bin/pw-cli.js
CHANGED
|
@@ -771,6 +771,43 @@ async function main() {
|
|
|
771
771
|
return;
|
|
772
772
|
}
|
|
773
773
|
|
|
774
|
+
// ── Fast path: send command directly to playwright-cli daemon socket ────
|
|
775
|
+
// Skip heavy setup (npm root -g, require playwright, CDP probes) entirely.
|
|
776
|
+
// Only for commands that the daemon handles AND don't need local preprocessing.
|
|
777
|
+
if (command && !MGMT_COMMANDS.has(command)) {
|
|
778
|
+
// Build the args array the daemon expects: strip session flags, keep command + args
|
|
779
|
+
let fastArgs = [...rawArgv];
|
|
780
|
+
// Remove session flags (-s xxx / --session xxx / --session=xxx)
|
|
781
|
+
fastArgs = fastArgs.filter((a, i, arr) => {
|
|
782
|
+
if (a === '-s' || a === '--session') { arr[i + 1] = undefined; return false; }
|
|
783
|
+
if (a === undefined) return false;
|
|
784
|
+
if (a.startsWith('-s=') || a.startsWith('--session=')) return false;
|
|
785
|
+
return true;
|
|
786
|
+
}).filter(Boolean);
|
|
787
|
+
|
|
788
|
+
// Apply XPath conversion if needed
|
|
789
|
+
fastArgs = convertXPathCommand(fastArgs);
|
|
790
|
+
|
|
791
|
+
// Handle run-code wrapping for XPath-converted commands
|
|
792
|
+
if (fastArgs[0] === 'run-code' && fastArgs.length > 1) {
|
|
793
|
+
const code = fastArgs.slice(1).join(' ');
|
|
794
|
+
fastArgs = ['run-code', wrapCodeIfNeeded(code)];
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const { sendCommand } = require('../src/fast-send');
|
|
798
|
+
const result = await sendCommand(fastArgs, session);
|
|
799
|
+
if (result !== null) {
|
|
800
|
+
// Daemon responded — use its result
|
|
801
|
+
if (result.isError) {
|
|
802
|
+
process.stderr.write(`${result.text}\n`);
|
|
803
|
+
process.exit(1);
|
|
804
|
+
}
|
|
805
|
+
if (result.text) process.stdout.write(result.text + '\n');
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
// result === null means daemon not running — fall through to full path
|
|
809
|
+
}
|
|
810
|
+
|
|
774
811
|
// ── From here on: delegate to playwright-cli (with enhancements) ─────────
|
|
775
812
|
const cliPath = findPlaywrightCli();
|
|
776
813
|
if (!cliPath) {
|
package/package.json
CHANGED
package/src/fast-send.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const net = require('net');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const crypto = require('crypto');
|
|
8
|
+
|
|
9
|
+
const HOME_DIR = os.homedir();
|
|
10
|
+
const PW_CLI_DIR = path.join(HOME_DIR, '.pw-cli');
|
|
11
|
+
const WORKSPACE_HASH = crypto.createHash('sha1')
|
|
12
|
+
.update(PW_CLI_DIR)
|
|
13
|
+
.digest('hex')
|
|
14
|
+
.substring(0, 16);
|
|
15
|
+
|
|
16
|
+
function getSocketPath(sessionName = 'default') {
|
|
17
|
+
const socketName = `${sessionName}.sock`;
|
|
18
|
+
if (os.platform() === 'win32')
|
|
19
|
+
return `\\\\.\\pipe\\${WORKSPACE_HASH}-${socketName}`;
|
|
20
|
+
const socketsDir = process.env.PLAYWRIGHT_DAEMON_SOCKETS_DIR || path.join(os.tmpdir(), 'playwright-cli');
|
|
21
|
+
return path.join(socketsDir, WORKSPACE_HASH, socketName);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getVersion() {
|
|
25
|
+
// Read version from session file (already written by playwright-cli daemon)
|
|
26
|
+
const sessionFile = path.join(
|
|
27
|
+
os.platform() === 'win32'
|
|
28
|
+
? path.join(process.env.LOCALAPPDATA || path.join(HOME_DIR, 'AppData', 'Local'), 'ms-playwright', 'daemon')
|
|
29
|
+
: os.platform() === 'darwin'
|
|
30
|
+
? path.join(HOME_DIR, 'Library', 'Caches', 'ms-playwright', 'daemon')
|
|
31
|
+
: path.join(process.env.XDG_CACHE_HOME || path.join(HOME_DIR, '.cache'), 'ms-playwright', 'daemon'),
|
|
32
|
+
WORKSPACE_HASH,
|
|
33
|
+
'default.session'
|
|
34
|
+
);
|
|
35
|
+
try {
|
|
36
|
+
const session = JSON.parse(fs.readFileSync(sessionFile, 'utf8'));
|
|
37
|
+
return session.version;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Convert a string array to minimist-style object.
|
|
45
|
+
* The daemon expects { _: [cmd, arg1, ...], flagName: value, ... }.
|
|
46
|
+
*/
|
|
47
|
+
function toMinimistArgs(argv) {
|
|
48
|
+
const result = { _: [] };
|
|
49
|
+
for (let i = 0; i < argv.length; i++) {
|
|
50
|
+
const arg = argv[i];
|
|
51
|
+
if (arg.startsWith('--')) {
|
|
52
|
+
const key = arg.slice(2);
|
|
53
|
+
const next = argv[i + 1];
|
|
54
|
+
if (next && !next.startsWith('-')) {
|
|
55
|
+
result[key] = next;
|
|
56
|
+
i++;
|
|
57
|
+
} else {
|
|
58
|
+
result[key] = true;
|
|
59
|
+
}
|
|
60
|
+
} else if (arg.startsWith('-') && arg.length === 2) {
|
|
61
|
+
const key = arg.slice(1);
|
|
62
|
+
const next = argv[i + 1];
|
|
63
|
+
if (next && !next.startsWith('-')) {
|
|
64
|
+
result[key] = next;
|
|
65
|
+
i++;
|
|
66
|
+
} else {
|
|
67
|
+
result[key] = true;
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
result._.push(arg);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Send a command directly to the playwright-cli daemon socket.
|
|
78
|
+
* Returns { text, isError } or null (daemon not running).
|
|
79
|
+
*/
|
|
80
|
+
function sendCommand(args, sessionName = 'default') {
|
|
81
|
+
const socketPath = getSocketPath(sessionName);
|
|
82
|
+
const version = getVersion();
|
|
83
|
+
if (!version) return Promise.resolve(null);
|
|
84
|
+
|
|
85
|
+
const minimistArgs = toMinimistArgs(args);
|
|
86
|
+
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
const socket = net.createConnection(socketPath, () => {
|
|
89
|
+
const message = JSON.stringify({
|
|
90
|
+
id: 1,
|
|
91
|
+
method: 'run',
|
|
92
|
+
params: { args: minimistArgs, cwd: process.cwd() },
|
|
93
|
+
version,
|
|
94
|
+
}) + '\n';
|
|
95
|
+
socket.write(message);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
let buf = '';
|
|
99
|
+
socket.on('data', chunk => {
|
|
100
|
+
buf += chunk.toString();
|
|
101
|
+
const nlIdx = buf.indexOf('\n');
|
|
102
|
+
if (nlIdx === -1) return;
|
|
103
|
+
const line = buf.slice(0, nlIdx);
|
|
104
|
+
clearTimeout(timer);
|
|
105
|
+
socket.destroy();
|
|
106
|
+
try {
|
|
107
|
+
const resp = JSON.parse(line);
|
|
108
|
+
if (resp.error) {
|
|
109
|
+
reject(new Error(resp.error));
|
|
110
|
+
} else {
|
|
111
|
+
resolve(resp.result);
|
|
112
|
+
}
|
|
113
|
+
} catch (e) {
|
|
114
|
+
reject(new Error('Invalid daemon response'));
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
socket.on('error', () => { clearTimeout(timer); resolve(null); }); // connection failed = daemon not running
|
|
119
|
+
const timer = setTimeout(() => { socket.destroy(); resolve(null); }, 3000);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = { sendCommand, getSocketPath, getVersion };
|