@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guanzhu.me/pw-cli",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
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"
@@ -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 };