browser-debugger-cli 0.11.0 → 0.13.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 +4 -4
- package/README.md +143 -79
- package/dist/commands/cdp.d.ts +22 -1
- package/dist/commands/cdp.js +100 -43
- package/dist/commands/console.d.ts +12 -0
- package/dist/commands/console.js +62 -12
- package/dist/commands/css.d.ts +13 -0
- package/dist/commands/css.js +53 -0
- package/dist/commands/dom/DomElementResolver.d.ts +3 -1
- package/dist/commands/dom/DomElementResolver.js +10 -3
- package/dist/commands/dom/a11y.js +3 -2
- package/dist/commands/dom/audit.d.ts +14 -0
- package/dist/commands/dom/audit.js +87 -0
- package/dist/commands/dom/eval.d.ts +3 -2
- package/dist/commands/dom/eval.js +11 -5
- package/dist/commands/dom/form.js +10 -9
- package/dist/commands/dom/formInteraction.js +42 -11
- package/dist/commands/dom/get.js +8 -8
- package/dist/commands/dom/helpers/index.d.ts +1 -1
- package/dist/commands/dom/helpers/index.js +1 -1
- package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
- package/dist/commands/dom/helpers/keyAttributes.js +6 -4
- package/dist/commands/dom/helpers/query.d.ts +27 -3
- package/dist/commands/dom/helpers/query.js +152 -64
- package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
- package/dist/commands/dom/helpers/screenshot.js +169 -49
- package/dist/commands/dom/index.js +7 -2
- package/dist/commands/dom/query.d.ts +19 -2
- package/dist/commands/dom/query.js +37 -6
- package/dist/commands/dom/screenshot.js +12 -7
- package/dist/commands/dom/semanticUtils.d.ts +3 -2
- package/dist/commands/dom/semanticUtils.js +40 -9
- package/dist/commands/dom/wait.js +5 -3
- package/dist/commands/helpJson.d.ts +82 -19
- package/dist/commands/helpJson.js +112 -41
- package/dist/commands/helpTopic.d.ts +16 -1
- package/dist/commands/helpTopic.js +59 -1
- package/dist/commands/installSkill.d.ts +15 -5
- package/dist/commands/installSkill.js +86 -16
- package/dist/commands/network/list.js +22 -12
- package/dist/commands/optionBehaviors.js +53 -16
- package/dist/commands/page.js +7 -4
- package/dist/commands/peek.d.ts +7 -0
- package/dist/commands/peek.js +65 -23
- package/dist/commands/shared/daemonErrorHandler.d.ts +5 -2
- package/dist/commands/shared/daemonErrorHandler.js +20 -9
- package/dist/commands/shared/dataFetcher.d.ts +12 -4
- package/dist/commands/shared/dataFetcher.js +12 -4
- package/dist/commands/shared/followMode.d.ts +9 -1
- package/dist/commands/shared/followMode.js +22 -4
- package/dist/commands/shared/optionTypes.d.ts +9 -2
- package/dist/commands/shared/outputFile.js +6 -1
- package/dist/commands/start.d.ts +20 -5
- package/dist/commands/start.js +84 -23
- package/dist/commands/stop.d.ts +11 -0
- package/dist/commands/stop.js +24 -1
- package/dist/commands/tail.d.ts +7 -1
- package/dist/commands/tail.js +13 -62
- package/dist/commands.js +2 -0
- package/dist/connection/cdp.d.ts +7 -0
- package/dist/connection/cdp.js +9 -0
- package/dist/connection/launcher.js +3 -2
- package/dist/daemon/SessionController.js +6 -1
- package/dist/daemon/launcher.d.ts +3 -2
- package/dist/daemon/launcher.js +47 -3
- package/dist/daemon/session/Session.d.ts +4 -1
- package/dist/daemon/session/Session.js +33 -2
- package/dist/daemon/session/TelemetryStore.d.ts +8 -1
- package/dist/daemon/session/TelemetryStore.js +13 -1
- package/dist/daemon/session/commandRegistry.js +36 -14
- package/dist/daemon/session/interactions.d.ts +2 -1
- package/dist/daemon/session/interactions.js +13 -1
- package/dist/daemon/session/plugins.js +19 -53
- package/dist/daemon/session/teardown.js +1 -1
- package/dist/daemon.js +9234 -7222
- package/dist/errors/messages.d.ts +88 -15
- package/dist/errors/messages.js +177 -27
- package/dist/index.js +19322 -13961
- package/dist/ipc/client.d.ts +22 -2
- package/dist/ipc/client.js +34 -5
- package/dist/ipc/protocol/auditTypes.d.ts +135 -0
- package/dist/ipc/protocol/auditTypes.js +6 -0
- package/dist/ipc/protocol/commands.d.ts +35 -0
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +16 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +73 -8
- package/dist/ipc/session/types.d.ts +2 -0
- package/dist/runtime/css/search.d.ts +39 -0
- package/dist/runtime/css/search.js +122 -0
- package/dist/runtime/dom/actionEffects.d.ts +9 -2
- package/dist/runtime/dom/actionEffects.js +30 -14
- package/dist/runtime/dom/audit.d.ts +19 -0
- package/dist/runtime/dom/audit.js +37 -0
- package/dist/runtime/dom/auditModel.d.ts +45 -0
- package/dist/runtime/dom/auditModel.js +220 -0
- package/dist/runtime/dom/auditScripts.d.ts +113 -0
- package/dist/runtime/dom/auditScripts.js +148 -0
- package/dist/runtime/dom/elementGeometry.d.ts +16 -3
- package/dist/runtime/dom/elementGeometry.js +49 -10
- package/dist/runtime/dom/elementInfo.d.ts +74 -17
- package/dist/runtime/dom/elementInfo.js +187 -34
- package/dist/runtime/dom/evalHelpers.d.ts +12 -2
- package/dist/runtime/dom/evalHelpers.js +67 -7
- package/dist/runtime/dom/formDiscovery.d.ts +6 -2
- package/dist/runtime/dom/formDiscovery.js +20 -3
- package/dist/runtime/dom/formFillHelpers/fill.js +8 -12
- package/dist/runtime/dom/formFillHelpers/pressKey.js +2 -2
- package/dist/runtime/dom/formFillHelpers/shared.d.ts +16 -10
- package/dist/runtime/dom/formFillHelpers/shared.js +19 -52
- package/dist/runtime/dom/formSubmitHelpers.js +4 -3
- package/dist/runtime/dom/frameLayout.js +1 -0
- package/dist/runtime/dom/inspect.d.ts +7 -0
- package/dist/runtime/dom/inspect.js +92 -28
- package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
- package/dist/runtime/dom/inspectAllStyles.js +90 -7
- package/dist/runtime/dom/inspectCascade.d.ts +19 -2
- package/dist/runtime/dom/inspectCascade.js +214 -44
- package/dist/runtime/dom/inspectCascadeModel.d.ts +8 -0
- package/dist/runtime/dom/inspectCascadeModel.js +108 -34
- package/dist/runtime/dom/inspectHints.d.ts +26 -3
- package/dist/runtime/dom/inspectHints.js +125 -9
- package/dist/runtime/dom/inspectModel.d.ts +5 -1
- package/dist/runtime/dom/inspectModel.js +37 -10
- package/dist/runtime/dom/inspectPaintModel.d.ts +50 -22
- package/dist/runtime/dom/inspectPaintModel.js +182 -68
- package/dist/runtime/dom/inspectRules.d.ts +19 -0
- package/dist/runtime/dom/inspectRules.js +21 -5
- package/dist/runtime/dom/inspectScripts.d.ts +112 -12
- package/dist/runtime/dom/inspectScripts.js +357 -32
- package/dist/runtime/dom/inspectTree.js +10 -2
- package/dist/runtime/dom/inspectWhyModel.d.ts +2 -1
- package/dist/runtime/dom/inspectWhyModel.js +52 -10
- package/dist/runtime/dom/layout.js +40 -16
- package/dist/runtime/dom/reactEventHelpers.d.ts +21 -4
- package/dist/runtime/dom/reactEventHelpers.js +90 -36
- package/dist/runtime/dom/targetNode.d.ts +18 -5
- package/dist/runtime/dom/targetNode.js +268 -8
- package/dist/runtime/dom/wait.js +2 -1
- package/dist/runtime/page/bdgWorld.d.ts +57 -0
- package/dist/runtime/page/bdgWorld.js +180 -0
- package/dist/runtime/page/emulation.d.ts +13 -4
- package/dist/runtime/page/emulation.js +69 -4
- package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
- package/dist/runtime/page/replacedBuiltins.js +136 -0
- package/dist/runtime/page/userAgent.d.ts +17 -0
- package/dist/runtime/page/userAgent.js +57 -0
- package/dist/session/QueryCacheManager.d.ts +4 -1
- package/dist/session/QueryCacheManager.js +5 -2
- package/dist/session/chrome.d.ts +4 -1
- package/dist/session/chrome.js +7 -1
- package/dist/session/cleanup/staleSession.d.ts +21 -4
- package/dist/session/cleanup/staleSession.js +79 -9
- package/dist/session/cleanup/userCommands.d.ts +4 -1
- package/dist/session/cleanup/userCommands.js +10 -5
- package/dist/session/daemonSocket.d.ts +10 -0
- package/dist/session/daemonSocket.js +22 -0
- package/dist/session/lastSession.d.ts +6 -3
- package/dist/session/lastSession.js +11 -5
- package/dist/session/paths.d.ts +3 -1
- package/dist/session/paths.js +5 -5
- package/dist/session/portClaims.js +4 -3
- package/dist/session/sessionList.d.ts +13 -5
- package/dist/session/sessionList.js +31 -7
- package/dist/telemetry/a11y.js +2 -2
- package/dist/telemetry/console.d.ts +2 -1
- package/dist/telemetry/console.js +30 -21
- package/dist/telemetry/pageCrash.d.ts +26 -0
- package/dist/telemetry/pageCrash.js +53 -0
- package/dist/types.d.ts +20 -0
- package/dist/ui/formatters/audit.d.ts +19 -0
- package/dist/ui/formatters/audit.js +115 -0
- package/dist/ui/formatters/cdp.d.ts +138 -0
- package/dist/ui/formatters/cdp.js +131 -0
- package/dist/ui/formatters/console/chronological.js +3 -1
- package/dist/ui/formatters/console/follow.d.ts +2 -1
- package/dist/ui/formatters/console/follow.js +2 -2
- package/dist/ui/formatters/console/json.d.ts +2 -2
- package/dist/ui/formatters/console/json.js +11 -5
- package/dist/ui/formatters/console/shared.d.ts +30 -0
- package/dist/ui/formatters/console/shared.js +16 -0
- package/dist/ui/formatters/console/summarize.d.ts +9 -2
- package/dist/ui/formatters/console/summarize.js +40 -9
- package/dist/ui/formatters/console.d.ts +2 -1
- package/dist/ui/formatters/console.js +7 -5
- package/dist/ui/formatters/details.js +3 -1
- package/dist/ui/formatters/dom.d.ts +2 -2
- package/dist/ui/formatters/dom.js +10 -8
- package/dist/ui/formatters/helpFormatters.js +1 -1
- package/dist/ui/formatters/inspect.js +50 -17
- package/dist/ui/formatters/installSkill.d.ts +9 -1
- package/dist/ui/formatters/installSkill.js +32 -6
- package/dist/ui/formatters/layout.js +2 -1
- package/dist/ui/formatters/networkList.d.ts +1 -1
- package/dist/ui/formatters/networkList.js +1 -2
- package/dist/ui/formatters/preview.d.ts +2 -0
- package/dist/ui/formatters/preview.js +17 -7
- package/dist/ui/formatters/sessions.d.ts +2 -2
- package/dist/ui/formatters/sessions.js +9 -2
- package/dist/ui/formatters/status.js +1 -1
- package/dist/ui/logging/logger.d.ts +1 -1
- package/dist/ui/messages/commands.d.ts +168 -11
- package/dist/ui/messages/commands.js +245 -18
- package/dist/ui/messages/consoleMessages.d.ts +24 -0
- package/dist/ui/messages/consoleMessages.js +32 -0
- package/dist/ui/messages/preview.d.ts +12 -0
- package/dist/ui/messages/preview.js +18 -2
- package/dist/ui/messages/session.d.ts +13 -2
- package/dist/ui/messages/session.js +22 -3
- package/dist/utils/cssValues.js +36 -4
- package/dist/utils/decisionTrees.js +0 -5
- package/dist/utils/directories.d.ts +34 -0
- package/dist/utils/directories.js +88 -0
- package/dist/utils/display.d.ts +16 -0
- package/dist/utils/display.js +42 -0
- package/dist/utils/exitCodes.d.ts +1 -0
- package/dist/utils/exitCodes.js +6 -0
- package/dist/utils/process.d.ts +12 -0
- package/dist/utils/process.js +25 -0
- package/dist/utils/suggestions.d.ts +4 -2
- package/dist/utils/suggestions.js +7 -5
- package/dist/utils/taskMappings.js +1 -1
- package/package.json +3 -2
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'fs';
|
|
2
2
|
import { homedir } from 'os';
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
4
|
import { runCommand } from './shared/CommandRunner.js';
|
|
5
5
|
import { jsonOption } from './shared/commonOptions.js';
|
|
6
6
|
import { CommandError } from '../errors/index.js';
|
|
7
7
|
import { skillSourceMissingError, skillWriteFailedError } from '../errors/messages.js';
|
|
8
|
-
import { formatInstalledSkills } from '../ui/formatters/installSkill.js';
|
|
8
|
+
import { formatInstalledSkills, formatSkillTargets } from '../ui/formatters/installSkill.js';
|
|
9
|
+
import { createLogger } from '../ui/logging/index.js';
|
|
9
10
|
import { getErrorMessage } from '../utils/errors.js';
|
|
10
11
|
import { EXIT_CODES } from '../utils/exitCodes.js';
|
|
12
|
+
import { safeRemoveFile } from '../utils/file.js';
|
|
11
13
|
import { PACKAGE_ROOT } from '../utils/packageRoot.js';
|
|
14
|
+
const log = createLogger('bdg');
|
|
12
15
|
/** The skill shipped with the package (also used by agents working in this repo). */
|
|
13
16
|
const SKILL_SOURCE_PATH = join(PACKAGE_ROOT, '.claude', 'skills', 'bdg', 'SKILL.md');
|
|
14
17
|
/** Skill roots, relative to the home directory, of the agents the skill is installed for. */
|
|
@@ -17,14 +20,16 @@ const SKILL_ROOTS = {
|
|
|
17
20
|
agents: join('.agents', 'skills'),
|
|
18
21
|
};
|
|
19
22
|
/**
|
|
20
|
-
* Copy the bdg skill into each target's skill directory
|
|
21
|
-
* older
|
|
23
|
+
* Copy the bdg skill into each target's skill directory. A copy that differs
|
|
24
|
+
* (an older version, or one the user edited) is kept as `SKILL.md.bak`
|
|
25
|
+
* before it is overwritten. A target that cannot be written does not stop
|
|
26
|
+
* the others.
|
|
22
27
|
*
|
|
23
28
|
* @param targets - Agents to install for
|
|
24
29
|
* @param home - Home directory the skill roots are relative to
|
|
25
30
|
* @param source - SKILL.md to copy
|
|
26
|
-
* @returns
|
|
27
|
-
* @throws CommandError when the source is missing (83)
|
|
31
|
+
* @returns The targets written, in the given order, and the failure if any
|
|
32
|
+
* @throws CommandError when the source is missing (83)
|
|
28
33
|
*/
|
|
29
34
|
export function installSkill(targets, home = homedir(), source = SKILL_SOURCE_PATH) {
|
|
30
35
|
if (!existsSync(source)) {
|
|
@@ -32,31 +37,76 @@ export function installSkill(targets, home = homedir(), source = SKILL_SOURCE_PA
|
|
|
32
37
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.RESOURCE_NOT_FOUND);
|
|
33
38
|
}
|
|
34
39
|
const content = readFileSync(source, 'utf-8');
|
|
35
|
-
|
|
40
|
+
const skills = [];
|
|
41
|
+
const failures = [];
|
|
42
|
+
for (const target of targets) {
|
|
43
|
+
const result = writeSkill(target, join(home, SKILL_ROOTS[target], 'bdg', 'SKILL.md'), content);
|
|
44
|
+
if ('error' in result)
|
|
45
|
+
failures.push(result);
|
|
46
|
+
else
|
|
47
|
+
skills.push(result);
|
|
48
|
+
}
|
|
49
|
+
if (failures.length === 0)
|
|
50
|
+
return { skills };
|
|
51
|
+
return { skills, failure: skillFailureError(failures, targets) };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The error for targets the skill could not be written for. When only one of
|
|
55
|
+
* several targets failed, it suggests installing for the other only. Each
|
|
56
|
+
* distinct suggestion is kept, so every failure cause gets its fix.
|
|
57
|
+
*
|
|
58
|
+
* @param failures - Targets that failed (at least one)
|
|
59
|
+
* @param targets - Targets picked
|
|
60
|
+
* @returns Command error (82)
|
|
61
|
+
*/
|
|
62
|
+
function skillFailureError(failures, targets) {
|
|
63
|
+
const other = failures.length === 1 ? targets.find((t) => t !== failures[0]?.target) : undefined;
|
|
64
|
+
const errors = failures.map(({ path, error }) => {
|
|
65
|
+
const code = error.code;
|
|
66
|
+
return skillWriteFailedError(path, getErrorMessage(error), code === 'ENOTDIR' || code === 'EEXIST', other && `--${other}`);
|
|
67
|
+
});
|
|
68
|
+
return new CommandError(errors.map((err) => err.message).join('\n'), { suggestion: [...new Set(errors.map((err) => err.suggestion))].join('\n') }, EXIT_CODES.PERMISSION_DENIED);
|
|
36
69
|
}
|
|
37
70
|
/**
|
|
38
|
-
* Write the skill to one path unless it already holds the same content
|
|
71
|
+
* Write the skill to one path unless it already holds the same content; a
|
|
72
|
+
* different copy is first kept next to it as `SKILL.md.bak` (replacing an
|
|
73
|
+
* earlier backup), so edits to it are not lost. The new text is written to a
|
|
74
|
+
* temporary file first, so a failed write leaves both the copy and the
|
|
75
|
+
* backup as they were; the backup gets the default file mode, whatever the
|
|
76
|
+
* copy's was.
|
|
39
77
|
*
|
|
40
78
|
* @param target - Agent the path belongs to
|
|
41
79
|
* @param path - Destination SKILL.md
|
|
42
80
|
* @param content - Skill text
|
|
43
|
-
* @returns What happened to the file
|
|
44
|
-
*
|
|
81
|
+
* @returns What happened to the file, with the backup path when one was made,
|
|
82
|
+
* or the file that could not be written and why
|
|
45
83
|
*/
|
|
46
84
|
function writeSkill(target, path, content) {
|
|
47
85
|
const existing = existsSync(path) ? readFileSync(path, 'utf-8') : undefined;
|
|
48
86
|
if (existing === content) {
|
|
49
87
|
return { target, path, status: 'unchanged' };
|
|
50
88
|
}
|
|
89
|
+
const backup = existing === undefined ? undefined : `${path}.bak`;
|
|
90
|
+
const temporary = `${path}.tmp`;
|
|
91
|
+
let writing = path;
|
|
51
92
|
try {
|
|
52
93
|
mkdirSync(dirname(path), { recursive: true });
|
|
53
|
-
writeFileSync(
|
|
94
|
+
writeFileSync(temporary, content);
|
|
95
|
+
if (backup !== undefined) {
|
|
96
|
+
writing = backup;
|
|
97
|
+
rmSync(backup, { force: true });
|
|
98
|
+
writeFileSync(backup, existing ?? '');
|
|
99
|
+
}
|
|
100
|
+
writing = path;
|
|
101
|
+
renameSync(temporary, path);
|
|
54
102
|
}
|
|
55
|
-
catch (
|
|
56
|
-
|
|
57
|
-
|
|
103
|
+
catch (error) {
|
|
104
|
+
safeRemoveFile(temporary, 'temporary skill copy', log);
|
|
105
|
+
return { target, path: writing, error };
|
|
58
106
|
}
|
|
59
|
-
|
|
107
|
+
if (backup === undefined)
|
|
108
|
+
return { target, path, status: 'installed' };
|
|
109
|
+
return { target, path, status: 'updated', backup };
|
|
60
110
|
}
|
|
61
111
|
/**
|
|
62
112
|
* Targets picked by the flags; no flag means every agent.
|
|
@@ -68,6 +118,26 @@ function selectedTargets(options) {
|
|
|
68
118
|
const picked = Object.keys(SKILL_ROOTS).filter((target) => options[target]);
|
|
69
119
|
return picked.length > 0 ? picked : Object.keys(SKILL_ROOTS);
|
|
70
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Install the skill for the picked targets. When a target fails, the error
|
|
123
|
+
* still lists the targets that were written (and their backups): JSON
|
|
124
|
+
* `skills`, or the same lines as a successful install.
|
|
125
|
+
*
|
|
126
|
+
* @param options - Parsed command options
|
|
127
|
+
* @returns Command result
|
|
128
|
+
*/
|
|
129
|
+
function installSkillResult(options) {
|
|
130
|
+
const { skills, failure } = installSkill(selectedTargets(options));
|
|
131
|
+
if (!failure)
|
|
132
|
+
return { success: true, data: { skills } };
|
|
133
|
+
const written = skills.length === 0 ? {} : options.json ? { skills } : { written: formatSkillTargets(skills) };
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
error: failure.message,
|
|
137
|
+
exitCode: failure.exitCode,
|
|
138
|
+
errorContext: { ...written, ...failure.metadata },
|
|
139
|
+
};
|
|
140
|
+
}
|
|
71
141
|
/**
|
|
72
142
|
* Register the install-skill command.
|
|
73
143
|
*
|
|
@@ -81,7 +151,7 @@ export function registerInstallSkillCommand(program) {
|
|
|
81
151
|
.option('--agents', 'Only ~/.agents/skills (Codex, Gemini CLI and other agents)')
|
|
82
152
|
.addOption(jsonOption())
|
|
83
153
|
.action(async (options) => {
|
|
84
|
-
await runCommand((opts) => Promise.resolve(
|
|
154
|
+
await runCommand((opts) => Promise.resolve(installSkillResult(opts)), options, formatInstalledSkills);
|
|
85
155
|
});
|
|
86
156
|
}
|
|
87
157
|
//# sourceMappingURL=installSkill.js.map
|
|
@@ -6,7 +6,7 @@ import { runCommand } from '../shared/CommandRunner.js';
|
|
|
6
6
|
import { jsonOption } from '../shared/commonOptions.js';
|
|
7
7
|
import { noteFollowConnected } from '../shared/daemonErrorHandler.js';
|
|
8
8
|
import { fetchNetworkRequests, createErrorResult } from '../shared/dataFetcher.js';
|
|
9
|
-
import { followFetchFailure, setupFollowMode, } from '../shared/followMode.js';
|
|
9
|
+
import { followFetchFailure, newPageCrashes, setupFollowMode, } from '../shared/followMode.js';
|
|
10
10
|
import { handleValidationError } from '../shared/handleValidationError.js';
|
|
11
11
|
import { positiveIntRule, resourceTypeRule } from '../shared/validation.js';
|
|
12
12
|
import { applyFilters, getFilterHelpText, validateFilterString } from '../../telemetry/filterDsl.js';
|
|
@@ -14,6 +14,7 @@ import { resolvePreset, FILTER_PRESETS } from '../../telemetry/filterPresets.js'
|
|
|
14
14
|
import { filterByResourceType } from '../../telemetry/filters.js';
|
|
15
15
|
import { buildSuccessResponse } from '../../ui/OutputBuilder.js';
|
|
16
16
|
import { formatNetworkFollowRows, formatNetworkList, pageStartOf, } from '../../ui/formatters/networkList.js';
|
|
17
|
+
import { pageCrashedNote, withPageCrashedNote } from '../../ui/messages/commands.js';
|
|
17
18
|
import { followingNetworkMessage, stoppedFollowingNetworkMessage, } from '../../ui/messages/networkMessages.js';
|
|
18
19
|
import { EXIT_CODES } from '../../utils/exitCodes.js';
|
|
19
20
|
import { validateFilterOption } from './shared.js';
|
|
@@ -116,7 +117,8 @@ function buildFormatOptions(options, result, lastLimit) {
|
|
|
116
117
|
/**
|
|
117
118
|
* Stream network requests: the last `lastN` finished ones at start, then
|
|
118
119
|
* each request once, when it has finished loading or failed (a request whose
|
|
119
|
-
* headers arrived but whose body is still loading waits), like `tail -f
|
|
120
|
+
* headers arrived but whose body is still loading waits), like `tail -f`,
|
|
121
|
+
* with a warning (JSON `pageCrashedAt`) once when the page crashes.
|
|
120
122
|
*
|
|
121
123
|
* @param options - Command options
|
|
122
124
|
* @param resourceTypes - Validated resource types
|
|
@@ -124,6 +126,7 @@ function buildFormatOptions(options, result, lastLimit) {
|
|
|
124
126
|
*/
|
|
125
127
|
async function runFollowMode(options, resourceTypes, lastN) {
|
|
126
128
|
const shown = new Set();
|
|
129
|
+
const newCrash = newPageCrashes();
|
|
127
130
|
let started = false;
|
|
128
131
|
const showNetwork = async () => {
|
|
129
132
|
const result = await fetchNetworkRequests(filtersNeedHeaders(options));
|
|
@@ -131,25 +134,28 @@ async function runFollowMode(options, resourceTypes, lastN) {
|
|
|
131
134
|
return followFetchFailure(result, { json: options.json, retryIntervalMs: FOLLOW_INTERVAL });
|
|
132
135
|
}
|
|
133
136
|
noteFollowConnected();
|
|
134
|
-
const
|
|
135
|
-
const
|
|
137
|
+
const { requests } = result.data;
|
|
138
|
+
const crashedAt = newCrash(result.data.pageCrashedAt);
|
|
139
|
+
const finished = filterRequests(requests, options, resourceTypes).filter((request) => request.duration !== undefined && !shown.has(request.requestId));
|
|
140
|
+
const present = new Set(requests.map((request) => request.requestId));
|
|
136
141
|
for (const id of shown)
|
|
137
142
|
if (!present.has(id))
|
|
138
143
|
shown.delete(id);
|
|
139
144
|
finished.forEach((request) => shown.add(request.requestId));
|
|
140
145
|
const fresh = started || lastN === 0 ? finished : finished.slice(-lastN);
|
|
141
146
|
if (options.json) {
|
|
142
|
-
if (!started || fresh.length > 0) {
|
|
147
|
+
if (!started || fresh.length > 0 || crashedAt !== undefined) {
|
|
143
148
|
const data = {
|
|
144
149
|
requests: fresh,
|
|
145
|
-
totalCount:
|
|
150
|
+
totalCount: requests.length,
|
|
146
151
|
filteredCount: fresh.length,
|
|
152
|
+
...(crashedAt !== undefined && { pageCrashedAt: crashedAt }),
|
|
147
153
|
};
|
|
148
|
-
console.log(JSON.stringify(buildSuccessResponse(data)
|
|
154
|
+
console.log(JSON.stringify(buildSuccessResponse(data)));
|
|
149
155
|
}
|
|
150
156
|
}
|
|
151
157
|
else {
|
|
152
|
-
const pageStart = pageStartOf(
|
|
158
|
+
const pageStart = pageStartOf(requests);
|
|
153
159
|
const text = formatNetworkFollowRows(fresh, {
|
|
154
160
|
header: !started,
|
|
155
161
|
verbose: options.verbose ?? false,
|
|
@@ -157,6 +163,8 @@ async function runFollowMode(options, resourceTypes, lastN) {
|
|
|
157
163
|
});
|
|
158
164
|
if (text)
|
|
159
165
|
console.log(text);
|
|
166
|
+
if (crashedAt !== undefined)
|
|
167
|
+
console.log(pageCrashedNote(crashedAt));
|
|
160
168
|
}
|
|
161
169
|
started = true;
|
|
162
170
|
return undefined;
|
|
@@ -219,18 +227,20 @@ export function registerListCommand(networkCmd) {
|
|
|
219
227
|
}
|
|
220
228
|
return createErrorResult(result.error, result.exitCode, result.suggestion);
|
|
221
229
|
}
|
|
222
|
-
const
|
|
223
|
-
const
|
|
230
|
+
const { requests, pageCrashedAt } = result.data;
|
|
231
|
+
const filtered = filterRequests(requests, options, resourceTypes);
|
|
232
|
+
const pageStart = pageStartOf(requests);
|
|
224
233
|
return {
|
|
225
234
|
success: true,
|
|
226
235
|
data: {
|
|
227
236
|
requests: lastN === 0 ? filtered : filtered.slice(-lastN),
|
|
228
|
-
totalCount:
|
|
237
|
+
totalCount: requests.length,
|
|
229
238
|
filteredCount: filtered.length,
|
|
230
239
|
...(pageStart && { pageStart }),
|
|
240
|
+
...(pageCrashedAt !== undefined && { pageCrashedAt }),
|
|
231
241
|
},
|
|
232
242
|
};
|
|
233
|
-
}, options, (data) => formatNetworkList(data.requests, buildFormatOptions(options, data, lastN)));
|
|
243
|
+
}, options, (data) => withPageCrashedNote(formatNetworkList(data.requests, buildFormatOptions(options, data, lastN)), data.pageCrashedAt));
|
|
234
244
|
});
|
|
235
245
|
}
|
|
236
246
|
//# sourceMappingURL=list.js.map
|
|
@@ -10,13 +10,15 @@ import { MAX_EDGE_PX, PIXELS_PER_TOKEN, TALL_PAGE_THRESHOLD, } from './dom/scree
|
|
|
10
10
|
/** What DOM actions report about the network requests they triggered */
|
|
11
11
|
const TRIGGERED_REQUESTS_BEHAVIOR = 'Requests (and WebSocket connections) that start after the action begins are returned as triggeredRequests (method, url, status, durationMs; pending when still running at return, loading when the response arrived but its body is still streaming; with resourceType; human output lists documents, XHR/fetch and WebSockets first (up to 10) and counts static assets on one line; absent when network telemetry is off). Attribution is by time: requests a page timer or poller starts meanwhile are listed too, whether or not the action caused them';
|
|
12
12
|
/** What every DOM action reports about the page besides its requests */
|
|
13
|
-
const ACTION_EFFECTS_BEHAVIOR = 'The result also says what changed on the page: a navigation (Page: navigated to <url> (status), or URL changed to <url> (same document); JSON navigation { url, sameDocument, status }), and messages that appeared or changed in alert/status/aria-live elements or flash/error/toast-like classes (New text: "…" (element); JSON messages [{ text, element }], at most 3; after a navigation every message on the new page counts; texts of only digits and time units, such as clocks and counters, are left out, but other text that changes on its own, such as a rotating banner, can show up). Both are absent when nothing changed. Cost: one page script sent before the action without waiting for it and one read after it, a few ms; when the page does not answer (a pending navigation) bdg waits at most 200 ms for the snapshot and 250 ms per read, and the navigation is still reported from CDP events';
|
|
13
|
+
const ACTION_EFFECTS_BEHAVIOR = 'The result also says what changed on the page: a navigation (Page: navigated to <url> (status), or URL changed to <url> (same document); JSON navigation { url, sameDocument, status }), and messages that appeared or changed in alert/status/aria-live elements or flash/error/toast-like classes (New text: "…" (element); JSON messages [{ text, element }], at most 3, with "(+N more)" and moreMessages for the rest; after a navigation every message on the new page counts; texts of only digits and time units, such as clocks and counters, are left out, but other text that changes on its own, such as a rotating banner, can show up). Both are absent when nothing changed. Cost: one page script sent before the action without waiting for it and one read after it, a few ms; when the page does not answer (a pending navigation) bdg waits at most 200 ms for the snapshot and 250 ms per read, and the navigation is still reported from CDP events';
|
|
14
14
|
/** What click and pressKey report when the page was still changing as they returned */
|
|
15
15
|
const STILL_CHANGING_BEHAVIOR = 'When the page was still changing as the action returned, the status line says (page still changing), a note below it says what was pending and suggests bdg dom wait <selector>, and JSON has settled: false with pending { requests (document, fetch/XHR and script requests still running), navigation (a new page still loading), loading (a loading indicator that appeared, e.g. "div#loading"), domChanging (DOM changes kept coming in bursts over a second look 250 ms later; a single render, ticking text and style animations do not count), busy (the page did not answer within 250 ms: a long script) }; absent when the page looked settled (exit code stays 0). A result a timer renders later, with no DOM change, request or loading indicator before it, is not detected. Cost: nothing extra, except 250 ms plus one read when the DOM looked busy. Not checked with --no-wait';
|
|
16
16
|
/** What hover and pressKey report about elements they showed */
|
|
17
17
|
const SHOWN_BEHAVIOR = 'Elements the action showed are listed (Shown: <element> "<text>"; JSON shown [{ text, element }], at most 3, outermost first): elements with visible text added inside the target\'s form, search box, dialog or combobox (else its grandparent, or its parent when that is the body), and popups and messages added anywhere (tooltip, menu, listbox, dialog, alert, status roles, popover, aria-live, message-like classes); widgets elsewhere on the page and re-rendered elements whose text was there before do not count';
|
|
18
18
|
/** What `--no-wait` does to a DOM action's triggered requests */
|
|
19
19
|
const NO_WAIT_TRIGGERED_REQUESTS = 'Returns immediately without waiting for network; triggeredRequests lists only requests bdg saw start before returning (often none yet; check bdg network list later)';
|
|
20
|
+
/** How every follow mode (peek, console, network list) runs and ends */
|
|
21
|
+
const FOLLOW_BEHAVIOR = 'Stops with exit 83 when the session it follows ends, 130 on Ctrl-C, 143 on SIGTERM; with --json prints one compact object per line (NDJSON). Other failures (a busy page, a timeout) are retried: reported once in text, as one error line per refresh in JSON';
|
|
20
22
|
/**
|
|
21
23
|
* Behavioral metadata registry.
|
|
22
24
|
*
|
|
@@ -25,8 +27,12 @@ const NO_WAIT_TRIGGERED_REQUESTS = 'Returns immediately without waiting for netw
|
|
|
25
27
|
const OPTION_BEHAVIORS = {
|
|
26
28
|
'screenshot:--selector': {
|
|
27
29
|
default: 'Captures the page (full page unless --no-full-page)',
|
|
28
|
-
whenEnabled: 'Captures one element; the selector (or a query index) can also be given as the second argument: bdg dom screenshot out.png "#sel".
|
|
29
|
-
automaticBehavior:
|
|
30
|
+
whenEnabled: 'Captures one element; the selector (or a query index) can also be given as the second argument: bdg dom screenshot out.png "#sel". With --index it picks that match of the selector (--selector ".item" --index 2). A positional and an option naming different elements exits 81',
|
|
31
|
+
automaticBehavior: "The capture covers the border box plus what overflows it: uncleared floats, positioned children, text past a tight line height, and the element's own box shadows and outline (a focus ring); not what an overflow: hidden ancestor cuts off, nor fixed descendants. JSON element.bounds is the border box and element.captured the larger area when it grew, which human output notes. An element smaller than the viewport is scrolled into view for the capture and the page scroll put back afterwards; a larger one is captured with the page laid out at its width without scrollbars, so it does not shift",
|
|
32
|
+
},
|
|
33
|
+
'screenshot:--padding': {
|
|
34
|
+
default: 'The element capture is its painted area (border box, overflowing content, shadows, outline)',
|
|
35
|
+
whenEnabled: 'Adds that many CSS px of the page around the element capture on every side (0-500); without an element it exits 81',
|
|
30
36
|
},
|
|
31
37
|
'screenshot:--no-resize': {
|
|
32
38
|
default: `Images auto-resized to max ${MAX_EDGE_PX}px longest edge for Claude Vision optimization (~1,600 tokens)`,
|
|
@@ -69,12 +75,12 @@ const OPTION_BEHAVIORS = {
|
|
|
69
75
|
'get:--index': {
|
|
70
76
|
default: 'Returns the first matching element (body without a selector)',
|
|
71
77
|
whenEnabled: 'Returns that match of the selector (0-based), in semantic and --raw output; --nth is an alias',
|
|
72
|
-
automaticBehavior: '
|
|
78
|
+
automaticBehavior: 'Past the last match exits 81; a numeric index argument (a cached query index) past the indexed matches, or from an earlier page, exits 87 (re-run dom query)',
|
|
73
79
|
},
|
|
74
80
|
'query:--limit': {
|
|
75
|
-
default: 'dom a11y query
|
|
81
|
+
default: 'dom query and dom a11y query list the first 50 matches and say how many more there are; --json returns 1000 (dom query) or all of them (dom a11y query)',
|
|
76
82
|
whenEnabled: 'Lists that many matches (0 = all), in human and JSON output; count is always the total, JSON omitted the rest',
|
|
77
|
-
automaticBehavior: '
|
|
83
|
+
automaticBehavior: 'Matches are cached for index-based access (bdg dom click 55 works even when 50 are listed): all of them for dom a11y query, the first 1000 (or --limit, if higher) for dom query, which describes only those, so a page with 50000 matches answers in under a second; an element the page and frame trees both report is listed once. Indices work with click, fill, hover, pressKey, scroll, submit, layout, get and listeners, also for elements of a cross-origin iframe of the same site (a consent dialog), whose scripts then run in that frame',
|
|
78
84
|
tokenImpact: 'About one line per match; a page can have hundreds of links',
|
|
79
85
|
},
|
|
80
86
|
'eval:--frame': {
|
|
@@ -93,16 +99,16 @@ const OPTION_BEHAVIORS = {
|
|
|
93
99
|
automaticBehavior: 'Page navigations create new "navigation contexts" - default filters to latest context',
|
|
94
100
|
},
|
|
95
101
|
'console:-l': {
|
|
96
|
-
default: 'Smart summary with errors deduplicated and warnings grouped',
|
|
102
|
+
default: 'Smart summary with errors deduplicated and warnings grouped: the newest 50 distinct errors and warnings, with a note for the earlier ones. The session keeps the newest 10000 messages; dropped ones are counted (dropped in JSON)',
|
|
97
103
|
whenEnabled: 'Lists all messages chronologically without deduplication',
|
|
98
104
|
},
|
|
99
105
|
'console:--list': {
|
|
100
|
-
default: 'Smart summary with errors deduplicated and warnings grouped',
|
|
106
|
+
default: 'Smart summary with errors deduplicated and warnings grouped: the newest 50 distinct errors and warnings, with a note for the earlier ones. The session keeps the newest 10000 messages; dropped ones are counted (dropped in JSON)',
|
|
101
107
|
whenEnabled: 'Lists all messages chronologically without deduplication',
|
|
102
108
|
},
|
|
103
109
|
'console:--last': {
|
|
104
110
|
default: 'Smart summary (without --list); a list shows the last 100 messages',
|
|
105
|
-
whenEnabled: 'Lists the last N messages (0 = all) chronologically, also without --list; JSON gets messages',
|
|
111
|
+
whenEnabled: 'Lists the last N messages (0 = all) chronologically, also without --list; JSON gets messages and N distinct errors and warnings (0 = all; default 50)',
|
|
106
112
|
automaticBehavior: 'The [n] shown are positions in the session message list (what bdg details console <n> takes); when the page or level filter left messages out between the listed ones, a note says how many and why',
|
|
107
113
|
},
|
|
108
114
|
'console:--level': {
|
|
@@ -122,7 +128,7 @@ const OPTION_BEHAVIORS = {
|
|
|
122
128
|
'click:--no-wait': {
|
|
123
129
|
default: 'Waits for network stability after click (150ms idle, up to 2s)',
|
|
124
130
|
whenDisabled: NO_WAIT_TRIGGERED_REQUESTS,
|
|
125
|
-
automaticBehavior: `Network wait helps ensure AJAX requests triggered by click complete. ${TRIGGERED_REQUESTS_BEHAVIOR}. The click itself uses real mouse events in the visible part of the element (method "mouse"); if the element is covered or has no size it falls back to DOM events (method "dom", with a warning; --strict refuses instead). Results the page shows later (timers, spinners, slow renders) are not waited for but reported as pending work: use bdg dom wait <selector> --visible. ${ACTION_EFFECTS_BEHAVIOR}. ${STILL_CHANGING_BEHAVIOR}. A click with no DOM change, no request and no
|
|
131
|
+
automaticBehavior: `Network wait helps ensure AJAX requests triggered by click complete. ${TRIGGERED_REQUESTS_BEHAVIOR}. The click itself uses real mouse events in the visible part of the element (method "mouse"); if the element is covered or has no size it falls back to DOM events (method "dom", with a warning; --strict refuses instead). Results the page shows later (timers, spinners, slow renders) are not waited for but reported as pending work: use bdg dom wait <selector> --visible. ${ACTION_EFFECTS_BEHAVIOR}. ${STILL_CHANGING_BEHAVIOR}. A click with no DOM change, no request, no navigation and no console message (checked again 300 ms later, which adds 300 ms plus at most 250 ms for the read) is reported as ⚠ Element Clicked (no visible effect observed: no DOM change, requests or navigation within 300 ms) and effect: "none" in JSON (exit code stays 0); not claimed with --no-wait, for hover or right-click, after a copy or cut, or when the click hit a form control, label, media, iframe, popover button, a mailto:/tel:/javascript: or other non-http link, a link to another window or a custom element with a closed shadow root, or moved focus to an element that is not a button or link. Effects outside the DOM (CSS :hover/:focus-within styles, canvas, clipboard without a copy event) are not seen`,
|
|
126
132
|
tokenImpact: 'A click that navigates lists the whole page load in JSON triggeredRequests',
|
|
127
133
|
},
|
|
128
134
|
'click:--double': {
|
|
@@ -141,7 +147,7 @@ const OPTION_BEHAVIORS = {
|
|
|
141
147
|
'hover:--no-wait': {
|
|
142
148
|
default: 'Waits for network stability after moving the mouse (menus may load content)',
|
|
143
149
|
whenDisabled: NO_WAIT_TRIGGERED_REQUESTS,
|
|
144
|
-
automaticBehavior: `The mouse stays over the element afterwards, so hover menus stay open until the next mouse action. ${TRIGGERED_REQUESTS_BEHAVIOR}. ${ACTION_EFFECTS_BEHAVIOR}. ${SHOWN_BEHAVIOR}; for a hover also elements around it (its parent's subtree) and tooltips, menus, listboxes, dialogs and popovers anywhere that were hidden before, so captions shown by CSS :hover count (hidden elements noted by identity right before the mouse moves: up to 1500, within 8 ms). A hover never claims "no visible effect" and does not check whether the page was still changing`,
|
|
150
|
+
automaticBehavior: `The element is scrolled into view first; when the page moved, Scrolled (JSON scrolledBy) says how far. The mouse stays over the element afterwards, so hover menus stay open until the next mouse action. ${TRIGGERED_REQUESTS_BEHAVIOR}. ${ACTION_EFFECTS_BEHAVIOR}. ${SHOWN_BEHAVIOR}; for a hover also elements around it (its parent's subtree) and tooltips, menus, listboxes, dialogs and popovers anywhere that were hidden before, so captions shown by CSS :hover count (hidden elements noted by identity right before the mouse moves: up to 1500, within 8 ms). A hover never claims "no visible effect" and does not check whether the page was still changing`,
|
|
145
151
|
},
|
|
146
152
|
'hover:--strict': {
|
|
147
153
|
default: 'A covered, hidden or zero-size element gets synthetic mouseover/mouseenter events (method "dom", with a warning)',
|
|
@@ -208,22 +214,28 @@ const OPTION_BEHAVIORS = {
|
|
|
208
214
|
whenEnabled: 'Lists every listener of framework roots individually',
|
|
209
215
|
tokenImpact: 'On React pages --all adds a row per event type and phase (about 140 rows, 60 KB of JSON)',
|
|
210
216
|
},
|
|
217
|
+
'audit:--level': {
|
|
218
|
+
default: 'Text must reach WCAG AA: 4.5, or 3 for large text (24px, or 18.66px bold); every text-drawing element is checked, composited like dom inspect',
|
|
219
|
+
whenEnabled: '--level AAA asks 7, or 4.5 for large text',
|
|
220
|
+
automaticBehavior: 'One walk over the rendered elements (open shadow roots included, at most 20000; capped says when it stopped). Findings are sorted weakest first; --limit (default 20) lists that many per check and the rest are counted. Overflow leaves out content inside horizontal scrollers and visually-hidden 1px text; identical findings are grouped (×N). Contrast is approximate (approximate: …) when something is painted behind or on top of text in view (hit-tested), or for text out of view whose ancestors paint nothing below body (only its ancestors were checked). Canvas animations cannot be listed; visible canvas elements are counted (canvases)',
|
|
221
|
+
tokenImpact: 'About one line per finding; --limit bounds it',
|
|
222
|
+
},
|
|
211
223
|
'layout:--index': {
|
|
212
224
|
default: 'Reports every match of the selector (human output lists the first 20, JSON up to 100 plus an omitted count); a numeric argument reports that cached query element',
|
|
213
225
|
whenEnabled: 'Reports only the nth match (0-based); out of range exits 81',
|
|
214
|
-
automaticBehavior: 'Coordinates are CSS px: bounds relative to the top-level page (iframe offsets and page scroll included), viewport relative to the visible area. Iframes and overflow containers (scroll lists, overflow: hidden) clip what counts as visible (clippedBy names the one cutting it off). scrollBy brings the whole element into view and is limited to how far the page can scroll: for an element out of view it centres it (aligns its start when it is larger than the viewport; human output says "to centre it"), for a partly visible one it is the smallest scroll that shows all of it (the part cut off at the top or bottom; the start of one larger than the viewport; "partly visible (87%); scroll up 5px to see all of it"). Elements a page script moves on scroll (floating menus) may move again after it; fixed and sticky elements (page scroll does not move them, or only until they stick) and ones beyond that range get offScreenReason instead, which says "page scrolling is locked (…)" when the page cannot scroll because body/html is position: fixed or overflow: hidden, so in-flow content is not called fixed; a visible dialog (dialog[open], [aria-modal=true], [role=dialog|alertdialog]) is named as the likely cause ("likely by dialog div#consent"). page.viewport is the layout viewport without scrollbars, as dom scroll reports it; page.colorScheme is the prefers-color-scheme media feature the page sees (not the theme it renders). Content in a closed <details> or under content-visibility: hidden is hidden. coveredBy is the
|
|
226
|
+
automaticBehavior: 'Coordinates are CSS px: bounds relative to the top-level page (iframe offsets and page scroll included), viewport relative to the visible area. Iframes and overflow containers (scroll lists, overflow: hidden) clip what counts as visible (clippedBy names the one cutting it off). scrollBy brings the whole element into view and is limited to how far the page can scroll: for an element out of view it centres it (aligns its start when it is larger than the viewport; human output says "to centre it"), for a partly visible one it is the smallest scroll that shows all of it (the part cut off at the top or bottom; the start of one larger than the viewport; "partly visible (87%); scroll up 5px to see all of it"). Elements a page script moves on scroll (floating menus) may move again after it; fixed and sticky elements (page scroll does not move them, or only until they stick) and ones beyond that range get offScreenReason instead, which says "page scrolling is locked (…)" when the page cannot scroll because body/html is position: fixed or overflow: hidden, so in-flow content is not called fixed; a visible dialog (dialog[open], [aria-modal=true], [role=dialog|alertdialog]) is named as the likely cause ("likely by dialog div#consent"). page.viewport is the layout viewport without scrollbars, as dom scroll reports it; page.colorScheme is the prefers-color-scheme media feature the page sees (not the theme it renders). Content in a closed <details> or under content-visibility: hidden is hidden. coveredBy is the first element painted above it at the center of the largest visible box that paints there (the background of a sticky header rather than the transparent logo on it; inside a shadow host, what its shadow root paints), else the topmost one with coverTransparent (none for pointer-events: none, nor for an element of the same click target: an overlay inside the link, button or label the element is in, a link to the same URL, or the textless absolutely positioned overlay link spanning the card that holds plain content); inert elements are flagged, not hidden',
|
|
215
227
|
tokenImpact: 'About one line per element; a cheap alternative to screenshots for "where is it?"',
|
|
216
228
|
},
|
|
217
229
|
'inspect:--index': {
|
|
218
230
|
default: 'Inspects the first rendered match (the first when none is rendered) and notes how many matched; a numeric argument inspects that cached element (from dom query, dom form or dom a11y query)',
|
|
219
231
|
whenEnabled: 'Inspects the nth match (0-based); out of range exits 81',
|
|
220
232
|
automaticBehavior: 'Answers "what does it look like" without a screenshot, grouped like Figma Dev Mode: header (element, text, size and page position, [flex]/[grid], [not rendered]/[hidden]/[offscreen]/[covered by …], prefers-color-scheme), box (margin, padding, border widths, box-sizing, overflow, scroll size), layout (display, position, flex/grid container and item settings), parent (its display and layout, distances to its content edges, gaps to the neighbouring siblings), text (first font family → the font Chrome rendered, (webfont) or local; weight size/line-height; color; WCAG contrast against the composited background; only for elements with text), fill, border (sides, radius, outline), fx (shadow, transform, filter, opacity, blend), state (cursor, pointer-events, user-select, appearance), pseudo (::before/::after with content, ::placeholder) and a child tree (depth 2, 20 rows, identical siblings grouped). Values that change nothing (0, none, transparent, normal) are left out; colors are hex (lab/oklch from Tailwind converted), lengths px without the unit, rounded to 0.1. Secrets are never shown. JSON uses Figma-aligned names (rect, box, layout.sizing hug/fill/fixed, text, fills, strokes, radius, effects, children). Also by default: hints, the element\'s own declarations that have no effect (justify-content on a block, width on an inline element, top on a static one, var() of an unset custom property) with the reason, the fix and the rule\'s file:line',
|
|
221
|
-
tokenImpact: 'About
|
|
233
|
+
tokenImpact: 'About 80–130 tokens for a button: 80–100 for the styles, up to 50 per hint (--no-hints drops them), and 30–70 more for a child tree (--tree 0 drops it), against about 1,500 for a screenshot or 3,000+ for raw computed styles',
|
|
222
234
|
},
|
|
223
235
|
'inspect:--all': {
|
|
224
236
|
default: 'Shows the curated groups (the properties that define the look)',
|
|
225
237
|
whenEnabled: 'Lists every computed property that differs from the default of the same element type, longhands collapsed into shorthands, noise (logical duplicates, currentColor echoes, custom properties) dropped',
|
|
226
|
-
tokenImpact: 'About
|
|
238
|
+
tokenImpact: 'About as many tokens as the curated groups (about 90 for a button)',
|
|
227
239
|
},
|
|
228
240
|
'inspect:--props': {
|
|
229
241
|
default: 'Shows the curated groups',
|
|
@@ -286,16 +298,36 @@ const OPTION_BEHAVIORS = {
|
|
|
286
298
|
},
|
|
287
299
|
'peek:-f': {
|
|
288
300
|
default: 'Shows snapshot of current data',
|
|
289
|
-
whenEnabled: 'Continuous monitoring - refreshes every second
|
|
301
|
+
whenEnabled: 'Continuous monitoring (like tail -f): refreshes every second, or every --interval ms (100-60000). Replaces the deprecated bdg tail',
|
|
302
|
+
automaticBehavior: FOLLOW_BEHAVIOR,
|
|
290
303
|
},
|
|
291
304
|
'peek:--follow': {
|
|
292
305
|
default: 'Shows snapshot of current data',
|
|
293
|
-
whenEnabled: 'Continuous monitoring - refreshes every second
|
|
306
|
+
whenEnabled: 'Continuous monitoring (like tail -f): refreshes every second, or every --interval ms (100-60000). Replaces the deprecated bdg tail',
|
|
307
|
+
automaticBehavior: FOLLOW_BEHAVIOR,
|
|
308
|
+
},
|
|
309
|
+
'console:-f': {
|
|
310
|
+
default: 'Prints the messages logged so far and exits',
|
|
311
|
+
whenEnabled: 'Streams new messages as they come (the last --last at start)',
|
|
312
|
+
automaticBehavior: FOLLOW_BEHAVIOR,
|
|
313
|
+
},
|
|
314
|
+
'list:-f': {
|
|
315
|
+
default: 'Lists the requests captured so far and exits',
|
|
316
|
+
whenEnabled: 'Streams requests as they finish',
|
|
317
|
+
automaticBehavior: FOLLOW_BEHAVIOR,
|
|
294
318
|
},
|
|
295
319
|
'peek:-v': {
|
|
296
320
|
default: 'Compact output (truncated URLs, no resource types)',
|
|
297
321
|
whenEnabled: 'Verbose output with full URLs and resource types',
|
|
298
322
|
},
|
|
323
|
+
'start:--headless': {
|
|
324
|
+
default: 'A window when there is a display: on macOS unless over SSH (SSH_CONNECTION, SSH_TTY) or CI is set; on Linux when DISPLAY or WAYLAND_DISPLAY is set. Servers, containers and CI run headless',
|
|
325
|
+
whenEnabled: 'Chrome runs without a window (pass it when running unattended on a Mac)',
|
|
326
|
+
},
|
|
327
|
+
'start:--no-headless': {
|
|
328
|
+
default: 'A window when there is a display: on macOS unless over SSH (SSH_CONNECTION, SSH_TTY) or CI is set; on Linux when DISPLAY or WAYLAND_DISPLAY is set',
|
|
329
|
+
whenEnabled: 'Chrome shows its window even without a detected display (it fails without one)',
|
|
330
|
+
},
|
|
299
331
|
'start:--all': {
|
|
300
332
|
default: 'Tracking/analytics requests and console noise are filtered; bodies of binary responses (images, fonts) are not captured',
|
|
301
333
|
whenEnabled: 'Everything is captured, including binary response bodies (base64, flagged by responseBodyBase64, within --max-body-size)',
|
|
@@ -326,6 +358,11 @@ const OPTION_BEHAVIORS = {
|
|
|
326
358
|
whenEnabled: 'The page gets exactly that viewport (CSS px, e.g. 1280x800) for the whole session, through navigations and reloads (Emulation.setDeviceMetricsOverride at the display pixel ratio); a launched Chrome also opens its window at that size, so tabs the page opens get it too',
|
|
327
359
|
automaticBehavior: 'Works with --chrome-ws-url: the override belongs to the session, and Chrome drops it when the session ends, so the attached browser gets its own size back. bdg status shows the resulting layout viewport without the scrollbar (Viewport: 1265×800 (emulated 1280x800)). Invalid sizes (not WxH, a side outside 1-10000) exit 81',
|
|
328
360
|
},
|
|
361
|
+
'bdg:--mobile': {
|
|
362
|
+
default: 'A desktop viewport: classic scrollbars take ~15px of the width, no touch, a desktop user agent',
|
|
363
|
+
whenEnabled: 'Emulates a phone for the whole session: a mobile viewport (390x844 unless --viewport) at pixel ratio 3 with mobile layout (meta viewport, overlay scrollbars, so 100vw fits), touch (pointer: coarse, maxTouchPoints 5) and an Android Chrome user agent with mobile client hints; bdg page emulate --mobile turns it on mid-session, --viewport WxH without --mobile or --reset turns it off',
|
|
364
|
+
automaticBehavior: 'Screenshots keep the mobile layout and are taken at pixel ratio 1 (CSS px = image px); bdg status shows "(emulated 390x844, phone)"',
|
|
365
|
+
},
|
|
329
366
|
'bdg:--color-scheme': {
|
|
330
367
|
default: 'The page sees the system setting for prefers-color-scheme (headless Chrome follows the OS, so a dark OS renders dark pages); bdg status and dom layout show which one',
|
|
331
368
|
whenEnabled: 'Emulates prefers-color-scheme: light or dark for the whole session (Emulation.setEmulatedMedia); other values exit 81 with a suggestion',
|
package/dist/commands/page.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { Option } from 'commander';
|
|
6
6
|
import { noActiveSessionError, runCommand } from './shared/CommandRunner.js';
|
|
7
7
|
import { jsonOption } from './shared/commonOptions.js';
|
|
8
|
-
import { parseColorScheme,
|
|
8
|
+
import { parseColorScheme, requestedViewport } from './start.js';
|
|
9
9
|
import { CommandError } from '../errors/index.js';
|
|
10
10
|
import { javascriptNavigationError } from '../errors/messages.js';
|
|
11
11
|
import { getStatus, pageEmulate, pageNavigate } from '../ipc/client.js';
|
|
@@ -127,12 +127,13 @@ async function showPageInfo(options) {
|
|
|
127
127
|
function emulationRequest(options) {
|
|
128
128
|
if (options.reset)
|
|
129
129
|
return { reset: true };
|
|
130
|
-
if (options.viewport === undefined && options.colorScheme === undefined) {
|
|
130
|
+
if (options.viewport === undefined && options.colorScheme === undefined && !options.mobile) {
|
|
131
131
|
const err = pageEmulateNothingError();
|
|
132
132
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
133
133
|
}
|
|
134
|
+
const viewport = requestedViewport(options.viewport, options.mobile);
|
|
134
135
|
return {
|
|
135
|
-
...(
|
|
136
|
+
...(viewport && { viewport }),
|
|
136
137
|
...(options.colorScheme !== undefined && {
|
|
137
138
|
colorScheme: parseColorScheme(options.colorScheme),
|
|
138
139
|
}),
|
|
@@ -188,11 +189,13 @@ export function registerPageCommands(program) {
|
|
|
188
189
|
page
|
|
189
190
|
.command('emulate')
|
|
190
191
|
.description(PAGE_EMULATE_DESCRIPTION)
|
|
191
|
-
.option('--viewport <WxH>', 'Viewport size in CSS px, e.g. 900x700')
|
|
192
|
+
.option('--viewport <WxH>', 'Viewport size in CSS px, e.g. 900x700 (a desktop one unless --mobile)')
|
|
192
193
|
.option('--color-scheme <scheme>', 'Emulate prefers-color-scheme: light or dark')
|
|
194
|
+
.option('--mobile', 'Emulate a phone: mobile viewport (390x844 unless --viewport), touch, mobile user agent')
|
|
193
195
|
.addOption(new Option('--reset', 'Back to the browser window size and the system setting').conflicts([
|
|
194
196
|
'viewport',
|
|
195
197
|
'colorScheme',
|
|
198
|
+
'mobile',
|
|
196
199
|
]))
|
|
197
200
|
.addOption(jsonOption())
|
|
198
201
|
.action(async (options) => {
|
package/dist/commands/peek.d.ts
CHANGED
|
@@ -2,5 +2,12 @@
|
|
|
2
2
|
* Peek command for previewing collected session data.
|
|
3
3
|
*/
|
|
4
4
|
import type { Command } from 'commander';
|
|
5
|
+
import type { PeekCommandOptions } from './shared/optionTypes.js';
|
|
6
|
+
/**
|
|
7
|
+
* Watch the session data (`peek --follow`, and the deprecated `tail`).
|
|
8
|
+
*
|
|
9
|
+
* @param options - Peek options (follow implied)
|
|
10
|
+
*/
|
|
11
|
+
export declare function followPreview(options: PeekCommandOptions): Promise<void>;
|
|
5
12
|
export declare function registerPeekCommand(program: Command): void;
|
|
6
13
|
//# sourceMappingURL=peek.d.ts.map
|