browser-debugger-cli 0.12.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 +1 -0
- 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/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/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 +8 -7
- 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/query.d.ts +27 -3
- package/dist/commands/dom/helpers/query.js +152 -64
- package/dist/commands/dom/helpers/screenshot.js +13 -13
- package/dist/commands/dom/index.js +4 -2
- package/dist/commands/dom/query.d.ts +19 -2
- package/dist/commands/dom/query.js +37 -6
- package/dist/commands/dom/screenshot.js +2 -1
- package/dist/commands/dom/semanticUtils.d.ts +3 -2
- package/dist/commands/dom/semanticUtils.js +40 -9
- package/dist/commands/helpJson.d.ts +82 -19
- package/dist/commands/helpJson.js +111 -40
- 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 +33 -11
- 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 +4 -1
- package/dist/commands/shared/outputFile.js +6 -1
- package/dist/commands/start.d.ts +7 -5
- package/dist/commands/start.js +65 -21
- package/dist/commands/stop.d.ts +11 -0
- package/dist/commands/stop.js +24 -1
- package/dist/commands.js +1 -1
- 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 +29 -13
- package/dist/daemon/session/interactions.d.ts +2 -1
- package/dist/daemon/session/interactions.js +13 -1
- package/dist/daemon/session/plugins.js +16 -2
- package/dist/daemon/session/teardown.js +1 -1
- package/dist/daemon.js +1622 -748
- package/dist/errors/messages.d.ts +54 -11
- package/dist/errors/messages.js +109 -22
- package/dist/index.js +13733 -8796
- package/dist/ipc/client.d.ts +18 -2
- package/dist/ipc/client.js +26 -5
- package/dist/ipc/protocol/auditTypes.d.ts +8 -2
- package/dist/ipc/protocol/commands.d.ts +12 -0
- package/dist/ipc/protocol/domTypes.d.ts +12 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +2 -0
- package/dist/ipc/session/types.d.ts +2 -0
- package/dist/runtime/dom/actionEffects.d.ts +5 -1
- package/dist/runtime/dom/actionEffects.js +26 -14
- package/dist/runtime/dom/audit.js +3 -2
- package/dist/runtime/dom/auditModel.js +6 -1
- package/dist/runtime/dom/auditScripts.d.ts +9 -3
- package/dist/runtime/dom/auditScripts.js +41 -5
- package/dist/runtime/dom/elementGeometry.d.ts +10 -3
- package/dist/runtime/dom/elementGeometry.js +27 -4
- package/dist/runtime/dom/elementInfo.d.ts +74 -18
- package/dist/runtime/dom/elementInfo.js +187 -40
- 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 +7 -11
- 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.js +5 -6
- package/dist/runtime/dom/inspectAllStyles.js +1 -0
- package/dist/runtime/dom/inspectHints.d.ts +1 -1
- package/dist/runtime/dom/inspectModel.d.ts +2 -1
- package/dist/runtime/dom/inspectModel.js +7 -3
- package/dist/runtime/dom/inspectPaintModel.d.ts +2 -0
- package/dist/runtime/dom/inspectPaintModel.js +3 -1
- package/dist/runtime/dom/inspectScripts.d.ts +29 -2
- package/dist/runtime/dom/inspectScripts.js +49 -10
- package/dist/runtime/dom/layout.js +9 -7
- package/dist/runtime/dom/reactEventHelpers.d.ts +14 -4
- package/dist/runtime/dom/reactEventHelpers.js +63 -27
- 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/replacedBuiltins.d.ts +28 -0
- package/dist/runtime/page/replacedBuiltins.js +136 -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 +16 -0
- package/dist/ui/formatters/audit.js +14 -5
- 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 +1 -1
- package/dist/ui/formatters/dom.js +5 -6
- package/dist/ui/formatters/helpFormatters.js +1 -1
- package/dist/ui/formatters/inspect.js +9 -3
- 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/logging/logger.d.ts +1 -1
- package/dist/ui/messages/commands.d.ts +124 -4
- package/dist/ui/messages/commands.js +162 -7
- package/dist/ui/messages/consoleMessages.d.ts +24 -0
- package/dist/ui/messages/consoleMessages.js +32 -0
- package/dist/ui/messages/preview.d.ts +6 -0
- package/dist/ui/messages/preview.js +9 -1
- package/dist/ui/messages/session.d.ts +13 -2
- package/dist/ui/messages/session.js +22 -3
- 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/package.json +1 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `bdg help <command...>` topics and Commander usage-error hints.
|
|
3
3
|
*/
|
|
4
|
+
import { commandPath } from './helpJson.js';
|
|
4
5
|
import { CommandError } from '../errors/index.js';
|
|
5
|
-
import { unknownHelpTopicError } from '../errors/messages.js';
|
|
6
|
+
import { missingSubcommandMessage, unknownHelpTopicError, usageHelpSuggestion, } from '../errors/messages.js';
|
|
6
7
|
import { EXIT_CODES } from '../utils/exitCodes.js';
|
|
7
8
|
import { findSimilar } from '../utils/suggestions.js';
|
|
8
9
|
/** Commander's typo hint on its own line, e.g. "(Did you mean query?)" */
|
|
@@ -55,4 +56,61 @@ export function splitCommanderHint(text) {
|
|
|
55
56
|
return { message };
|
|
56
57
|
return { message: message.slice(0, hint.index).trim(), suggestion: `Did you mean: ${hint[1]}?` };
|
|
57
58
|
}
|
|
59
|
+
/** The option named in Commander's "unknown option '--x'" message, without an `=value` */
|
|
60
|
+
const UNKNOWN_OPTION = /unknown option '([^'=]+)/;
|
|
61
|
+
/**
|
|
62
|
+
* The long option of a command closest to a mistyped one, hidden global
|
|
63
|
+
* options (`--session`, `--quiet`) included. Up to one edit per three letters
|
|
64
|
+
* of the name counts as a typo, so `--frob` does not suggest `--json`.
|
|
65
|
+
*
|
|
66
|
+
* @param flag - Option as typed, e.g. "--sesion"
|
|
67
|
+
* @param command - Command it was given to
|
|
68
|
+
* @returns Closest long option, if any is similar
|
|
69
|
+
*/
|
|
70
|
+
function closestOption(flag, command) {
|
|
71
|
+
const longs = command.options.flatMap((option) => (option.long ? [option.long] : []));
|
|
72
|
+
const maxDistance = Math.ceil(flag.replace(/^-+/, '').length / 3);
|
|
73
|
+
return findSimilar(flag, longs, { maxDistance })[0];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The option as typed, for one Commander split: it reads `-josn` as `-j`
|
|
77
|
+
* (`--json`) followed by `-osn`, and reports `-osn` as unknown.
|
|
78
|
+
*
|
|
79
|
+
* @param flag - Option Commander reported, e.g. "-osn"
|
|
80
|
+
* @param argv - Process arguments
|
|
81
|
+
* @returns The argument it came from, e.g. "-josn", else the option itself
|
|
82
|
+
*/
|
|
83
|
+
function typedOption(flag, argv) {
|
|
84
|
+
if (flag.startsWith('--'))
|
|
85
|
+
return flag;
|
|
86
|
+
const rest = flag.slice(1);
|
|
87
|
+
return (argv.find((arg) => /^-[^-]/.test(arg) && arg.length > flag.length && arg.endsWith(rest)) ?? flag);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Message and suggestion for a Commander usage error: a did-you-mean for an
|
|
91
|
+
* unknown option or command, otherwise a pointer to the command's `--help`.
|
|
92
|
+
* A word typed after a single dash (`-josn`) is matched against the long
|
|
93
|
+
* options.
|
|
94
|
+
*
|
|
95
|
+
* @param error - Commander error (not a help or version display)
|
|
96
|
+
* @param command - Command the error came from (see resolveCommand)
|
|
97
|
+
* @param argv - Process arguments (to name an option as typed)
|
|
98
|
+
* @returns Message and suggestion
|
|
99
|
+
*/
|
|
100
|
+
export function usageErrorDetails(error, command, argv = process.argv) {
|
|
101
|
+
const help = usageHelpSuggestion(commandPath(command));
|
|
102
|
+
if (error.code === 'commander.help') {
|
|
103
|
+
return { message: missingSubcommandMessage(), suggestion: help };
|
|
104
|
+
}
|
|
105
|
+
const { message, suggestion } = splitCommanderHint(error.message);
|
|
106
|
+
const flag = error.code === 'commander.unknownOption' && UNKNOWN_OPTION.exec(message)?.[1];
|
|
107
|
+
if (!flag)
|
|
108
|
+
return { message, suggestion: suggestion ?? help };
|
|
109
|
+
const typed = typedOption(flag, argv);
|
|
110
|
+
const closest = closestOption(/^-[^-]../.test(typed) ? `-${typed}` : typed, command);
|
|
111
|
+
return {
|
|
112
|
+
message: message.replace(`'${flag}'`, `'${typed}'`),
|
|
113
|
+
suggestion: closest ? `Did you mean: ${closest}?` : help,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
58
116
|
//# sourceMappingURL=helpTopic.js.map
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
|
+
import { CommandError } from '../errors/index.js';
|
|
2
3
|
import type { InstalledSkill, SkillTarget } from '../types.js';
|
|
4
|
+
/** What installing the skill did, and why it failed for a target, if it did */
|
|
5
|
+
export interface SkillInstallResult {
|
|
6
|
+
/** Targets the skill was installed for (or left unchanged) */
|
|
7
|
+
skills: InstalledSkill[];
|
|
8
|
+
/** Error (82) for the targets that could not be written */
|
|
9
|
+
failure?: CommandError;
|
|
10
|
+
}
|
|
3
11
|
/**
|
|
4
|
-
* Copy the bdg skill into each target's skill directory
|
|
5
|
-
* older
|
|
12
|
+
* Copy the bdg skill into each target's skill directory. A copy that differs
|
|
13
|
+
* (an older version, or one the user edited) is kept as `SKILL.md.bak`
|
|
14
|
+
* before it is overwritten. A target that cannot be written does not stop
|
|
15
|
+
* the others.
|
|
6
16
|
*
|
|
7
17
|
* @param targets - Agents to install for
|
|
8
18
|
* @param home - Home directory the skill roots are relative to
|
|
9
19
|
* @param source - SKILL.md to copy
|
|
10
|
-
* @returns
|
|
11
|
-
* @throws CommandError when the source is missing (83)
|
|
20
|
+
* @returns The targets written, in the given order, and the failure if any
|
|
21
|
+
* @throws CommandError when the source is missing (83)
|
|
12
22
|
*/
|
|
13
|
-
export declare function installSkill(targets: SkillTarget[], home?: string, source?: string):
|
|
23
|
+
export declare function installSkill(targets: SkillTarget[], home?: string, source?: string): SkillInstallResult;
|
|
14
24
|
/**
|
|
15
25
|
* Register the install-skill command.
|
|
16
26
|
*
|
|
@@ -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
|
|
@@ -17,6 +17,8 @@ const STILL_CHANGING_BEHAVIOR = 'When the page was still changing as the action
|
|
|
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
|
*
|
|
@@ -73,12 +75,12 @@ const OPTION_BEHAVIORS = {
|
|
|
73
75
|
'get:--index': {
|
|
74
76
|
default: 'Returns the first matching element (body without a selector)',
|
|
75
77
|
whenEnabled: 'Returns that match of the selector (0-based), in semantic and --raw output; --nth is an alias',
|
|
76
|
-
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)',
|
|
77
79
|
},
|
|
78
80
|
'query:--limit': {
|
|
79
|
-
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)',
|
|
80
82
|
whenEnabled: 'Lists that many matches (0 = all), in human and JSON output; count is always the total, JSON omitted the rest',
|
|
81
|
-
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',
|
|
82
84
|
tokenImpact: 'About one line per match; a page can have hundreds of links',
|
|
83
85
|
},
|
|
84
86
|
'eval:--frame': {
|
|
@@ -97,16 +99,16 @@ const OPTION_BEHAVIORS = {
|
|
|
97
99
|
automaticBehavior: 'Page navigations create new "navigation contexts" - default filters to latest context',
|
|
98
100
|
},
|
|
99
101
|
'console:-l': {
|
|
100
|
-
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)',
|
|
101
103
|
whenEnabled: 'Lists all messages chronologically without deduplication',
|
|
102
104
|
},
|
|
103
105
|
'console:--list': {
|
|
104
|
-
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)',
|
|
105
107
|
whenEnabled: 'Lists all messages chronologically without deduplication',
|
|
106
108
|
},
|
|
107
109
|
'console:--last': {
|
|
108
110
|
default: 'Smart summary (without --list); a list shows the last 100 messages',
|
|
109
|
-
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)',
|
|
110
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',
|
|
111
113
|
},
|
|
112
114
|
'console:--level': {
|
|
@@ -126,7 +128,7 @@ const OPTION_BEHAVIORS = {
|
|
|
126
128
|
'click:--no-wait': {
|
|
127
129
|
default: 'Waits for network stability after click (150ms idle, up to 2s)',
|
|
128
130
|
whenDisabled: NO_WAIT_TRIGGERED_REQUESTS,
|
|
129
|
-
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`,
|
|
130
132
|
tokenImpact: 'A click that navigates lists the whole page load in JSON triggeredRequests',
|
|
131
133
|
},
|
|
132
134
|
'click:--double': {
|
|
@@ -145,7 +147,7 @@ const OPTION_BEHAVIORS = {
|
|
|
145
147
|
'hover:--no-wait': {
|
|
146
148
|
default: 'Waits for network stability after moving the mouse (menus may load content)',
|
|
147
149
|
whenDisabled: NO_WAIT_TRIGGERED_REQUESTS,
|
|
148
|
-
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`,
|
|
149
151
|
},
|
|
150
152
|
'hover:--strict': {
|
|
151
153
|
default: 'A covered, hidden or zero-size element gets synthetic mouseover/mouseenter events (method "dom", with a warning)',
|
|
@@ -215,7 +217,7 @@ const OPTION_BEHAVIORS = {
|
|
|
215
217
|
'audit:--level': {
|
|
216
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',
|
|
217
219
|
whenEnabled: '--level AAA asks 7, or 4.5 for large text',
|
|
218
|
-
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)',
|
|
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)',
|
|
219
221
|
tokenImpact: 'About one line per finding; --limit bounds it',
|
|
220
222
|
},
|
|
221
223
|
'layout:--index': {
|
|
@@ -228,12 +230,12 @@ const OPTION_BEHAVIORS = {
|
|
|
228
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)',
|
|
229
231
|
whenEnabled: 'Inspects the nth match (0-based); out of range exits 81',
|
|
230
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',
|
|
231
|
-
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',
|
|
232
234
|
},
|
|
233
235
|
'inspect:--all': {
|
|
234
236
|
default: 'Shows the curated groups (the properties that define the look)',
|
|
235
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',
|
|
236
|
-
tokenImpact: 'About
|
|
238
|
+
tokenImpact: 'About as many tokens as the curated groups (about 90 for a button)',
|
|
237
239
|
},
|
|
238
240
|
'inspect:--props': {
|
|
239
241
|
default: 'Shows the curated groups',
|
|
@@ -297,15 +299,35 @@ const OPTION_BEHAVIORS = {
|
|
|
297
299
|
'peek:-f': {
|
|
298
300
|
default: 'Shows snapshot of current data',
|
|
299
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,
|
|
300
303
|
},
|
|
301
304
|
'peek:--follow': {
|
|
302
305
|
default: 'Shows snapshot of current data',
|
|
303
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,
|
|
304
318
|
},
|
|
305
319
|
'peek:-v': {
|
|
306
320
|
default: 'Compact output (truncated URLs, no resource types)',
|
|
307
321
|
whenEnabled: 'Verbose output with full URLs and resource types',
|
|
308
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
|
+
},
|
|
309
331
|
'start:--all': {
|
|
310
332
|
default: 'Tracking/analytics requests and console noise are filtered; bodies of binary responses (images, fonts) are not captured',
|
|
311
333
|
whenEnabled: 'Everything is captured, including binary response bodies (base64, flagged by responseBodyBase64, within --max-body-size)',
|
|
@@ -28,8 +28,11 @@ export declare function noteFollowConnected(): void;
|
|
|
28
28
|
* Handle daemon connection errors with consistent formatting and behavior.
|
|
29
29
|
*
|
|
30
30
|
* Outside follow mode the command exits. In follow mode, a session that never
|
|
31
|
-
* answered exits too (there is nothing to follow, exit 83)
|
|
32
|
-
*
|
|
31
|
+
* answered exits too (there is nothing to follow, exit 83), and so does one
|
|
32
|
+
* that ends while followed (no session any more, exit 83), so a follower
|
|
33
|
+
* running in the background finds out. Other failures (a busy page, a
|
|
34
|
+
* timeout) are retried: reported once in text, and on every failed refresh
|
|
35
|
+
* in JSON, one object per line.
|
|
33
36
|
*
|
|
34
37
|
* @param error - Error message to display
|
|
35
38
|
* @param options - Error handling options
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { sessionUnavailableSuggestion } from '../../errors/messages.js';
|
|
5
5
|
import { genericError } from '../../errors/messages.js';
|
|
6
6
|
import { OutputBuilder } from '../../ui/OutputBuilder.js';
|
|
7
|
-
import { connectionLostRetryMessage, connectionLostStopHintMessage, } from '../../ui/messages/preview.js';
|
|
7
|
+
import { connectionLostRetryMessage, connectionLostStopHintMessage, followedSessionEndedMessage, } from '../../ui/messages/preview.js';
|
|
8
8
|
import { EXIT_CODES } from '../../utils/exitCodes.js';
|
|
9
9
|
/** Follow-mode state: whether a session ever answered, and whether its loss was reported */
|
|
10
10
|
const followState = { connected: false, lossReported: false };
|
|
@@ -20,8 +20,11 @@ export function noteFollowConnected() {
|
|
|
20
20
|
* Handle daemon connection errors with consistent formatting and behavior.
|
|
21
21
|
*
|
|
22
22
|
* Outside follow mode the command exits. In follow mode, a session that never
|
|
23
|
-
* answered exits too (there is nothing to follow, exit 83)
|
|
24
|
-
*
|
|
23
|
+
* answered exits too (there is nothing to follow, exit 83), and so does one
|
|
24
|
+
* that ends while followed (no session any more, exit 83), so a follower
|
|
25
|
+
* running in the background finds out. Other failures (a busy page, a
|
|
26
|
+
* timeout) are retried: reported once in text, and on every failed refresh
|
|
27
|
+
* in JSON, one object per line.
|
|
25
28
|
*
|
|
26
29
|
* @param error - Error message to display
|
|
27
30
|
* @param options - Error handling options
|
|
@@ -29,22 +32,30 @@ export function noteFollowConnected() {
|
|
|
29
32
|
*/
|
|
30
33
|
export function handleDaemonConnectionError(error, options) {
|
|
31
34
|
const { json = false, follow = false, retryIntervalMs = 1000, exitCode = EXIT_CODES.RESOURCE_NOT_FOUND, } = options;
|
|
32
|
-
const
|
|
33
|
-
|
|
35
|
+
const sessionGone = exitCode === EXIT_CODES.RESOURCE_NOT_FOUND;
|
|
36
|
+
const exits = !follow || !followState.connected || sessionGone;
|
|
37
|
+
const message = follow && followState.connected && sessionGone ? followedSessionEndedMessage() : error;
|
|
38
|
+
if (exits || json || !followState.lossReported) {
|
|
34
39
|
if (json) {
|
|
35
40
|
const suggestion = exits ? sessionUnavailableSuggestion(exitCode) : undefined;
|
|
36
|
-
|
|
41
|
+
const envelope = OutputBuilder.buildJsonError(message, {
|
|
42
|
+
exitCode,
|
|
43
|
+
...(suggestion && { suggestion }),
|
|
44
|
+
});
|
|
45
|
+
console.log(follow ? JSON.stringify(envelope) : JSON.stringify(envelope, null, 2));
|
|
37
46
|
}
|
|
38
47
|
else {
|
|
39
|
-
console.error(genericError(
|
|
48
|
+
console.error(genericError(message));
|
|
40
49
|
}
|
|
41
50
|
}
|
|
42
51
|
if (exits)
|
|
43
52
|
return { shouldExit: true, exitCode };
|
|
44
53
|
if (!followState.lossReported) {
|
|
45
54
|
const retryMessage = retryIntervalMs >= 1000 ? `${retryIntervalMs / 1000}s` : `${retryIntervalMs}ms`;
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
if (!json) {
|
|
56
|
+
console.error(connectionLostRetryMessage(new Date().toISOString(), retryMessage));
|
|
57
|
+
console.error(connectionLostStopHintMessage());
|
|
58
|
+
}
|
|
48
59
|
followState.lossReported = true;
|
|
49
60
|
}
|
|
50
61
|
return { shouldExit: false };
|
|
@@ -47,18 +47,26 @@ export declare function fetchPreviewData(query?: PreviewQuery): Promise<FetchRes
|
|
|
47
47
|
* Fetch all captured network requests from daemon.
|
|
48
48
|
*
|
|
49
49
|
* @param withHeaders - Include request/response headers (needed by header filters)
|
|
50
|
-
* @returns Requests
|
|
50
|
+
* @returns Requests, and when the page crashed (while it is not loaded
|
|
51
|
+
* again), or a fetch error
|
|
51
52
|
*/
|
|
52
|
-
export declare function fetchNetworkRequests(withHeaders?: boolean): Promise<FetchResult<
|
|
53
|
+
export declare function fetchNetworkRequests(withHeaders?: boolean): Promise<FetchResult<{
|
|
54
|
+
requests: NetworkRequest[];
|
|
55
|
+
pageCrashedAt: number | undefined;
|
|
56
|
+
}>>;
|
|
53
57
|
/**
|
|
54
58
|
* Fetch all console messages from daemon.
|
|
55
59
|
*
|
|
56
|
-
* @returns Messages (with their session-wide index)
|
|
57
|
-
* the page currently loaded
|
|
60
|
+
* @returns Messages (with their session-wide index), the navigation id of
|
|
61
|
+
* the page currently loaded, how many of the oldest messages the session
|
|
62
|
+
* dropped at its limit and when the page crashed (while it is not loaded
|
|
63
|
+
* again)
|
|
58
64
|
*/
|
|
59
65
|
export declare function fetchConsoleMessages(): Promise<FetchResult<{
|
|
60
66
|
messages: ConsoleMessage[];
|
|
61
67
|
currentNavigationId: number | undefined;
|
|
68
|
+
dropped: number;
|
|
69
|
+
pageCrashedAt: number | undefined;
|
|
62
70
|
}>>;
|
|
63
71
|
interface ErrorResult {
|
|
64
72
|
success: false;
|