playwright-repl 0.3.0 → 0.7.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/src/repl.mjs DELETED
@@ -1,600 +0,0 @@
1
- /**
2
- * Main REPL loop.
3
- *
4
- * Handles readline, command queue, meta-commands, and session management.
5
- */
6
-
7
- import readline from 'node:readline';
8
- import path from 'node:path';
9
- import fs from 'node:fs';
10
- import { execSync } from 'node:child_process';
11
-
12
- import { replVersion } from './resolve.mjs';
13
- import { DaemonConnection } from './connection.mjs';
14
- import { socketPath, daemonProfilesDir, isDaemonRunning, startDaemon } from './workspace.mjs';
15
- import { parseInput, ALIASES, ALL_COMMANDS } from './parser.mjs';
16
- import { SessionManager } from './recorder.mjs';
17
- import { buildCompletionItems } from './completion-data.mjs';
18
- import {
19
- buildRunCode, verifyText, verifyElement, verifyValue, verifyList,
20
- actionByText, fillByText, selectByText, checkByText, uncheckByText,
21
- } from './page-scripts.mjs';
22
- import { c } from './colors.mjs';
23
-
24
- // ─── Response filtering ─────────────────────────────────────────────────────
25
-
26
- export function filterResponse(text) {
27
- const sections = text.split(/^### /m).slice(1);
28
- const kept = [];
29
- for (const section of sections) {
30
- const newline = section.indexOf('\n');
31
- if (newline === -1) continue;
32
- const title = section.substring(0, newline).trim();
33
- const content = section.substring(newline + 1).trim();
34
- if (title === 'Error')
35
- kept.push(`${c.red}${content}${c.reset}`);
36
- else if (title === 'Result' || title === 'Modal state')
37
- kept.push(content);
38
- }
39
- return kept.length > 0 ? kept.join('\n') : null;
40
- }
41
-
42
- // ─── Meta-command handlers ──────────────────────────────────────────────────
43
-
44
- export function showHelp() {
45
- console.log(`\n${c.bold}Available commands:${c.reset}`);
46
- const categories = {
47
- 'Navigation': ['open', 'goto', 'go-back', 'go-forward', 'reload'],
48
- 'Interaction': ['click', 'dblclick', 'fill', 'type', 'press', 'hover', 'select', 'check', 'uncheck', 'drag'],
49
- 'Inspection': ['snapshot', 'screenshot', 'eval', 'console', 'network', 'run-code'],
50
- 'Tabs': ['tab-list', 'tab-new', 'tab-close', 'tab-select'],
51
- 'Storage': ['cookie-list', 'cookie-get', 'localstorage-list', 'localstorage-get', 'state-save', 'state-load'],
52
- };
53
- for (const [cat, cmds] of Object.entries(categories)) {
54
- console.log(` ${c.bold}${cat}:${c.reset} ${cmds.join(', ')}`);
55
- }
56
- console.log(`\n ${c.dim}Use .aliases for shortcuts, or type any command with --help${c.reset}`);
57
- console.log(`\n${c.bold}REPL meta-commands:${c.reset}`);
58
- console.log(` .aliases Show command aliases`);
59
- console.log(` .status Show connection status`);
60
- console.log(` .reconnect Reconnect to daemon`);
61
- console.log(` .record [filename] Start recording commands`);
62
- console.log(` .save Stop recording and save`);
63
- console.log(` .pause Pause/resume recording`);
64
- console.log(` .discard Discard recording`);
65
- console.log(` .replay <filename> Replay a recorded session`);
66
- console.log(` .exit Exit REPL\n`);
67
- }
68
-
69
- export function showAliases() {
70
- console.log(`\n${c.bold}Command aliases:${c.reset}`);
71
- const groups = {};
72
- for (const [alias, cmd] of Object.entries(ALIASES)) {
73
- if (!groups[cmd]) groups[cmd] = [];
74
- groups[cmd].push(alias);
75
- }
76
- for (const [cmd, aliases] of Object.entries(groups).sort()) {
77
- console.log(` ${c.cyan}${aliases.join(', ')}${c.reset} → ${cmd}`);
78
- }
79
- console.log();
80
- }
81
-
82
- export function showStatus(ctx) {
83
- const { conn, sessionName, session } = ctx;
84
- console.log(`Connected: ${conn.connected ? `${c.green}yes${c.reset}` : `${c.red}no${c.reset}`}`);
85
- console.log(`Session: ${sessionName}`);
86
- console.log(`Socket: ${socketPath(sessionName)}`);
87
- console.log(`Commands sent: ${ctx.commandCount}`);
88
- console.log(`Mode: ${session.mode}`);
89
- if (session.mode === 'recording' || session.mode === 'paused') {
90
- console.log(`Recording: ${c.red}⏺${c.reset} ${session.recordingFilename} (${session.recordedCount} commands${session.mode === 'paused' ? ', paused' : ''})`);
91
- }
92
- }
93
-
94
- // ─── Session-level commands ─────────────────────────────────────────────────
95
-
96
- export async function handleKillAll(ctx) {
97
- try {
98
- let killed = 0;
99
- if (process.platform === 'win32') {
100
- let result = '';
101
- try {
102
- result = execSync(
103
- 'powershell -NoProfile -Command "Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like \'*run-mcp-server*\' -and $_.CommandLine -like \'*--daemon-session*\' } | Select-Object -ExpandProperty ProcessId"',
104
- { encoding: 'utf-8' }
105
- );
106
- } catch (err) {
107
- result = err.stdout || '';
108
- }
109
- for (const line of result.trim().split(/\r?\n/)) {
110
- const pid = line.trim();
111
- if (/^\d+$/.test(pid)) {
112
- try { process.kill(parseInt(pid, 10)); killed++; } catch {}
113
- }
114
- }
115
- } else {
116
- const result = execSync('ps aux', { encoding: 'utf-8' });
117
- for (const ln of result.split('\n')) {
118
- if (ln.includes('run-mcp-server') && ln.includes('--daemon-session')) {
119
- const pid = ln.trim().split(/\s+/)[1];
120
- if (pid && /^\d+$/.test(pid)) {
121
- try { process.kill(parseInt(pid, 10), 'SIGKILL'); killed++; } catch {}
122
- }
123
- }
124
- }
125
- }
126
- console.log(killed > 0
127
- ? `${c.green}✓${c.reset} Killed ${killed} daemon process${killed === 1 ? '' : 'es'}`
128
- : `${c.dim}No daemon processes found${c.reset}`);
129
- ctx.conn.close();
130
- } catch (err) {
131
- console.error(`${c.red}Error:${c.reset} ${err.message}`);
132
- }
133
- }
134
-
135
- export async function handleClose(ctx) {
136
- try {
137
- await ctx.conn.send('stop', {});
138
- console.log(`${c.green}✓${c.reset} Daemon stopped`);
139
- ctx.conn.close();
140
- } catch (err) {
141
- console.error(`${c.red}Error:${c.reset} ${err.message}`);
142
- }
143
- }
144
-
145
- // ─── Session meta-commands (.record, .save, .pause, .discard, .replay) ──────
146
-
147
- export function handleSessionCommand(ctx, line) {
148
- const { session } = ctx;
149
-
150
- if (line.startsWith('.record')) {
151
- const filename = line.split(/\s+/)[1] || undefined;
152
- const file = session.startRecording(filename);
153
- console.log(`${c.red}⏺${c.reset} Recording to ${c.bold}${file}${c.reset}`);
154
- ctx.rl.setPrompt(promptStr(ctx));
155
- return true;
156
- }
157
-
158
- if (line === '.save') {
159
- const { filename, count } = session.save();
160
- console.log(`${c.green}✓${c.reset} Saved ${count} commands to ${c.bold}${filename}${c.reset}`);
161
- ctx.rl.setPrompt(promptStr(ctx));
162
- return true;
163
- }
164
-
165
- if (line === '.pause') {
166
- const paused = session.togglePause();
167
- console.log(paused ? `${c.yellow}⏸${c.reset} Recording paused` : `${c.red}⏺${c.reset} Recording resumed`);
168
- return true;
169
- }
170
-
171
- if (line === '.discard') {
172
- session.discard();
173
- console.log(`${c.yellow}Recording discarded${c.reset}`);
174
- ctx.rl.setPrompt(promptStr(ctx));
175
- return true;
176
- }
177
-
178
- return false;
179
- }
180
-
181
- // ─── Process a single line ──────────────────────────────────────────────────
182
-
183
- export async function processLine(ctx, line) {
184
- line = line.trim();
185
- if (!line) return;
186
-
187
- // ── Meta-commands ────────────────────────────────────────────────
188
-
189
- if (line === '.help' || line === '?') return showHelp();
190
- if (line === '.aliases') return showAliases();
191
- if (line === '.status') return showStatus(ctx);
192
-
193
- if (line === '.exit' || line === '.quit') {
194
- ctx.conn.close();
195
- process.exit(0);
196
- }
197
-
198
- if (line === '.reconnect') {
199
- ctx.conn.close();
200
- try {
201
- await ctx.conn.connect();
202
- console.log(`${c.green}✓${c.reset} Reconnected`);
203
- } catch (err) {
204
- console.error(`${c.red}✗${c.reset} ${err.message}`);
205
- }
206
- return;
207
- }
208
-
209
- // ── Session commands (record/save/pause/discard) ────────────────
210
-
211
- if (line.startsWith('.')) {
212
- try {
213
- if (handleSessionCommand(ctx, line)) return;
214
- } catch (err) {
215
- console.log(`${c.yellow}${err.message}${c.reset}`);
216
- return;
217
- }
218
- }
219
-
220
- // ── Inline replay ──────────────────────────────────────────────
221
-
222
- if (line.startsWith('.replay')) {
223
- const filename = line.split(/\s+/)[1];
224
- if (!filename) {
225
- console.log(`${c.yellow}Usage: .replay <filename>${c.reset}`);
226
- return;
227
- }
228
- try {
229
- const player = ctx.session.startReplay(filename);
230
- console.log(`${c.blue}▶${c.reset} Replaying ${c.bold}${filename}${c.reset} (${player.commands.length} commands)\n`);
231
- while (!player.done) {
232
- const cmd = player.next();
233
- console.log(`${c.dim}${player.progress}${c.reset} ${cmd}`);
234
- await processLine(ctx, cmd);
235
- }
236
- ctx.session.endReplay();
237
- console.log(`\n${c.green}✓${c.reset} Replay complete`);
238
- } catch (err) {
239
- console.error(`${c.red}Error:${c.reset} ${err.message}`);
240
- ctx.session.endReplay();
241
- }
242
- return;
243
- }
244
-
245
- // ── Regular command — parse and send ─────────────────────────────
246
-
247
- let args = parseInput(line);
248
- if (!args) return;
249
-
250
- const cmdName = args._[0];
251
- if (!cmdName) return;
252
-
253
- // Validate command exists
254
- const knownExtras = ['help', 'list', 'close-all', 'kill-all', 'install', 'install-browser',
255
- 'verify-text', 'verify-element', 'verify-value', 'verify-list'];
256
- if (!ALL_COMMANDS.includes(cmdName) && !knownExtras.includes(cmdName)) {
257
- console.log(`${c.yellow}Unknown command: ${cmdName}${c.reset}`);
258
- console.log(`${c.dim}Type .help for available commands${c.reset}`);
259
- return;
260
- }
261
-
262
- // ── Session-level commands (not forwarded to daemon) ──────────
263
- if (cmdName === 'kill-all') return handleKillAll(ctx);
264
- if (cmdName === 'close' || cmdName === 'close-all') return handleClose(ctx);
265
-
266
- // ── Verify commands → run-code translation ──────────────────
267
- const verifyFns = {
268
- 'verify-text': verifyText,
269
- 'verify-element': verifyElement,
270
- 'verify-value': verifyValue,
271
- 'verify-list': verifyList,
272
- };
273
- if (verifyFns[cmdName]) {
274
- const pos = args._.slice(1);
275
- const fn = verifyFns[cmdName];
276
- let translated = null;
277
- if (cmdName === 'verify-text') {
278
- const text = pos.join(' ');
279
- if (text) translated = buildRunCode(fn, text);
280
- } else if (pos[0] && pos.length >= 2) {
281
- const rest = cmdName === 'verify-list' ? pos.slice(1) : pos.slice(1).join(' ');
282
- translated = buildRunCode(fn, pos[0], rest);
283
- }
284
- if (translated) {
285
- args = translated;
286
- } else {
287
- console.log(`${c.yellow}Usage: ${cmdName} <args>${c.reset}`);
288
- return;
289
- }
290
- }
291
-
292
- // ── Auto-resolve text to native Playwright locator ─────────
293
- const textFns = {
294
- click: actionByText, dblclick: actionByText, hover: actionByText,
295
- fill: fillByText, select: selectByText, check: checkByText, uncheck: uncheckByText,
296
- };
297
- if (textFns[cmdName] && args._[1] && !/^e\d+$/.test(args._[1])) {
298
- const textArg = args._[1];
299
- const extraArgs = args._.slice(2);
300
- const fn = textFns[cmdName];
301
- let runCodeArgs;
302
- if (fn === actionByText) runCodeArgs = buildRunCode(fn, textArg, cmdName);
303
- else if (cmdName === 'fill' || cmdName === 'select') runCodeArgs = buildRunCode(fn, textArg, extraArgs[0] || '');
304
- else runCodeArgs = buildRunCode(fn, textArg);
305
- const argsHint = extraArgs.length > 0 ? ` ${extraArgs.join(' ')}` : '';
306
- ctx.log(`${c.dim}→ ${cmdName} "${textArg}"${argsHint} (via run-code)${c.reset}`);
307
- args = runCodeArgs;
308
- }
309
-
310
- // ── Auto-wrap run-code body with async (page) => { ... } ──
311
- if (cmdName === 'run-code' && args._[1] && !args._[1].startsWith('async')) {
312
- const STMT = /^(await|return|const|let|var|for|if|while|throw|try)\b/;
313
- const body = !args._[1].includes(';') && !STMT.test(args._[1])
314
- ? `return await ${args._[1]}`
315
- : args._[1];
316
- args = { _: ['run-code', `async (page) => { ${body} }`] };
317
- ctx.log(`${c.dim}→ ${args._[1]}${c.reset}`);
318
- }
319
-
320
- const startTime = performance.now();
321
- try {
322
- const result = await ctx.conn.run(args);
323
- const elapsed = (performance.now() - startTime).toFixed(0);
324
- if (result?.text) {
325
- const output = filterResponse(result.text);
326
- if (output) console.log(output);
327
- }
328
- ctx.commandCount++;
329
- ctx.session.record(line);
330
-
331
- if (elapsed > 500) {
332
- ctx.log(`${c.dim}(${elapsed}ms)${c.reset}`);
333
- }
334
- } catch (err) {
335
- console.error(`${c.red}Error:${c.reset} ${err.message}`);
336
- if (!ctx.conn.connected) {
337
- console.log(`${c.yellow}Connection lost. Trying to reconnect...${c.reset}`);
338
- try {
339
- await ctx.conn.connect();
340
- console.log(`${c.green}✓${c.reset} Reconnected. Try your command again.`);
341
- } catch {
342
- console.error(`${c.red}✗${c.reset} Could not reconnect. Use .reconnect or restart.`);
343
- }
344
- }
345
- }
346
- }
347
-
348
- // ─── Replay mode (non-interactive, --replay flag) ───────────────────────────
349
-
350
- export async function runReplayMode(ctx, replayFile, step) {
351
- try {
352
- const player = ctx.session.startReplay(replayFile, step);
353
- console.log(`${c.blue}▶${c.reset} Replaying ${c.bold}${replayFile}${c.reset} (${player.commands.length} commands)\n`);
354
- while (!player.done) {
355
- const cmd = player.next();
356
- console.log(`${c.dim}${player.progress}${c.reset} ${cmd}`);
357
- await processLine(ctx, cmd);
358
-
359
- if (ctx.session.step && !player.done) {
360
- await new Promise((resolve) => {
361
- process.stdout.write(`${c.dim} Press Enter to continue...${c.reset}`);
362
- process.stdin.once('data', () => {
363
- process.stdout.write('\r\x1b[K');
364
- resolve();
365
- });
366
- });
367
- }
368
- }
369
- ctx.session.endReplay();
370
- console.log(`\n${c.green}✓${c.reset} Replay complete`);
371
- ctx.conn.close();
372
- process.exit(0);
373
- } catch (err) {
374
- console.error(`${c.red}Error:${c.reset} ${err.message}`);
375
- ctx.conn.close();
376
- process.exit(1);
377
- }
378
- }
379
-
380
- // ─── Command loop (interactive) ─────────────────────────────────────────────
381
-
382
- export function startCommandLoop(ctx) {
383
- let processing = false;
384
- const commandQueue = [];
385
-
386
- async function processQueue() {
387
- if (processing) return;
388
- processing = true;
389
- while (commandQueue.length > 0) {
390
- const line = commandQueue.shift();
391
- await processLine(ctx, line);
392
- if (line.trim()) {
393
- try {
394
- fs.mkdirSync(path.dirname(ctx.historyFile), { recursive: true });
395
- fs.appendFileSync(ctx.historyFile, line.trim() + '\n');
396
- } catch {}
397
- }
398
- }
399
- processing = false;
400
- ctx.rl.prompt();
401
- }
402
-
403
- ctx.rl.prompt();
404
-
405
- ctx.rl.on('line', (line) => {
406
- commandQueue.push(line);
407
- processQueue();
408
- });
409
-
410
- ctx.rl.on('close', async () => {
411
- while (processing || commandQueue.length > 0) {
412
- await new Promise(r => setTimeout(r, 50));
413
- }
414
- ctx.log(`\n${c.dim}Disconnecting... (daemon stays running)${c.reset}`);
415
- ctx.conn.close();
416
- process.exit(0);
417
- });
418
-
419
- let lastSigint = 0;
420
- ctx.rl.on('SIGINT', () => {
421
- const now = Date.now();
422
- if (now - lastSigint < 500) {
423
- ctx.conn.close();
424
- process.exit(0);
425
- }
426
- lastSigint = now;
427
- ctx.log(`\n${c.dim}(Ctrl+C again to exit, or type .exit)${c.reset}`);
428
- ctx.rl.prompt();
429
- });
430
- }
431
-
432
- // ─── Prompt string ──────────────────────────────────────────────────────────
433
-
434
- export function promptStr(ctx) {
435
- const mode = ctx.session.mode;
436
- const prefix = mode === 'recording' ? `${c.red}⏺${c.reset} `
437
- : mode === 'paused' ? `${c.yellow}⏸${c.reset} `
438
- : '';
439
- return `${prefix}${c.cyan}pw>${c.reset} `;
440
- }
441
-
442
- // ─── Ghost completion (inline suggestion) ───────────────────────────────────
443
-
444
- /**
445
- * Attaches ghost-text completion to a readline interface.
446
- * Shows dimmed inline suggestion after the cursor; Tab or Right Arrow accepts it.
447
- *
448
- * Uses _ttyWrite wrapper instead of _writeToOutput because Node 22+ optimizes
449
- * single-character appends and doesn't always trigger a full line refresh.
450
- *
451
- * @param {readline.Interface} rl
452
- * @param {Array<{cmd: string, desc: string}>} items - from buildCompletionItems()
453
- */
454
- /**
455
- * Returns matching commands for ghost completion.
456
- * When the input exactly matches a command AND there are longer matches,
457
- * the exact match is included so the user can cycle through all options.
458
- */
459
- export function getGhostMatches(cmds, input) {
460
- if (input.length > 0 && !input.includes(' ')) {
461
- const longer = cmds.filter(cmd => cmd.startsWith(input) && cmd !== input);
462
- if (longer.length > 0 && cmds.includes(input)) longer.push(input);
463
- return longer;
464
- }
465
- return [];
466
- }
467
-
468
- function attachGhostCompletion(rl, items) {
469
- if (!process.stdin.isTTY) return; // no ghost text for piped input
470
-
471
- const cmds = items.map(i => i.cmd);
472
- let ghost = '';
473
- let matches = []; // all matching commands for current input
474
- let matchIdx = 0; // which match is currently shown
475
-
476
- function renderGhost(suffix) {
477
- ghost = suffix;
478
- rl.output.write(`\x1b[2m${ghost}\x1b[0m\x1b[${ghost.length}D`);
479
- }
480
-
481
- const origTtyWrite = rl._ttyWrite.bind(rl);
482
- rl._ttyWrite = function (s, key) {
483
- // Tab handling — based on matches, not ghost text
484
- if (key && key.name === 'tab') {
485
- // Cycle through multiple matches
486
- if (matches.length > 1) {
487
- rl.output.write('\x1b[K');
488
- ghost = '';
489
- matchIdx = (matchIdx + 1) % matches.length;
490
- const input = rl.line || '';
491
- const suffix = matches[matchIdx].slice(input.length);
492
- if (suffix) renderGhost(suffix);
493
- return;
494
- }
495
- // Single match — accept it
496
- if (ghost && matches.length === 1) {
497
- const text = ghost;
498
- rl.output.write('\x1b[K');
499
- ghost = '';
500
- matches = [];
501
- rl._insertString(text);
502
- return;
503
- }
504
- return;
505
- }
506
-
507
- if (ghost && key) {
508
- // Right-arrow-at-end accepts ghost suggestion
509
- if (key.name === 'right' && rl.cursor === rl.line.length) {
510
- const text = ghost;
511
- rl.output.write('\x1b[K');
512
- ghost = '';
513
- matches = [];
514
- rl._insertString(text);
515
- return;
516
- }
517
- }
518
-
519
- // Clear existing ghost text before readline processes the key
520
- if (ghost) {
521
- rl.output.write('\x1b[K');
522
- ghost = '';
523
- }
524
-
525
- // Let readline handle the key normally
526
- origTtyWrite(s, key);
527
-
528
- // Render new ghost text if cursor is at end of line
529
- const input = rl.line || '';
530
- matches = getGhostMatches(cmds, input);
531
- matchIdx = 0;
532
- if (matches.length > 0 && rl.cursor === rl.line.length) {
533
- renderGhost(matches[0].slice(input.length));
534
- }
535
- };
536
- }
537
-
538
- // ─── REPL ────────────────────────────────────────────────────────────────────
539
-
540
- export async function startRepl(opts = {}) {
541
- const sessionName = opts.session || 'default';
542
- const silent = opts.silent || false;
543
- const log = (...args) => { if (!silent) console.log(...args); };
544
-
545
- log(`${c.bold}${c.magenta}🎭 Playwright REPL${c.reset} ${c.dim}v${replVersion}${c.reset}`);
546
- log(`${c.dim}Session: ${sessionName} | Type .help for commands${c.reset}\n`);
547
-
548
- // ─── Connect to daemon ───────────────────────────────────────────
549
-
550
- const running = await isDaemonRunning(sessionName);
551
- if (!running) {
552
- await startDaemon(sessionName, opts);
553
- await new Promise(r => setTimeout(r, 500));
554
- }
555
-
556
- const conn = new DaemonConnection(socketPath(sessionName), replVersion);
557
- try {
558
- await conn.connect();
559
- log(`${c.green}✓${c.reset} Connected to daemon${running ? '' : ' (newly started)'}\n`);
560
- } catch (err) {
561
- console.error(`${c.red}✗${c.reset} Failed to connect: ${err.message}`);
562
- console.error(` Try: playwright-cli open`);
563
- process.exit(1);
564
- }
565
-
566
- // ─── Session + readline ──────────────────────────────────────────
567
-
568
- const session = new SessionManager();
569
- const historyFile = path.join(daemonProfilesDir, '.repl-history');
570
- const ctx = { conn, session, rl: null, sessionName, log, historyFile, commandCount: 0 };
571
-
572
- // Auto-start recording if --record was passed
573
- if (opts.record) {
574
- const file = session.startRecording(opts.record);
575
- log(`${c.red}⏺${c.reset} Recording to ${c.bold}${file}${c.reset}`);
576
- }
577
-
578
- const rl = readline.createInterface({
579
- input: process.stdin,
580
- output: process.stdout,
581
- prompt: promptStr(ctx),
582
- historySize: 500,
583
- });
584
- ctx.rl = rl;
585
-
586
- try {
587
- const hist = fs.readFileSync(historyFile, 'utf-8').split('\n').filter(Boolean).reverse();
588
- for (const line of hist) rl.history.push(line);
589
- } catch {}
590
-
591
- attachGhostCompletion(rl, buildCompletionItems());
592
-
593
- // ─── Start ───────────────────────────────────────────────────────
594
-
595
- if (opts.replay) {
596
- await runReplayMode(ctx, opts.replay, opts.step);
597
- } else {
598
- startCommandLoop(ctx);
599
- }
600
- }
package/src/resolve.mjs DELETED
@@ -1,82 +0,0 @@
1
- /**
2
- * Shared dependencies and command vocabulary.
3
- * No @playwright/cli — we start the daemon ourselves via daemon-launcher.cjs.
4
- */
5
-
6
- import fs from 'node:fs';
7
- import { fileURLToPath } from 'node:url';
8
- import { createRequire } from 'node:module';
9
-
10
- const require = createRequire(import.meta.url);
11
-
12
- // ─── Own dependencies ────────────────────────────────────────────────────────
13
-
14
- export const minimist = require('minimist');
15
-
16
- const pkgUrl = new URL('../package.json', import.meta.url);
17
- const pkg = JSON.parse(fs.readFileSync(pkgUrl, 'utf-8'));
18
- export const replVersion = pkg.version;
19
-
20
- // Must match what daemon-launcher.cjs computes via require.resolve('../package.json')
21
- export const packageLocation = fileURLToPath(pkgUrl);
22
-
23
- // ─── Command vocabulary ──────────────────────────────────────────────────────
24
-
25
- export const COMMANDS = {
26
- 'open': { desc: 'Open the browser', options: [] },
27
- 'close': { desc: 'Close the browser', options: [] },
28
- 'goto': { desc: 'Navigate to a URL', options: [] },
29
- 'go-back': { desc: 'Go back', options: [] },
30
- 'go-forward': { desc: 'Go forward', options: [] },
31
- 'reload': { desc: 'Reload page', options: [] },
32
- 'click': { desc: 'Click an element', options: ['--button', '--modifiers'] },
33
- 'dblclick': { desc: 'Double-click', options: ['--button', '--modifiers'] },
34
- 'fill': { desc: 'Fill a form field', options: ['--submit'] },
35
- 'type': { desc: 'Type text key by key', options: ['--submit'] },
36
- 'press': { desc: 'Press a keyboard key', options: [] },
37
- 'hover': { desc: 'Hover over element', options: [] },
38
- 'select': { desc: 'Select dropdown option', options: [] },
39
- 'check': { desc: 'Check a checkbox', options: [] },
40
- 'uncheck': { desc: 'Uncheck a checkbox', options: [] },
41
- 'upload': { desc: 'Upload a file', options: [] },
42
- 'drag': { desc: 'Drag and drop', options: [] },
43
- 'snapshot': { desc: 'Accessibility snapshot', options: ['--filename'] },
44
- 'screenshot': { desc: 'Take a screenshot', options: ['--filename', '--fullPage'] },
45
- 'eval': { desc: 'Evaluate JavaScript', options: [] },
46
- 'console': { desc: 'Console messages', options: ['--clear'] },
47
- 'network': { desc: 'Network requests', options: ['--clear', '--includeStatic'] },
48
- 'run-code': { desc: 'Run Playwright code', options: [] },
49
- 'tab-list': { desc: 'List tabs', options: [] },
50
- 'tab-new': { desc: 'New tab', options: [] },
51
- 'tab-close': { desc: 'Close tab', options: [] },
52
- 'tab-select': { desc: 'Select tab', options: [] },
53
- 'cookie-list': { desc: 'List cookies', options: [] },
54
- 'cookie-get': { desc: 'Get cookie', options: [] },
55
- 'cookie-set': { desc: 'Set cookie', options: [] },
56
- 'cookie-delete': { desc: 'Delete cookie', options: [] },
57
- 'cookie-clear': { desc: 'Clear cookies', options: [] },
58
- 'localstorage-list': { desc: 'List localStorage', options: [] },
59
- 'localstorage-get': { desc: 'Get localStorage', options: [] },
60
- 'localstorage-set': { desc: 'Set localStorage', options: [] },
61
- 'localstorage-delete': { desc: 'Delete localStorage', options: [] },
62
- 'localstorage-clear': { desc: 'Clear localStorage', options: [] },
63
- 'sessionstorage-list': { desc: 'List sessionStorage', options: [] },
64
- 'sessionstorage-get': { desc: 'Get sessionStorage', options: [] },
65
- 'sessionstorage-set': { desc: 'Set sessionStorage', options: [] },
66
- 'sessionstorage-delete':{ desc: 'Delete sessionStorage', options: [] },
67
- 'sessionstorage-clear': { desc: 'Clear sessionStorage', options: [] },
68
- 'state-save': { desc: 'Save storage state', options: ['--filename'] },
69
- 'state-load': { desc: 'Load storage state', options: [] },
70
- 'dialog-accept': { desc: 'Accept dialog', options: [] },
71
- 'dialog-dismiss': { desc: 'Dismiss dialog', options: [] },
72
- 'route': { desc: 'Add network route', options: [] },
73
- 'route-list': { desc: 'List routes', options: [] },
74
- 'unroute': { desc: 'Remove route', options: [] },
75
- 'resize': { desc: 'Resize window', options: [] },
76
- 'pdf': { desc: 'Save as PDF', options: ['--filename'] },
77
- 'config-print': { desc: 'Print config', options: [] },
78
- 'install-browser': { desc: 'Install browser', options: [] },
79
- 'list': { desc: 'List sessions', options: [] },
80
- 'close-all': { desc: 'Close all sessions', options: [] },
81
- 'kill-all': { desc: 'Kill all daemons', options: [] },
82
- };