@monoes/monobrowse 1.0.17 → 1.0.21
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/dist/src/__tests__/browse-concurrency.test.d.ts +15 -0
- package/dist/src/__tests__/browse-concurrency.test.d.ts.map +1 -0
- package/dist/src/__tests__/browse-concurrency.test.js +198 -0
- package/dist/src/__tests__/browse-concurrency.test.js.map +1 -0
- package/dist/src/__tests__/browser-launch.test.js +132 -1
- package/dist/src/__tests__/browser-launch.test.js.map +1 -1
- package/dist/src/__tests__/close-browser.test.js +2 -2
- package/dist/src/__tests__/close-browser.test.js.map +1 -1
- package/dist/src/__tests__/frame-command.test.js +12 -2
- package/dist/src/__tests__/frame-command.test.js.map +1 -1
- package/dist/src/__tests__/reap-idle-browser.test.js +13 -13
- package/dist/src/__tests__/reap-idle-browser.test.js.map +1 -1
- package/dist/src/__tests__/ref-cache.test.js +96 -58
- package/dist/src/__tests__/ref-cache.test.js.map +1 -1
- package/dist/src/__tests__/report-exit-code.test.js +1 -1
- package/dist/src/__tests__/session-discovery.test.d.ts +13 -0
- package/dist/src/__tests__/session-discovery.test.d.ts.map +1 -0
- package/dist/src/__tests__/session-discovery.test.js +218 -0
- package/dist/src/__tests__/session-discovery.test.js.map +1 -0
- package/dist/src/browser/browser.d.ts +2 -1
- package/dist/src/browser/browser.d.ts.map +1 -1
- package/dist/src/browser/browser.js +145 -42
- package/dist/src/browser/browser.js.map +1 -1
- package/dist/src/browser/ref-cache.d.ts +50 -22
- package/dist/src/browser/ref-cache.d.ts.map +1 -1
- package/dist/src/browser/ref-cache.js +122 -57
- package/dist/src/browser/ref-cache.js.map +1 -1
- package/dist/src/browser/types.d.ts +2 -0
- package/dist/src/browser/types.d.ts.map +1 -1
- package/dist/src/browser/types.js.map +1 -1
- package/dist/src/cli/commands-context.js +1 -1
- package/dist/src/cli/commands-context.js.map +1 -1
- package/dist/src/cli/commands-navigate.js +2 -2
- package/dist/src/cli/commands-navigate.js.map +1 -1
- package/dist/src/cli/commands-page.js +2 -2
- package/dist/src/cli/commands-page.js.map +1 -1
- package/dist/src/cli/commands-report.js +2 -2
- package/dist/src/cli/commands-report.js.map +1 -1
- package/dist/src/cli/commands-session.d.ts.map +1 -1
- package/dist/src/cli/commands-session.js +70 -56
- package/dist/src/cli/commands-session.js.map +1 -1
- package/dist/src/cli/commands.d.ts.map +1 -1
- package/dist/src/cli/commands.js +102 -66
- package/dist/src/cli/commands.js.map +1 -1
- package/dist/src/cli/session.d.ts +66 -2
- package/dist/src/cli/session.d.ts.map +1 -1
- package/dist/src/cli/session.js +190 -33
- package/dist/src/cli/session.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/__tests__/browse-concurrency.test.ts +233 -0
- package/src/__tests__/browser-launch.test.ts +142 -1
- package/src/__tests__/close-browser.test.ts +2 -2
- package/src/__tests__/frame-command.test.ts +12 -2
- package/src/__tests__/reap-idle-browser.test.ts +15 -14
- package/src/__tests__/ref-cache.test.ts +112 -59
- package/src/__tests__/report-exit-code.test.ts +1 -1
- package/src/__tests__/session-discovery.test.ts +259 -0
- package/src/browser/browser.ts +149 -39
- package/src/browser/ref-cache.ts +144 -72
- package/src/browser/types.ts +2 -0
- package/src/cli/commands-context.ts +1 -1
- package/src/cli/commands-navigate.ts +2 -2
- package/src/cli/commands-page.ts +2 -2
- package/src/cli/commands-report.ts +2 -2
- package/src/cli/commands-session.ts +74 -55
- package/src/cli/commands.ts +106 -66
- package/src/cli/session.ts +208 -33
|
@@ -10,10 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
import { output } from './output.js';
|
|
12
12
|
import {
|
|
13
|
+
adoptLegacySession,
|
|
13
14
|
detectAttentionNeeded,
|
|
14
15
|
ensureConnected,
|
|
15
|
-
ensureSignalCleanupHandlers,
|
|
16
16
|
getBrowser,
|
|
17
|
+
launchSessionBrowser,
|
|
18
|
+
pinnedPort,
|
|
19
|
+
resolveLiveSession,
|
|
17
20
|
session,
|
|
18
21
|
switchToHeaded,
|
|
19
22
|
} from './session.js';
|
|
@@ -23,7 +26,13 @@ export const openCommand: Command = {
|
|
|
23
26
|
name: 'open',
|
|
24
27
|
description: 'Open a URL in the browser. Usage: monomind browse open <url>',
|
|
25
28
|
options: [
|
|
26
|
-
{
|
|
29
|
+
{
|
|
30
|
+
name: 'port',
|
|
31
|
+
short: 'p',
|
|
32
|
+
type: 'number',
|
|
33
|
+
description:
|
|
34
|
+
'Attach to (or launch on) this CDP port. Default: a free port of this session’s own',
|
|
35
|
+
},
|
|
27
36
|
{
|
|
28
37
|
name: 'headed',
|
|
29
38
|
type: 'boolean',
|
|
@@ -37,7 +46,10 @@ export const openCommand: Command = {
|
|
|
37
46
|
const url = ctx.args[0] as string;
|
|
38
47
|
if (!url) throw new Error('URL required. Usage: monomind browse open <url>');
|
|
39
48
|
|
|
40
|
-
|
|
49
|
+
// No --port means "start a session of my own on a free port" (#318) —
|
|
50
|
+
// never "join whatever is on 9222", which is what made two uncoordinated
|
|
51
|
+
// `open` calls race for one browser and one profile directory.
|
|
52
|
+
const port = pinnedPort(ctx.flags);
|
|
41
53
|
const forceHeaded = ctx.flags.headed as boolean;
|
|
42
54
|
const browser = await getBrowser();
|
|
43
55
|
|
|
@@ -77,38 +89,9 @@ export const openCommand: Command = {
|
|
|
77
89
|
session.refs = new Map();
|
|
78
90
|
}
|
|
79
91
|
|
|
80
|
-
session
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
// persist the launched PID/userDataDir so a later process's closeBrowser
|
|
84
|
-
// can still kill this Chrome even though launchedPids (browser.ts) is
|
|
85
|
-
// per-process and empty there.
|
|
86
|
-
//
|
|
87
|
-
// launchBrowser() can either LAUNCH a fresh Chrome or ATTACH to one
|
|
88
|
-
// already listening on the requested port (see its own "attach if
|
|
89
|
-
// already Chrome" comment) — it returns only a port number, with no
|
|
90
|
-
// signal telling this caller which happened. getLaunchedPid(session.port) is
|
|
91
|
-
// undefined on the attach path (this process never spawned anything).
|
|
92
|
-
// Bug fixed here: unconditionally saving {pid: undefined, ...} on
|
|
93
|
-
// attach used to CLOBBER a real PID a previous `open` had already
|
|
94
|
-
// persisted for this exact port, destroying the only way a later
|
|
95
|
-
// process's closeBrowser() PID-kill fallback could ever find it.
|
|
96
|
-
const freshPid = browser.getLaunchedPid(session.port);
|
|
97
|
-
const freshUserDataDir = browser.getLaunchedUserDataDir(session.port);
|
|
98
|
-
if (freshPid !== undefined) {
|
|
99
|
-
await browser.saveActivePort(session.port, { pid: freshPid, userDataDir: freshUserDataDir });
|
|
100
|
-
} else {
|
|
101
|
-
// Attach path: preserve whatever PID/userDataDir was already on file
|
|
102
|
-
// for this port (most likely from the process that originally
|
|
103
|
-
// launched it) instead of overwriting with undefined.
|
|
104
|
-
const existing = await browser.loadActivePortInfo();
|
|
105
|
-
const preserved = existing && existing.port === session.port ? existing : undefined;
|
|
106
|
-
await browser.saveActivePort(session.port, {
|
|
107
|
-
pid: preserved?.pid,
|
|
108
|
-
userDataDir: preserved?.userDataDir,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
ensureSignalCleanupHandlers();
|
|
92
|
+
// Starts this session's own browser (and records it) unless --port named
|
|
93
|
+
// one to attach to — see the session rule in session.ts.
|
|
94
|
+
session.port = await launchSessionBrowser(browser, { port, headless: !forceHeaded });
|
|
112
95
|
const conn = await browser.connectToTarget(session.port);
|
|
113
96
|
session.client = conn.client;
|
|
114
97
|
session.sessionId = conn.sessionId;
|
|
@@ -117,7 +100,7 @@ export const openCommand: Command = {
|
|
|
117
100
|
// A snapshot taken before this navigation is no longer valid for the new
|
|
118
101
|
// page — drop the persisted ref cache so a stale process's weak
|
|
119
102
|
// time-based check can't resurrect it before the next explicit snapshot.
|
|
120
|
-
await browser.clearRefCache();
|
|
103
|
+
await browser.clearRefCache(session.port);
|
|
121
104
|
|
|
122
105
|
if (ctx.flags.state && ctx.flags.session) {
|
|
123
106
|
output.printWarning('Both --state and --session provided; --state takes precedence');
|
|
@@ -142,7 +125,7 @@ export const openCommand: Command = {
|
|
|
142
125
|
output.printWarning(
|
|
143
126
|
`${attentionType === 'captcha' ? 'CAPTCHA' : 'Login'} detected — switching to headed mode`,
|
|
144
127
|
);
|
|
145
|
-
await switchToHeaded(currentUrl, port);
|
|
128
|
+
await switchToHeaded(currentUrl, session.port);
|
|
146
129
|
await browser.openUrl(session.client!, session.sessionId, currentUrl);
|
|
147
130
|
output.printSuccess(
|
|
148
131
|
`Resumed headless after ${attentionType === 'captcha' ? 'CAPTCHA' : 'login'}`,
|
|
@@ -153,15 +136,27 @@ export const openCommand: Command = {
|
|
|
153
136
|
const finalUrl = await browser.getCurrentUrl(session.client!, session.sessionId);
|
|
154
137
|
const title = await browser.getCurrentTitle(session.client!, session.sessionId);
|
|
155
138
|
|
|
156
|
-
|
|
157
|
-
|
|
139
|
+
// The port is part of the result, not decoration: it is this session's
|
|
140
|
+
// handle, and the only way a concurrent caller can pin it from another
|
|
141
|
+
// process (`--port N`) instead of resolving "the newest live session".
|
|
142
|
+
output.printSuccess(`Opened: ${title} (${finalUrl}) [port ${session.port}]`);
|
|
143
|
+
return { success: true, data: { url: finalUrl, title, port: session.port } };
|
|
158
144
|
},
|
|
159
145
|
};
|
|
160
146
|
|
|
161
147
|
export const closeCommand: Command = {
|
|
162
148
|
name: 'close',
|
|
163
|
-
description:
|
|
164
|
-
|
|
149
|
+
description:
|
|
150
|
+
'Close a browser session. Usage: monomind browse close [--port <port>] (default: the newest live session here)',
|
|
151
|
+
options: [
|
|
152
|
+
{
|
|
153
|
+
name: 'port',
|
|
154
|
+
short: 'p',
|
|
155
|
+
type: 'number',
|
|
156
|
+
description: 'Close the session on this CDP port instead of the newest live one',
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
action: async (ctx: CommandContext): Promise<CommandResult> => {
|
|
165
160
|
if (session.client) {
|
|
166
161
|
const browser = await getBrowser();
|
|
167
162
|
const sid = session.sessionId;
|
|
@@ -192,26 +187,48 @@ export const closeCommand: Command = {
|
|
|
192
187
|
browser.stopRequestCapture(sid);
|
|
193
188
|
browser.teardownDialogHandling(sid);
|
|
194
189
|
browser.teardownConsoleCapture(sid);
|
|
190
|
+
// Terminate the browser this process launched, not just our socket to
|
|
191
|
+
// it: with a session of its own on a free port (#318), a Chrome left
|
|
192
|
+
// behind here is one nothing else knows how to find. A browser we
|
|
193
|
+
// merely attached to (`connect`) is the user's — never killed.
|
|
194
|
+
const record = await browser.loadSessionRecord(session.port);
|
|
195
|
+
if (record?.launched !== false) {
|
|
196
|
+
try {
|
|
197
|
+
await browser.closeBrowser(client, session.port);
|
|
198
|
+
} catch {
|
|
199
|
+
/* best-effort — the record cleanup below still runs */
|
|
200
|
+
}
|
|
201
|
+
}
|
|
195
202
|
client.close();
|
|
196
203
|
session.client = null;
|
|
197
204
|
session.sessionId = '';
|
|
198
205
|
session.parentSessionId = '';
|
|
199
206
|
session.targetId = '';
|
|
200
207
|
session.refs = new Map();
|
|
201
|
-
//
|
|
208
|
+
// This session is gone — forget its record and stale refs so later
|
|
202
209
|
// invocations don't chase a dead endpoint (or the wrong elements).
|
|
203
|
-
|
|
204
|
-
await browser.
|
|
205
|
-
|
|
210
|
+
// Other sessions' records are left alone.
|
|
211
|
+
await browser.removeSessionRecord(session.port);
|
|
212
|
+
await browser.clearRefCache(session.port);
|
|
213
|
+
output.printSuccess(`Browser session closed (port ${session.port})`);
|
|
206
214
|
} else {
|
|
207
215
|
// Each CLI invocation is a fresh process, so `close` almost always
|
|
208
|
-
// lands here. The
|
|
209
|
-
//
|
|
216
|
+
// lands here. The session record is the real handle: `--port` names
|
|
217
|
+
// one, otherwise it is the newest session whose browser still answers
|
|
218
|
+
// (strict:false — a dead one is cleaned up, not raised as an error).
|
|
219
|
+
// If monobrowse LAUNCHED that browser (open), gracefully Browser.close
|
|
210
220
|
// it — otherwise every open→close cycle leaks a headless Chrome. If we
|
|
211
221
|
// merely ATTACHED to it (connect, launched:false), never kill it: it's
|
|
212
|
-
// the user's own browser. Either way, forget
|
|
222
|
+
// the user's own browser. Either way, forget that one session.
|
|
213
223
|
const browser = await getBrowser();
|
|
214
|
-
const
|
|
224
|
+
const pinned = pinnedPort(ctx.flags);
|
|
225
|
+
const persisted = pinned
|
|
226
|
+
? ((await browser.loadSessionRecord(pinned)) ??
|
|
227
|
+
(await adoptLegacySession(browser, { port: pinned })) ?? {
|
|
228
|
+
port: pinned,
|
|
229
|
+
launched: true,
|
|
230
|
+
})
|
|
231
|
+
: await resolveLiveSession(browser, { strict: false });
|
|
215
232
|
if (persisted?.launched) {
|
|
216
233
|
try {
|
|
217
234
|
const conn = await browser.connectToTarget(persisted.port);
|
|
@@ -257,8 +274,10 @@ export const closeCommand: Command = {
|
|
|
257
274
|
: 'No active browser session',
|
|
258
275
|
);
|
|
259
276
|
}
|
|
260
|
-
|
|
261
|
-
|
|
277
|
+
if (persisted) {
|
|
278
|
+
await browser.removeSessionRecord(persisted.port);
|
|
279
|
+
await browser.clearRefCache(persisted.port);
|
|
280
|
+
}
|
|
262
281
|
}
|
|
263
282
|
return { success: true };
|
|
264
283
|
},
|
|
@@ -346,12 +365,12 @@ export const connectCommand: Command = {
|
|
|
346
365
|
session.targetId = conn.target.id;
|
|
347
366
|
session.port = port;
|
|
348
367
|
session.refs = new Map();
|
|
349
|
-
//
|
|
350
|
-
// (each command is a fresh process)
|
|
351
|
-
//
|
|
368
|
+
// Record the session like `open` does — without this, the NEXT CLI
|
|
369
|
+
// process (each command is a fresh process) has nothing to resolve and
|
|
370
|
+
// launches a browser of its own instead of reusing this session.
|
|
352
371
|
// launched:false marks this browser as someone else's — close must never
|
|
353
372
|
// kill it, and a dead endpoint must not be silently relaunched.
|
|
354
|
-
await browser.
|
|
373
|
+
await browser.saveSessionRecord(port, { launched: false });
|
|
355
374
|
const url = await browser.getCurrentUrl(session.client, session.sessionId);
|
|
356
375
|
const title = await browser.getCurrentTitle(session.client, session.sessionId);
|
|
357
376
|
output.printSuccess(`Connected: ${title} (${url})`);
|
package/src/cli/commands.ts
CHANGED
|
@@ -90,6 +90,7 @@ import {
|
|
|
90
90
|
} from './commands-trace.js';
|
|
91
91
|
import { output } from './output.js';
|
|
92
92
|
import { createPlatformCommand } from './platform.js';
|
|
93
|
+
import { applySessionPortFlag } from './session.js';
|
|
93
94
|
import type { Command, CommandContext, CommandResult } from './types.js';
|
|
94
95
|
|
|
95
96
|
// Re-exported for direct unit testing — these were exported from this file
|
|
@@ -212,80 +213,119 @@ const batchCommand: Command = {
|
|
|
212
213
|
const actionSubcommand: Command = wrapCommanderCommand(createActionCommand);
|
|
213
214
|
const platformSubcommand: Command = wrapCommanderCommand(createPlatformCommand);
|
|
214
215
|
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
// Session selector
|
|
218
|
+
//
|
|
219
|
+
// Sessions are per-port (#318), so `--port N` is how any command says which
|
|
220
|
+
// one it means; without it a command resolves the newest live session in this
|
|
221
|
+
// directory. Wiring it here, once, gives every subcommand the selector
|
|
222
|
+
// without each action having to read the flag: session.port is set before the
|
|
223
|
+
// action runs, and that is what every ensureConnected(session.port) resolves.
|
|
224
|
+
// ---------------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
const PORT_OPTION = {
|
|
227
|
+
name: 'port',
|
|
228
|
+
short: 'p',
|
|
229
|
+
type: 'number',
|
|
230
|
+
description: 'Act on the browse session on this CDP port (default: the newest live session)',
|
|
231
|
+
} as const;
|
|
232
|
+
|
|
233
|
+
function withSessionSelector(cmd: Command): Command {
|
|
234
|
+
const action = cmd.action;
|
|
235
|
+
return {
|
|
236
|
+
...cmd,
|
|
237
|
+
options: cmd.options?.some((o) => o.name === 'port')
|
|
238
|
+
? cmd.options
|
|
239
|
+
: [...(cmd.options ?? []), { ...PORT_OPTION }],
|
|
240
|
+
action: action
|
|
241
|
+
? async (ctx: CommandContext) => {
|
|
242
|
+
applySessionPortFlag(ctx.flags);
|
|
243
|
+
return action(ctx);
|
|
244
|
+
}
|
|
245
|
+
: undefined,
|
|
246
|
+
subcommands: cmd.subcommands?.map(withSessionSelector),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
215
250
|
// ---------------------------------------------------------------------------
|
|
216
251
|
// Root browse command
|
|
217
252
|
// ---------------------------------------------------------------------------
|
|
218
253
|
|
|
254
|
+
// Every subcommand gets the `--port` session selector (see
|
|
255
|
+
// withSessionSelector). batchCommand dispatches over this same array, so a
|
|
256
|
+
// batched command honours the selector too.
|
|
257
|
+
const subcommands: Command[] = [
|
|
258
|
+
openCommand,
|
|
259
|
+
snapshotCommand,
|
|
260
|
+
clickCommand,
|
|
261
|
+
dblclickCommand,
|
|
262
|
+
fillCommand,
|
|
263
|
+
typeCommand,
|
|
264
|
+
pressCommand,
|
|
265
|
+
keyboardCommand,
|
|
266
|
+
keydownCommand,
|
|
267
|
+
keyupCommand,
|
|
268
|
+
hoverCommand,
|
|
269
|
+
focusCommand,
|
|
270
|
+
selectCommand,
|
|
271
|
+
checkCommand,
|
|
272
|
+
uncheckCommand,
|
|
273
|
+
isvisibleCommand,
|
|
274
|
+
isenabledCommand,
|
|
275
|
+
ischeckedCommand,
|
|
276
|
+
tapCommand,
|
|
277
|
+
swipeCommand,
|
|
278
|
+
scrollIntoViewCommand,
|
|
279
|
+
dragCommand,
|
|
280
|
+
uploadCommand,
|
|
281
|
+
downloadCommand,
|
|
282
|
+
mouseCommand,
|
|
283
|
+
clipboardCommand,
|
|
284
|
+
waitCommand,
|
|
285
|
+
screenshotCommand,
|
|
286
|
+
getCommand,
|
|
287
|
+
scrollCommand,
|
|
288
|
+
navigateCommand,
|
|
289
|
+
setCommand,
|
|
290
|
+
stateCommand,
|
|
291
|
+
networkCommand,
|
|
292
|
+
evalCommand,
|
|
293
|
+
dialogCommand,
|
|
294
|
+
frameCommand,
|
|
295
|
+
tabCommand,
|
|
296
|
+
windowCommand,
|
|
297
|
+
consoleLogCommand,
|
|
298
|
+
errorsCommand,
|
|
299
|
+
storageCommand,
|
|
300
|
+
cookiesCommand,
|
|
301
|
+
pdfCommand,
|
|
302
|
+
isCommand,
|
|
303
|
+
findCommand,
|
|
304
|
+
highlightCommand,
|
|
305
|
+
diffCommand,
|
|
306
|
+
pushstateCommand,
|
|
307
|
+
batchCommand,
|
|
308
|
+
addinitscriptCommand,
|
|
309
|
+
removeinitscriptCommand,
|
|
310
|
+
connectCommand,
|
|
311
|
+
recordCommand,
|
|
312
|
+
traceCommand,
|
|
313
|
+
profilerCommand,
|
|
314
|
+
vitalsCommand,
|
|
315
|
+
reportCommand,
|
|
316
|
+
harCommand,
|
|
317
|
+
resizeCommand,
|
|
318
|
+
closeCommand,
|
|
319
|
+
actionSubcommand,
|
|
320
|
+
platformSubcommand,
|
|
321
|
+
].map(withSessionSelector);
|
|
322
|
+
|
|
219
323
|
const browseCommand: Command = {
|
|
220
324
|
name: 'browse',
|
|
221
325
|
description: 'Native browser automation via Chrome DevTools Protocol',
|
|
222
|
-
subcommands
|
|
223
|
-
openCommand,
|
|
224
|
-
snapshotCommand,
|
|
225
|
-
clickCommand,
|
|
226
|
-
dblclickCommand,
|
|
227
|
-
fillCommand,
|
|
228
|
-
typeCommand,
|
|
229
|
-
pressCommand,
|
|
230
|
-
keyboardCommand,
|
|
231
|
-
keydownCommand,
|
|
232
|
-
keyupCommand,
|
|
233
|
-
hoverCommand,
|
|
234
|
-
focusCommand,
|
|
235
|
-
selectCommand,
|
|
236
|
-
checkCommand,
|
|
237
|
-
uncheckCommand,
|
|
238
|
-
isvisibleCommand,
|
|
239
|
-
isenabledCommand,
|
|
240
|
-
ischeckedCommand,
|
|
241
|
-
tapCommand,
|
|
242
|
-
swipeCommand,
|
|
243
|
-
scrollIntoViewCommand,
|
|
244
|
-
dragCommand,
|
|
245
|
-
uploadCommand,
|
|
246
|
-
downloadCommand,
|
|
247
|
-
mouseCommand,
|
|
248
|
-
clipboardCommand,
|
|
249
|
-
waitCommand,
|
|
250
|
-
screenshotCommand,
|
|
251
|
-
getCommand,
|
|
252
|
-
scrollCommand,
|
|
253
|
-
navigateCommand,
|
|
254
|
-
setCommand,
|
|
255
|
-
stateCommand,
|
|
256
|
-
networkCommand,
|
|
257
|
-
evalCommand,
|
|
258
|
-
dialogCommand,
|
|
259
|
-
frameCommand,
|
|
260
|
-
tabCommand,
|
|
261
|
-
windowCommand,
|
|
262
|
-
consoleLogCommand,
|
|
263
|
-
errorsCommand,
|
|
264
|
-
storageCommand,
|
|
265
|
-
cookiesCommand,
|
|
266
|
-
pdfCommand,
|
|
267
|
-
isCommand,
|
|
268
|
-
findCommand,
|
|
269
|
-
highlightCommand,
|
|
270
|
-
diffCommand,
|
|
271
|
-
pushstateCommand,
|
|
272
|
-
batchCommand,
|
|
273
|
-
addinitscriptCommand,
|
|
274
|
-
removeinitscriptCommand,
|
|
275
|
-
connectCommand,
|
|
276
|
-
recordCommand,
|
|
277
|
-
traceCommand,
|
|
278
|
-
profilerCommand,
|
|
279
|
-
vitalsCommand,
|
|
280
|
-
reportCommand,
|
|
281
|
-
harCommand,
|
|
282
|
-
resizeCommand,
|
|
283
|
-
closeCommand,
|
|
284
|
-
actionSubcommand,
|
|
285
|
-
platformSubcommand,
|
|
286
|
-
],
|
|
326
|
+
subcommands,
|
|
287
327
|
options: [
|
|
288
|
-
{
|
|
328
|
+
{ ...PORT_OPTION },
|
|
289
329
|
{ name: 'session', short: 's', type: 'string', description: 'Named session to use' },
|
|
290
330
|
],
|
|
291
331
|
examples: [
|