browser-debugger-cli 0.15.0 → 0.16.0
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/.claude/skills/bdg/SKILL.md +2 -1
- package/dist/cdp/methodTarget.d.ts +92 -0
- package/dist/cdp/methodTarget.js +159 -0
- package/dist/cdp/protocol.d.ts +16 -1
- package/dist/cdp/protocol.js +21 -0
- package/dist/cdp/schema.d.ts +55 -1
- package/dist/cdp/schema.js +134 -25
- package/dist/cdp/types.d.ts +3 -1
- package/dist/commands/cdp.d.ts +38 -1
- package/dist/commands/cdp.js +200 -133
- package/dist/commands/cleanup.js +18 -4
- package/dist/commands/dom/formInteraction.js +8 -4
- package/dist/commands/dom/helpers/index.d.ts +4 -4
- package/dist/commands/dom/helpers/index.js +3 -3
- package/dist/commands/dom/helpers/query.d.ts +2 -2
- package/dist/commands/dom/helpers/query.js +2 -2
- package/dist/commands/dom/helpers/screenshot.d.ts +21 -26
- package/dist/commands/dom/helpers/screenshot.js +50 -668
- package/dist/commands/dom/screenshot.js +56 -36
- package/dist/commands/optionBehaviors.js +18 -8
- package/dist/commands/shared/CommandRunner.d.ts +5 -0
- package/dist/commands/shared/CommandRunner.js +18 -3
- package/dist/commands/shared/interrupt.d.ts +40 -0
- package/dist/commands/shared/interrupt.js +73 -0
- package/dist/commands/shared/optionTypes.d.ts +2 -0
- package/dist/commands/shared/startHelpers.d.ts +26 -3
- package/dist/commands/shared/startHelpers.js +145 -23
- package/dist/commands/types.d.ts +5 -0
- package/dist/connection/cdp.js +1 -16
- package/dist/connection/chromeIdentity.d.ts +24 -5
- package/dist/connection/chromeIdentity.js +53 -22
- package/dist/connection/launcher.d.ts +34 -1
- package/dist/connection/launcher.js +98 -10
- package/dist/connection/typed-cdp.d.ts +3 -2
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/daemon/SessionController.d.ts +10 -5
- package/dist/daemon/SessionController.js +15 -8
- package/dist/daemon/ipcServer.js +1 -1
- package/dist/daemon/launcher.d.ts +5 -0
- package/dist/daemon/launcher.js +8 -1
- package/dist/daemon/session/Session.d.ts +5 -1
- package/dist/daemon/session/Session.js +9 -8
- package/dist/daemon/session/TelemetryStore.d.ts +5 -0
- package/dist/daemon/session/TelemetryStore.js +4 -0
- package/dist/daemon/session/captureGate.d.ts +59 -0
- package/dist/daemon/session/captureGate.js +96 -0
- package/dist/daemon/session/chromeConnection.d.ts +16 -1
- package/dist/daemon/session/chromeConnection.js +34 -4
- package/dist/daemon/session/collectors.d.ts +15 -0
- package/dist/daemon/session/collectors.js +39 -2
- package/dist/daemon/session/commandRegistry.d.ts +14 -1
- package/dist/daemon/session/commandRegistry.js +46 -11
- package/dist/daemon/session/downloads.d.ts +32 -0
- package/dist/daemon/session/downloads.js +96 -0
- package/dist/daemon/session/interactions.d.ts +3 -2
- package/dist/daemon/session/interactions.js +7 -2
- package/dist/daemon/session/plugins.js +6 -0
- package/dist/daemon.js +12843 -11482
- package/dist/errors/CommandError.d.ts +2 -0
- package/dist/errors/issues.d.ts +1 -1
- package/dist/errors/messages.d.ts +58 -0
- package/dist/errors/messages.js +112 -0
- package/dist/index.js +999 -1020
- package/dist/ipc/client.d.ts +14 -1
- package/dist/ipc/client.js +21 -4
- package/dist/ipc/protocol/commands.d.ts +32 -2
- package/dist/ipc/protocol/commands.js +1 -0
- package/dist/ipc/protocol/domTypes.d.ts +24 -1
- package/dist/ipc/session/queries.d.ts +3 -0
- package/dist/ipc/session/types.d.ts +5 -0
- package/dist/ipc/transport/IPCError.d.ts +9 -0
- package/dist/ipc/transport/IPCError.js +12 -0
- package/dist/ipc/transport/errors.d.ts +2 -1
- package/dist/ipc/transport/errors.js +4 -1
- package/dist/ipc/transport/index.d.ts +4 -2
- package/dist/ipc/transport/index.js +13 -3
- package/dist/runtime/dom/actionEffects.d.ts +48 -9
- package/dist/runtime/dom/actionEffects.js +269 -34
- package/dist/runtime/dom/actionEffectsScripts.d.ts +45 -0
- package/dist/runtime/dom/actionEffectsScripts.js +101 -2
- package/dist/runtime/dom/captureArea.d.ts +35 -0
- package/dist/runtime/dom/captureArea.js +203 -0
- package/dist/runtime/dom/elementInfo.d.ts +10 -8
- package/dist/runtime/dom/elementInfo.js +8 -6
- package/dist/runtime/dom/formDiscovery.d.ts +1 -1
- package/dist/runtime/page/bdgWorld.d.ts +9 -0
- package/dist/runtime/page/bdgWorld.js +11 -0
- package/dist/runtime/page/captureEmulation.d.ts +119 -0
- package/dist/runtime/page/captureEmulation.js +189 -0
- package/dist/runtime/page/captureScroll.d.ts +24 -0
- package/dist/runtime/page/captureScroll.js +124 -0
- package/dist/runtime/page/screenshot.d.ts +41 -0
- package/dist/runtime/page/screenshot.js +394 -0
- package/dist/session/paths.d.ts +14 -0
- package/dist/session/paths.js +25 -0
- package/dist/telemetry/downloads.d.ts +127 -0
- package/dist/telemetry/downloads.js +265 -0
- package/dist/telemetry/har/builder.js +22 -7
- package/dist/telemetry/har/sanitize.d.ts +7 -3
- package/dist/telemetry/har/sanitize.js +52 -6
- package/dist/telemetry/har/sanitizeBody.d.ts +47 -7
- package/dist/telemetry/har/sanitizeBody.js +429 -56
- package/dist/telemetry/har/types.d.ts +2 -0
- package/dist/telemetry/network.d.ts +4 -4
- package/dist/telemetry/network.js +38 -4
- package/dist/telemetry/networkRetention.d.ts +35 -14
- package/dist/telemetry/networkRetention.js +62 -26
- package/dist/types.d.ts +9 -14
- package/dist/ui/OutputBuilder.d.ts +3 -2
- package/dist/ui/OutputBuilder.js +4 -3
- package/dist/ui/formatters/cdp.d.ts +32 -9
- package/dist/ui/formatters/cdp.js +77 -6
- package/dist/ui/formatters/details.js +7 -15
- package/dist/ui/formatters/preview.d.ts +2 -0
- package/dist/ui/formatters/preview.js +7 -1
- package/dist/ui/formatters/status.js +6 -1
- package/dist/ui/formatting.d.ts +7 -0
- package/dist/ui/formatting.js +13 -0
- package/dist/ui/logging/logger.d.ts +1 -1
- package/dist/ui/messages/chrome.d.ts +13 -0
- package/dist/ui/messages/chrome.js +26 -0
- package/dist/ui/messages/commands.d.ts +71 -3
- package/dist/ui/messages/commands.js +98 -3
- package/dist/ui/messages/networkMessages.d.ts +24 -5
- package/dist/ui/messages/networkMessages.js +31 -8
- package/dist/utils/async.d.ts +3 -2
- package/dist/utils/async.js +16 -3
- package/dist/utils/http.d.ts +11 -4
- package/dist/utils/http.js +5 -3
- package/package.json +18 -4
- /package/dist/{commands/dom → runtime/page}/screenshotResize.d.ts +0 -0
- /package/dist/{commands/dom → runtime/page}/screenshotResize.js +0 -0
package/dist/commands/cdp.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Command } from 'commander';
|
|
2
2
|
import { type CommandResult } from './shared/CommandRunner.js';
|
|
3
3
|
import type { CdpCommandOptions } from './shared/optionTypes.js';
|
|
4
|
-
import { type CdpExecuteData } from '../ui/formatters/cdp.js';
|
|
4
|
+
import { type CdpDomainDescription, type CdpExecuteData, type CdpMethodDescription, type CdpTypeDescription } from '../ui/formatters/cdp.js';
|
|
5
5
|
/**
|
|
6
6
|
* Register CDP command with full introspection support.
|
|
7
7
|
*
|
|
@@ -35,4 +35,41 @@ export declare function isBareDomain(method: string, options: CdpCommandOptions)
|
|
|
35
35
|
* @returns Error result, or undefined when the result has no exception
|
|
36
36
|
*/
|
|
37
37
|
export declare function pageExceptionResult(result: unknown): CommandResult<CdpExecuteData> | undefined;
|
|
38
|
+
/** A description `bdg cdp <name> --describe` gives */
|
|
39
|
+
type CdpDescription = CdpMethodDescription | CdpDomainDescription | CdpTypeDescription;
|
|
40
|
+
/**
|
|
41
|
+
* Handle describe mode: show a domain, a method's signature and parameters,
|
|
42
|
+
* or a protocol type's values or properties.
|
|
43
|
+
*
|
|
44
|
+
* @param name - Domain, `Domain.method` or `Domain.Type` (case-insensitive)
|
|
45
|
+
* @returns Success result with the description
|
|
46
|
+
*/
|
|
47
|
+
export declare function handleDescribeMethod(name: string): CommandResult<CdpDescription>;
|
|
48
|
+
/**
|
|
49
|
+
* The method `bdg cdp <name>` sends: a bundled method with its casing, or a
|
|
50
|
+
* well-formed method the bundled protocol lacks, as typed with a warning.
|
|
51
|
+
*
|
|
52
|
+
* @param methodName - Method name as typed
|
|
53
|
+
* @param options - `sendAnyway` sends a close typo of bundled methods as typed
|
|
54
|
+
* @returns Method to send, and the warning for one the bundled protocol lacks
|
|
55
|
+
* @throws CommandError (exit 81) for a blocked method, a type, a close typo
|
|
56
|
+
* of bundled methods (without `sendAnyway`) or a name that is not `Domain.method`
|
|
57
|
+
*/
|
|
58
|
+
export declare function methodToSend(methodName: string, options?: Pick<CdpCommandOptions, 'sendAnyway'>): {
|
|
59
|
+
method: string;
|
|
60
|
+
warning?: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* The result of a CDP call: the page exception it reported (exit 91), or its
|
|
64
|
+
* result with the session's and the method's hints; either way with the
|
|
65
|
+
* warning for a method the bundled protocol lacks.
|
|
66
|
+
*
|
|
67
|
+
* @param method - Method called
|
|
68
|
+
* @param cdpResult - What Chrome returned
|
|
69
|
+
* @param warning - Warning for a method the bundled protocol lacks
|
|
70
|
+
* @param ipcHint - Hint the session gave (e.g. a repeated-call pattern)
|
|
71
|
+
* @returns Command result
|
|
72
|
+
*/
|
|
73
|
+
export declare function cdpCallResult(method: string, cdpResult: unknown, warning?: string, ipcHint?: string): CommandResult<CdpExecuteData>;
|
|
74
|
+
export {};
|
|
38
75
|
//# sourceMappingURL=cdp.d.ts.map
|
package/dist/commands/cdp.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { Option } from 'commander';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { resolveMethodTarget } from '../cdp/methodTarget.js';
|
|
3
|
+
import { getBundledProtocolVersion } from '../cdp/protocol.js';
|
|
4
|
+
import { getAllDomainSummaries, getDomainMethods, getProtocolCounts, getDomainSummary, getMethodSchema, getTypeSchema, } from '../cdp/schema.js';
|
|
4
5
|
import { runCommand } from './shared/CommandRunner.js';
|
|
5
6
|
import { jsonOption } from './shared/commonOptions.js';
|
|
6
7
|
import { CommandError } from '../errors/index.js';
|
|
7
|
-
import { emptyCdpSearchError, missingArgumentError, scriptExecutionError, } from '../errors/messages.js';
|
|
8
|
+
import { cdpMethodNotFoundError, cdpMethodNotInBundledProtocolError, cdpMethodTypoError, cdpTypeNotMethodError, emptyCdpSearchError, missingArgumentError, scriptExecutionError, } from '../errors/messages.js';
|
|
8
9
|
import { callCDP } from '../ipc/client.js';
|
|
9
10
|
import { validateIPCResponse } from '../ipc/index.js';
|
|
10
11
|
import { describeException } from '../runtime/dom/evalHelpers.js';
|
|
11
12
|
import { formatCdpDescription, formatCdpDomainMethods, formatCdpDomains, formatCdpResult, formatCdpSearch, isEmptyCdpResult, } from '../ui/formatters/cdp.js';
|
|
13
|
+
import { CDP_EXECUTION_HELP, cdpUnlistedMethodWarning } from '../ui/messages/commands.js';
|
|
12
14
|
import { formatHint } from '../ui/messages/hints.js';
|
|
13
15
|
import { sessionCommand } from '../ui/messages/sessionCommand.js';
|
|
14
16
|
import { getErrorMessage } from '../utils/errors.js';
|
|
@@ -83,7 +85,7 @@ export function registerCdpCommand(program) {
|
|
|
83
85
|
.summary('CDP protocol introspection and execution')
|
|
84
86
|
.description('CDP protocol introspection and execution\n' +
|
|
85
87
|
' Discovery: --list, --search, --describe\n' +
|
|
86
|
-
|
|
88
|
+
CDP_EXECUTION_HELP)
|
|
87
89
|
.argument('[method]', 'CDP method name (e.g., Network.getCookies, network.getcookies)')
|
|
88
90
|
.addOption(new Option('--params <json>', 'Method parameters as JSON'))
|
|
89
91
|
.addOption(new Option('--list', 'List all domains or methods in a domain').conflicts([
|
|
@@ -91,6 +93,7 @@ export function registerCdpCommand(program) {
|
|
|
91
93
|
'params',
|
|
92
94
|
]))
|
|
93
95
|
.addOption(new Option('--describe', 'Show method signature and parameters').conflicts('params'))
|
|
96
|
+
.addOption(new Option('--send-anyway', 'Send a Domain.method that looks like a typo of a bundled one as typed').conflicts(['list', 'describe', 'search']))
|
|
94
97
|
.addOption(new Option('--search <query>', 'Search methods by keyword').conflicts([
|
|
95
98
|
'list',
|
|
96
99
|
'describe',
|
|
@@ -123,7 +126,7 @@ async function runCdpCommand(method, options) {
|
|
|
123
126
|
return runCommand(async () => handleDescribeMethod(method), options, formatCdpDescription);
|
|
124
127
|
}
|
|
125
128
|
if (method) {
|
|
126
|
-
return runCommand(async () => handleExecuteMethod(method, options
|
|
129
|
+
return runCommand(async () => handleExecuteMethod(method, options), options, formatCdpResult);
|
|
127
130
|
}
|
|
128
131
|
return runCommand(async () => {
|
|
129
132
|
const err = missingArgumentError(CDP_USAGE);
|
|
@@ -331,99 +334,116 @@ function handleListDomainMethods(domainName) {
|
|
|
331
334
|
};
|
|
332
335
|
}
|
|
333
336
|
/**
|
|
334
|
-
* Handle describe
|
|
337
|
+
* Handle describe mode: show a domain, a method's signature and parameters,
|
|
338
|
+
* or a protocol type's values or properties.
|
|
335
339
|
*
|
|
336
|
-
* @param
|
|
337
|
-
* @returns Success result with
|
|
340
|
+
* @param name - Domain, `Domain.method` or `Domain.Type` (case-insensitive)
|
|
341
|
+
* @returns Success result with the description
|
|
338
342
|
*/
|
|
339
|
-
function handleDescribeMethod(
|
|
340
|
-
const [domainName,
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
events: summary.eventCount,
|
|
371
|
-
experimental: summary.experimental,
|
|
372
|
-
deprecated: summary.deprecated,
|
|
373
|
-
note: domainNote,
|
|
374
|
-
nextStep: `Use: bdg cdp ${summary.name} --list (to see all methods)`,
|
|
375
|
-
},
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
const schema = getMethodSchema(domainName, method);
|
|
379
|
-
if (!schema) {
|
|
380
|
-
const similar = findSimilarMethods(methodName, domainName);
|
|
381
|
-
const suggestions = [`Use: bdg cdp ${domainName} --list (to see all ${domainName} methods)`];
|
|
382
|
-
if (similar.length > 0) {
|
|
383
|
-
suggestions.push('');
|
|
384
|
-
suggestions.push('Did you mean:');
|
|
385
|
-
similar.forEach((name) => suggestions.push(` - ${name}`));
|
|
386
|
-
}
|
|
343
|
+
export function handleDescribeMethod(name) {
|
|
344
|
+
const [domainName = '', member] = name.includes('.') ? name.split('.') : [name, undefined];
|
|
345
|
+
if (!member)
|
|
346
|
+
return describeDomain(name);
|
|
347
|
+
const schema = getMethodSchema(domainName, member);
|
|
348
|
+
if (schema)
|
|
349
|
+
return { success: true, data: describeMethod(schema) };
|
|
350
|
+
const type = getTypeSchema(domainName, member);
|
|
351
|
+
if (type)
|
|
352
|
+
return { success: true, data: { type: 'type', ...type } };
|
|
353
|
+
const target = resolveMethodTarget(name);
|
|
354
|
+
const err = target.kind === 'unlisted'
|
|
355
|
+
? cdpMethodNotInBundledProtocolError(target.method, getBundledProtocolVersion())
|
|
356
|
+
: cdpMethodNotFoundError(name, findSimilarMethods(name, domainName), `Use: bdg cdp ${domainName} --list (to see all ${domainName} methods)`);
|
|
357
|
+
return {
|
|
358
|
+
success: false,
|
|
359
|
+
error: err.message,
|
|
360
|
+
exitCode: EXIT_CODES.INVALID_ARGUMENTS,
|
|
361
|
+
errorContext: { suggestion: err.suggestion },
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Describe a domain (`bdg cdp Network --describe`).
|
|
366
|
+
*
|
|
367
|
+
* @param domainName - Domain name (case-insensitive)
|
|
368
|
+
* @returns Domain description, or not found with similar names
|
|
369
|
+
*/
|
|
370
|
+
function describeDomain(domainName) {
|
|
371
|
+
const summary = getDomainSummary(domainName);
|
|
372
|
+
if (!summary) {
|
|
373
|
+
const err = cdpMethodNotFoundError(domainName, findSimilarMethods(domainName), 'Use: bdg cdp --list (to see all domains)');
|
|
387
374
|
return {
|
|
388
375
|
success: false,
|
|
389
|
-
error: `
|
|
376
|
+
error: `Domain or method '${domainName}' not found`,
|
|
390
377
|
exitCode: EXIT_CODES.INVALID_ARGUMENTS,
|
|
391
|
-
errorContext: {
|
|
392
|
-
suggestion: suggestions.join('\n'),
|
|
393
|
-
},
|
|
378
|
+
errorContext: { suggestion: err.suggestion },
|
|
394
379
|
};
|
|
395
380
|
}
|
|
396
|
-
const methodNote = METHOD_NOTES[schema.name] ?? DOMAIN_NOTES[schema.domain];
|
|
397
|
-
const alternative = blockedAlternative(schema.name);
|
|
398
381
|
return {
|
|
399
382
|
success: true,
|
|
400
383
|
data: {
|
|
401
|
-
type: '
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
experimental:
|
|
407
|
-
deprecated:
|
|
408
|
-
note:
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
384
|
+
type: 'domain',
|
|
385
|
+
domain: summary.name,
|
|
386
|
+
description: summary.description,
|
|
387
|
+
commands: summary.commandCount,
|
|
388
|
+
events: summary.eventCount,
|
|
389
|
+
experimental: summary.experimental,
|
|
390
|
+
deprecated: summary.deprecated,
|
|
391
|
+
note: DOMAIN_NOTES[summary.name],
|
|
392
|
+
nextStep: `Use: bdg cdp ${summary.name} --list (to see all methods)`,
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* A parameter as `--describe` shows it.
|
|
398
|
+
*
|
|
399
|
+
* @param p - Parameter schema
|
|
400
|
+
* @returns Described parameter
|
|
401
|
+
*/
|
|
402
|
+
function describeParameter(p) {
|
|
403
|
+
return {
|
|
404
|
+
name: p.name,
|
|
405
|
+
type: p.type,
|
|
406
|
+
required: p.required,
|
|
407
|
+
description: p.description,
|
|
408
|
+
enum: p.enum,
|
|
409
|
+
ref: p.ref,
|
|
410
|
+
refType: p.refType,
|
|
411
|
+
items: p.items,
|
|
412
|
+
experimental: p.experimental,
|
|
413
|
+
deprecated: p.deprecated,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Describe a method (`bdg cdp Network.getCookies --describe`).
|
|
418
|
+
*
|
|
419
|
+
* @param schema - Method schema
|
|
420
|
+
* @returns Method description with its redirect, note and example
|
|
421
|
+
*/
|
|
422
|
+
function describeMethod(schema) {
|
|
423
|
+
const alternative = blockedAlternative(schema.name);
|
|
424
|
+
return {
|
|
425
|
+
type: 'method',
|
|
426
|
+
name: schema.name,
|
|
427
|
+
domain: schema.domain,
|
|
428
|
+
method: schema.method,
|
|
429
|
+
description: schema.description,
|
|
430
|
+
experimental: schema.experimental,
|
|
431
|
+
deprecated: schema.deprecated,
|
|
432
|
+
note: METHOD_NOTES[schema.name] ?? DOMAIN_NOTES[schema.domain],
|
|
433
|
+
parameters: schema.parameters.map(describeParameter),
|
|
434
|
+
returns: schema.returns.map((r) => ({
|
|
435
|
+
name: r.name,
|
|
436
|
+
type: r.type,
|
|
437
|
+
optional: r.optional,
|
|
438
|
+
description: r.description,
|
|
439
|
+
items: r.items,
|
|
440
|
+
})),
|
|
441
|
+
redirect: schema.redirect && {
|
|
442
|
+
method: schema.redirect.method,
|
|
443
|
+
resolved: schema.redirect.resolved,
|
|
444
|
+
parameters: schema.redirect.parameters.map(describeParameter),
|
|
426
445
|
},
|
|
446
|
+
example: alternative ? { command: alternative } : schema.example,
|
|
427
447
|
};
|
|
428
448
|
}
|
|
429
449
|
/**
|
|
@@ -445,44 +465,78 @@ const BLOCKED_CDP_METHODS = {
|
|
|
445
465
|
},
|
|
446
466
|
};
|
|
447
467
|
/**
|
|
448
|
-
*
|
|
468
|
+
* The method `bdg cdp <name>` sends: a bundled method with its casing, or a
|
|
469
|
+
* well-formed method the bundled protocol lacks, as typed with a warning.
|
|
449
470
|
*
|
|
450
|
-
* @param methodName - Method name
|
|
451
|
-
* @param
|
|
452
|
-
* @returns
|
|
471
|
+
* @param methodName - Method name as typed
|
|
472
|
+
* @param options - `sendAnyway` sends a close typo of bundled methods as typed
|
|
473
|
+
* @returns Method to send, and the warning for one the bundled protocol lacks
|
|
474
|
+
* @throws CommandError (exit 81) for a blocked method, a type, a close typo
|
|
475
|
+
* of bundled methods (without `sendAnyway`) or a name that is not `Domain.method`
|
|
453
476
|
*/
|
|
454
|
-
|
|
455
|
-
const
|
|
456
|
-
if (
|
|
457
|
-
const blocked = BLOCKED_CDP_METHODS[
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
error: `${normalized} is blocked via raw CDP: ${blocked.reason}`,
|
|
461
|
-
exitCode: EXIT_CODES.INVALID_ARGUMENTS,
|
|
462
|
-
errorContext: { suggestion: `Use: ${sessionCommand(blocked.alternative)}` },
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
if (!normalized) {
|
|
466
|
-
const similar = findSimilarMethods(methodName);
|
|
467
|
-
const suggestions = ['Use: bdg cdp --search <keyword> (to search for methods)'];
|
|
468
|
-
if (similar.length > 0) {
|
|
469
|
-
suggestions.push('');
|
|
470
|
-
suggestions.push('Did you mean:');
|
|
471
|
-
similar.forEach((name) => suggestions.push(` - ${name}`));
|
|
477
|
+
export function methodToSend(methodName, options = {}) {
|
|
478
|
+
const target = resolveMethodTarget(methodName);
|
|
479
|
+
if (target.kind === 'known') {
|
|
480
|
+
const blocked = BLOCKED_CDP_METHODS[target.method];
|
|
481
|
+
if (blocked) {
|
|
482
|
+
throw new CommandError(`${target.method} is blocked via raw CDP: ${blocked.reason}`, { suggestion: `Use: ${sessionCommand(blocked.alternative)}` }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
472
483
|
}
|
|
484
|
+
return { method: target.method };
|
|
485
|
+
}
|
|
486
|
+
if (target.kind === 'unlisted' || (target.kind === 'typo' && options.sendAnyway)) {
|
|
473
487
|
return {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
exitCode: EXIT_CODES.INVALID_ARGUMENTS,
|
|
477
|
-
errorContext: {
|
|
478
|
-
suggestion: suggestions.join('\n'),
|
|
479
|
-
},
|
|
488
|
+
method: target.method,
|
|
489
|
+
warning: cdpUnlistedMethodWarning(target.method, getBundledProtocolVersion()),
|
|
480
490
|
};
|
|
481
491
|
}
|
|
492
|
+
const err = target.kind === 'type'
|
|
493
|
+
? cdpTypeNotMethodError(target.name)
|
|
494
|
+
: unknownMethodError(methodName, target);
|
|
495
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Not found, for a close typo of bundled methods or domains, or a name that
|
|
499
|
+
* is not `Domain.method`.
|
|
500
|
+
*
|
|
501
|
+
* @param methodName - Method name as typed
|
|
502
|
+
* @param target - Typo with its suggestions, or malformed
|
|
503
|
+
* @returns Message and suggestion
|
|
504
|
+
*/
|
|
505
|
+
function unknownMethodError(methodName, target) {
|
|
506
|
+
if (target.kind === 'malformed') {
|
|
507
|
+
return cdpMethodNotFoundError(methodName, findSimilarMethods(methodName), 'Use: bdg cdp --search <keyword> (to search for methods)');
|
|
508
|
+
}
|
|
509
|
+
const [domainName = ''] = methodName.split('.');
|
|
510
|
+
return target.suggestions.length > 0
|
|
511
|
+
? cdpMethodTypoError(methodName, target.suggestions.slice(0, 3), 'Use: bdg cdp --search <keyword> (to search for methods)')
|
|
512
|
+
: cdpMethodTypoError(methodName, [], domainSuggestion(domainName));
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Add the warning about a method missing from the bundled protocol to the
|
|
516
|
+
* error Chrome gave for it, so a failed call still says it was sent as typed.
|
|
517
|
+
*
|
|
518
|
+
* @param error - Error of the call
|
|
519
|
+
* @param warning - Warning, when the method was not in the bundled protocol
|
|
520
|
+
* @returns The error, with the warning (top-level JSON `warning`) when there is one
|
|
521
|
+
*/
|
|
522
|
+
function withWarning(error, warning) {
|
|
523
|
+
if (!warning || !(error instanceof CommandError))
|
|
524
|
+
return error;
|
|
525
|
+
return new CommandError(error.message, { ...error.metadata, warning }, error.exitCode);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Handle execute method mode: Call CDP method.
|
|
529
|
+
*
|
|
530
|
+
* @param methodName - Method name (case-insensitive for bundled methods)
|
|
531
|
+
* @param paramsJson - Parameters as JSON string
|
|
532
|
+
* @returns Success result with method response
|
|
533
|
+
*/
|
|
534
|
+
async function handleExecuteMethod(methodName, options) {
|
|
535
|
+
const { method: normalized, warning } = methodToSend(methodName, options);
|
|
482
536
|
let params;
|
|
483
|
-
if (
|
|
537
|
+
if (options.params) {
|
|
484
538
|
try {
|
|
485
|
-
params = JSON.parse(
|
|
539
|
+
params = JSON.parse(options.params);
|
|
486
540
|
}
|
|
487
541
|
catch (error) {
|
|
488
542
|
return {
|
|
@@ -492,29 +546,42 @@ async function handleExecuteMethod(methodName, paramsJson) {
|
|
|
492
546
|
errorContext: {
|
|
493
547
|
suggestion: `Use: bdg cdp ${normalized} --describe (to see parameter schema)`,
|
|
494
548
|
},
|
|
549
|
+
...(warning && { warning }),
|
|
495
550
|
};
|
|
496
551
|
}
|
|
497
552
|
}
|
|
498
553
|
const response = await callCDP(normalized, params);
|
|
499
|
-
|
|
500
|
-
|
|
554
|
+
try {
|
|
555
|
+
validateIPCResponse(response);
|
|
556
|
+
}
|
|
557
|
+
catch (error) {
|
|
558
|
+
throw withWarning(error, warning);
|
|
559
|
+
}
|
|
560
|
+
const ipcHint = response.data?.hint && formatHint(response.data.hint);
|
|
561
|
+
return cdpCallResult(normalized, response.data?.result, warning, ipcHint);
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* The result of a CDP call: the page exception it reported (exit 91), or its
|
|
565
|
+
* result with the session's and the method's hints; either way with the
|
|
566
|
+
* warning for a method the bundled protocol lacks.
|
|
567
|
+
*
|
|
568
|
+
* @param method - Method called
|
|
569
|
+
* @param cdpResult - What Chrome returned
|
|
570
|
+
* @param warning - Warning for a method the bundled protocol lacks
|
|
571
|
+
* @param ipcHint - Hint the session gave (e.g. a repeated-call pattern)
|
|
572
|
+
* @returns Command result
|
|
573
|
+
*/
|
|
574
|
+
export function cdpCallResult(method, cdpResult, warning, ipcHint) {
|
|
575
|
+
const withCallWarning = warning ? { warning } : {};
|
|
501
576
|
const exception = pageExceptionResult(cdpResult);
|
|
502
577
|
if (exception)
|
|
503
|
-
return exception;
|
|
504
|
-
const
|
|
578
|
+
return { ...exception, ...withCallWarning };
|
|
579
|
+
const hints = [ipcHint, getMethodHint(method, cdpResult)].filter(Boolean);
|
|
580
|
+
return {
|
|
505
581
|
success: true,
|
|
506
|
-
data: {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
},
|
|
582
|
+
data: { method, result: cdpResult },
|
|
583
|
+
...withCallWarning,
|
|
584
|
+
...(hints.length > 0 && { hint: hints.join('\n') }),
|
|
510
585
|
};
|
|
511
|
-
if (response.data?.hint) {
|
|
512
|
-
result.hint = formatHint(response.data.hint);
|
|
513
|
-
}
|
|
514
|
-
const methodHint = getMethodHint(normalized, cdpResult);
|
|
515
|
-
if (methodHint) {
|
|
516
|
-
result.hint = result.hint ? `${result.hint}\n${methodHint}` : methodHint;
|
|
517
|
-
}
|
|
518
|
-
return result;
|
|
519
586
|
}
|
|
520
587
|
//# sourceMappingURL=cdp.js.map
|
package/dist/commands/cleanup.js
CHANGED
|
@@ -5,10 +5,10 @@ import { purgeNeedsNamedSessionError, purgeRefusedError, sessionDirIsFileError,
|
|
|
5
5
|
import { isSessionChrome } from '../session/cleanup/staleSession.js';
|
|
6
6
|
import { performSessionCleanup } from '../session/cleanup/userCommands.js';
|
|
7
7
|
import { isDaemonAlive } from '../session/daemonSocket.js';
|
|
8
|
-
import { getSessionDir, getSessionFilePath, getSessionName } from '../session/paths.js';
|
|
8
|
+
import { getSessionDir, getSessionDownloadsDir, getSessionFilePath, getSessionName, } from '../session/paths.js';
|
|
9
9
|
import { readDaemonPid, readPidFromFile } from '../session/pid.js';
|
|
10
10
|
import { joinLines } from '../ui/formatting.js';
|
|
11
|
-
import { sessionFilesCleanedMessage, sessionOutputRemovedMessage, sessionDirectoryCleanMessage, sessionDirectoryPurgedMessage, noSessionFilesMessage, sessionStillActiveError, sessionStillActiveSuggestion, warningMessage, } from '../ui/messages/commands.js';
|
|
11
|
+
import { downloadsKeptMessage, sessionFilesCleanedMessage, sessionOutputRemovedMessage, sessionDirectoryCleanMessage, sessionDirectoryPurgedMessage, noSessionFilesMessage, sessionStillActiveError, sessionStillActiveSuggestion, warningMessage, } from '../ui/messages/commands.js';
|
|
12
12
|
import { delay } from '../utils/async.js';
|
|
13
13
|
import { EXIT_CODES } from '../utils/exitCodes.js';
|
|
14
14
|
import { isProcessAlive } from '../utils/process.js';
|
|
@@ -19,7 +19,7 @@ import { isProcessAlive } from '../utils/process.js';
|
|
|
19
19
|
*/
|
|
20
20
|
function formatCleanup(data) {
|
|
21
21
|
const { cleaned } = data;
|
|
22
|
-
return joinLines(cleaned.session && sessionFilesCleanedMessage(), cleaned.output && sessionOutputRemovedMessage(), ...(data.warnings ?? []).map((warning) => warningMessage(warning)), '', data.message);
|
|
22
|
+
return joinLines(cleaned.session && sessionFilesCleanedMessage(), cleaned.output && sessionOutputRemovedMessage(), data.downloadsKept && downloadsKeptMessage(data.downloadsKept.dir, data.downloadsKept.files), ...(data.warnings ?? []).map((warning) => warningMessage(warning)), '', data.message);
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Delete the selected named session's directory (Chrome profile, logs, port).
|
|
@@ -162,11 +162,13 @@ async function cleanupSession(opts) {
|
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
164
|
const didCleanup = Object.values(cleaned).some(Boolean) || purged !== undefined;
|
|
165
|
+
const downloadsKept = purged === undefined ? keptDownloads() : undefined;
|
|
165
166
|
return {
|
|
166
167
|
success: true,
|
|
167
168
|
data: {
|
|
168
169
|
cleaned,
|
|
169
170
|
...(purged !== undefined && { purged }),
|
|
171
|
+
...(downloadsKept && { downloadsKept }),
|
|
170
172
|
message: !didCleanup
|
|
171
173
|
? noSessionFilesMessage()
|
|
172
174
|
: purged !== undefined
|
|
@@ -176,6 +178,18 @@ async function cleanupSession(opts) {
|
|
|
176
178
|
},
|
|
177
179
|
};
|
|
178
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Downloaded files of the session, which cleanup keeps.
|
|
183
|
+
*
|
|
184
|
+
* @returns Their directory and count, or undefined when there are none
|
|
185
|
+
*/
|
|
186
|
+
function keptDownloads() {
|
|
187
|
+
const dir = getSessionDownloadsDir();
|
|
188
|
+
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory())
|
|
189
|
+
return undefined;
|
|
190
|
+
const files = fs.readdirSync(dir).length;
|
|
191
|
+
return files > 0 ? { dir, files } : undefined;
|
|
192
|
+
}
|
|
179
193
|
/**
|
|
180
194
|
* Register cleanup command
|
|
181
195
|
*
|
|
@@ -184,7 +198,7 @@ async function cleanupSession(opts) {
|
|
|
184
198
|
export function registerCleanupCommand(program) {
|
|
185
199
|
program
|
|
186
200
|
.command('cleanup')
|
|
187
|
-
.description('Clean up stale session files')
|
|
201
|
+
.description('Clean up stale session files (downloaded files are kept)')
|
|
188
202
|
.option('-f, --force', 'Kill a running (possibly hung) session, then clean up', false)
|
|
189
203
|
.option('--remove-output', 'Also remove session.json output file', false)
|
|
190
204
|
.option('--aggressive', 'Alias for --force (kept for compatibility)', false)
|
|
@@ -18,7 +18,7 @@ import { callCDP, domClick, domFill, domPressKey, domScroll, domSubmit } from '.
|
|
|
18
18
|
import { findUnknownModifiers } from '../../runtime/dom/keyMapping.js';
|
|
19
19
|
import { formatTriggeredRequestLines, formatTriggeredRequestsTitle, } from '../../ui/formatters/triggeredRequests.js';
|
|
20
20
|
import { OutputFormatter } from '../../ui/formatting.js';
|
|
21
|
-
import { CLICK_RESULT_WAIT_HELP, HOVER_OFF_DONE, HOVER_USAGE, POINTER_ACTION_DONE, POINTER_ACTION_NOUN, pointerScrollText, actionStatusLine, dialogConsoleText, moreMessagesText, newMessageText, pageNavigationText, shownElementText, stillChangingNote, } from '../../ui/messages/commands.js';
|
|
21
|
+
import { CLICK_RESULT_WAIT_HELP, HOVER_OFF_DONE, HOVER_USAGE, POINTER_ACTION_DONE, POINTER_ACTION_NOUN, pointerScrollText, actionStatusLine, dialogConsoleText, downloadText, moreMessagesText, newMessageText, pageNavigationText, shownElementText, stillChangingNote, } from '../../ui/messages/commands.js';
|
|
22
22
|
import { sessionCommand } from '../../ui/messages/sessionCommand.js';
|
|
23
23
|
import { EXIT_CODES } from '../../utils/exitCodes.js';
|
|
24
24
|
/** Help of `--strict` on click and hover */
|
|
@@ -313,9 +313,9 @@ async function runPointerCommand(selectorOrIndex, options, action) {
|
|
|
313
313
|
* "⚠ Element Clicked (page still changing)" with what it was still working
|
|
314
314
|
* on, or "⚠ Element Clicked (no visible effect: …)"), the details, what
|
|
315
315
|
* changed on the page (`Page:` navigation, `New text:` messages, `Shown:`
|
|
316
|
-
* elements), then the network requests it triggered
|
|
317
|
-
* caused. No request list is shown when there
|
|
318
|
-
* `triggeredRequests` then).
|
|
316
|
+
* elements), then the network requests it triggered, the downloads it
|
|
317
|
+
* started and the dialogs it caused. No request list is shown when there
|
|
318
|
+
* were none (JSON has an empty `triggeredRequests` then).
|
|
319
319
|
*
|
|
320
320
|
* @param done - What was done, e.g. "Element Clicked"
|
|
321
321
|
* @param details - Label/value rows
|
|
@@ -353,6 +353,10 @@ function formatActionOutput(done, details, result, options = {}) {
|
|
|
353
353
|
.blank()
|
|
354
354
|
.section(formatTriggeredRequestsTitle(result.triggeredRequests ?? [], omitted), requests);
|
|
355
355
|
}
|
|
356
|
+
if (result.downloads?.length)
|
|
357
|
+
fmt.blank();
|
|
358
|
+
for (const download of result.downloads ?? [])
|
|
359
|
+
fmt.text(downloadText(download));
|
|
356
360
|
for (const dialog of result.dialogs ?? []) {
|
|
357
361
|
fmt.blank();
|
|
358
362
|
fmt.text(`Dialog: ${dialogConsoleText(dialog)}`);
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* so callers can keep importing from `@/commands/dom/helpers.js`.
|
|
4
4
|
*
|
|
5
5
|
* - `query.ts` — selector → backend node ids (shadow roots, same-origin iframes), DOM.describeNode
|
|
6
|
-
* - `screenshot.ts` — page / element capture
|
|
6
|
+
* - `screenshot.ts` — page / element capture through the daemon, writing the image
|
|
7
7
|
*/
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export type {
|
|
8
|
+
export { noMatchesError, queryDOMElements, getDomContext, getDOMElements, resolveSelector, resolveBackendNodeIds, selectMatch, assertNodeAttached, pageDocumentId, } from './query.js';
|
|
9
|
+
export { captureScreenshot, screenshotInterrupted } from './screenshot.js';
|
|
10
|
+
export type { DomGetOptions, DomContext } from '../../../types.js';
|
|
11
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* so callers can keep importing from `@/commands/dom/helpers.js`.
|
|
4
4
|
*
|
|
5
5
|
* - `query.ts` — selector → backend node ids (shadow roots, same-origin iframes), DOM.describeNode
|
|
6
|
-
* - `screenshot.ts` — page / element capture
|
|
6
|
+
* - `screenshot.ts` — page / element capture through the daemon, writing the image
|
|
7
7
|
*/
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
8
|
+
export { noMatchesError, queryDOMElements, getDomContext, getDOMElements, resolveSelector, resolveBackendNodeIds, selectMatch, assertNodeAttached, pageDocumentId, } from './query.js';
|
|
9
|
+
export { captureScreenshot, screenshotInterrupted } from './screenshot.js';
|
|
10
10
|
//# sourceMappingURL=index.js.map
|
|
@@ -91,10 +91,10 @@ export declare function getDOMElements(options: DomGetOptions): Promise<DomGetRe
|
|
|
91
91
|
* Resolve a selector to its first match.
|
|
92
92
|
*
|
|
93
93
|
* @param selector - CSS selector
|
|
94
|
-
* @returns
|
|
94
|
+
* @returns Backend node id of the first match
|
|
95
95
|
* @throws CommandError (83) when nothing matches
|
|
96
96
|
*/
|
|
97
|
-
export declare function resolveSelector(selector: string): Promise<
|
|
97
|
+
export declare function resolveSelector(selector: string): Promise<number>;
|
|
98
98
|
/**
|
|
99
99
|
* Resolve selectors to backend node ids (first match each).
|
|
100
100
|
*
|
|
@@ -655,7 +655,7 @@ export async function getDOMElements(options) {
|
|
|
655
655
|
* Resolve a selector to its first match.
|
|
656
656
|
*
|
|
657
657
|
* @param selector - CSS selector
|
|
658
|
-
* @returns
|
|
658
|
+
* @returns Backend node id of the first match
|
|
659
659
|
* @throws CommandError (83) when nothing matches
|
|
660
660
|
*/
|
|
661
661
|
export async function resolveSelector(selector) {
|
|
@@ -664,7 +664,7 @@ export async function resolveSelector(selector) {
|
|
|
664
664
|
const err = await noMatchesError(selector);
|
|
665
665
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.RESOURCE_NOT_FOUND);
|
|
666
666
|
}
|
|
667
|
-
return
|
|
667
|
+
return backendNodeId;
|
|
668
668
|
}
|
|
669
669
|
/**
|
|
670
670
|
* Resolve selectors to backend node ids (first match each).
|