browser-debugger-cli 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/.claude/skills/bdg/SKILL.md +3 -2
  2. package/dist/cdp/methodTarget.d.ts +92 -0
  3. package/dist/cdp/methodTarget.js +159 -0
  4. package/dist/cdp/protocol.d.ts +16 -1
  5. package/dist/cdp/protocol.js +21 -0
  6. package/dist/cdp/schema.d.ts +55 -1
  7. package/dist/cdp/schema.js +134 -25
  8. package/dist/cdp/types.d.ts +3 -1
  9. package/dist/commands/cdp.d.ts +38 -1
  10. package/dist/commands/cdp.js +201 -133
  11. package/dist/commands/cleanup.js +21 -4
  12. package/dist/commands/dom/eval.d.ts +2 -1
  13. package/dist/commands/dom/eval.js +6 -21
  14. package/dist/commands/dom/formInteraction.js +8 -4
  15. package/dist/commands/dom/helpers/evalResult.d.ts +36 -0
  16. package/dist/commands/dom/helpers/evalResult.js +59 -0
  17. package/dist/commands/dom/helpers/index.d.ts +4 -4
  18. package/dist/commands/dom/helpers/index.js +3 -3
  19. package/dist/commands/dom/helpers/query.d.ts +2 -2
  20. package/dist/commands/dom/helpers/query.js +2 -2
  21. package/dist/commands/dom/helpers/screenshot.d.ts +21 -26
  22. package/dist/commands/dom/helpers/screenshot.js +50 -668
  23. package/dist/commands/dom/screenshot.js +56 -36
  24. package/dist/commands/helpJson.d.ts +1 -1
  25. package/dist/commands/helpJson.js +3 -3
  26. package/dist/commands/helpTopic.js +10 -4
  27. package/dist/commands/network/har.js +18 -14
  28. package/dist/commands/optionBehaviors.js +24 -9
  29. package/dist/commands/shared/CommandRunner.d.ts +5 -0
  30. package/dist/commands/shared/CommandRunner.js +18 -3
  31. package/dist/commands/shared/interrupt.d.ts +40 -0
  32. package/dist/commands/shared/interrupt.js +73 -0
  33. package/dist/commands/shared/optionTypes.d.ts +3 -0
  34. package/dist/commands/shared/outputFile.d.ts +2 -1
  35. package/dist/commands/shared/outputFile.js +7 -4
  36. package/dist/commands/shared/startHelpers.d.ts +26 -3
  37. package/dist/commands/shared/startHelpers.js +145 -23
  38. package/dist/commands/status.js +3 -1
  39. package/dist/commands/stop.js +2 -1
  40. package/dist/commands/types.d.ts +5 -0
  41. package/dist/connection/cdp.js +1 -16
  42. package/dist/connection/chromeIdentity.d.ts +24 -5
  43. package/dist/connection/chromeIdentity.js +53 -22
  44. package/dist/connection/launcher/flagsBuilder.d.ts +46 -0
  45. package/dist/connection/launcher/flagsBuilder.js +107 -23
  46. package/dist/connection/launcher.d.ts +35 -2
  47. package/dist/connection/launcher.js +99 -12
  48. package/dist/connection/typed-cdp.d.ts +3 -2
  49. package/dist/constants.d.ts +3 -5
  50. package/dist/constants.js +3 -5
  51. package/dist/daemon/SessionController.d.ts +10 -5
  52. package/dist/daemon/SessionController.js +15 -8
  53. package/dist/daemon/ipcServer.js +1 -1
  54. package/dist/daemon/launcher.d.ts +22 -3
  55. package/dist/daemon/launcher.js +45 -8
  56. package/dist/daemon/session/Session.d.ts +5 -1
  57. package/dist/daemon/session/Session.js +9 -8
  58. package/dist/daemon/session/TelemetryStore.d.ts +5 -0
  59. package/dist/daemon/session/TelemetryStore.js +4 -0
  60. package/dist/daemon/session/captureGate.d.ts +59 -0
  61. package/dist/daemon/session/captureGate.js +96 -0
  62. package/dist/daemon/session/chromeConnection.d.ts +16 -1
  63. package/dist/daemon/session/chromeConnection.js +34 -4
  64. package/dist/daemon/session/collectors.d.ts +15 -0
  65. package/dist/daemon/session/collectors.js +39 -2
  66. package/dist/daemon/session/commandRegistry.d.ts +14 -1
  67. package/dist/daemon/session/commandRegistry.js +48 -13
  68. package/dist/daemon/session/downloads.d.ts +32 -0
  69. package/dist/daemon/session/downloads.js +96 -0
  70. package/dist/daemon/session/interactions.d.ts +3 -2
  71. package/dist/daemon/session/interactions.js +7 -2
  72. package/dist/daemon/session/plugins.js +6 -0
  73. package/dist/daemon.js +18520 -17014
  74. package/dist/errors/CommandError.d.ts +2 -0
  75. package/dist/errors/issues.d.ts +1 -1
  76. package/dist/errors/messages.d.ts +81 -0
  77. package/dist/errors/messages.js +198 -6
  78. package/dist/index.js +1446 -1078
  79. package/dist/ipc/client.d.ts +20 -2
  80. package/dist/ipc/client.js +32 -6
  81. package/dist/ipc/protocol/commands.d.ts +36 -2
  82. package/dist/ipc/protocol/commands.js +1 -0
  83. package/dist/ipc/protocol/domTypes.d.ts +24 -1
  84. package/dist/ipc/session/queries.d.ts +3 -0
  85. package/dist/ipc/session/types.d.ts +5 -0
  86. package/dist/ipc/transport/IPCError.d.ts +9 -0
  87. package/dist/ipc/transport/IPCError.js +12 -0
  88. package/dist/ipc/transport/errors.d.ts +2 -1
  89. package/dist/ipc/transport/errors.js +4 -1
  90. package/dist/ipc/transport/index.d.ts +10 -2
  91. package/dist/ipc/transport/index.js +29 -4
  92. package/dist/runtime/dom/actionEffects.d.ts +48 -9
  93. package/dist/runtime/dom/actionEffects.js +269 -34
  94. package/dist/runtime/dom/actionEffectsScripts.d.ts +45 -0
  95. package/dist/runtime/dom/actionEffectsScripts.js +101 -2
  96. package/dist/runtime/dom/captureArea.d.ts +35 -0
  97. package/dist/runtime/dom/captureArea.js +203 -0
  98. package/dist/runtime/dom/elementInfo.d.ts +13 -4
  99. package/dist/runtime/dom/elementInfo.js +12 -3
  100. package/dist/runtime/dom/evalHelpers.d.ts +24 -4
  101. package/dist/runtime/dom/evalHelpers.js +40 -12
  102. package/dist/runtime/dom/formDiscovery.d.ts +1 -1
  103. package/dist/runtime/dom/frames.d.ts +2 -1
  104. package/dist/runtime/dom/frames.js +3 -1
  105. package/dist/runtime/page/bdgWorld.d.ts +9 -0
  106. package/dist/runtime/page/bdgWorld.js +11 -0
  107. package/dist/runtime/page/captureEmulation.d.ts +119 -0
  108. package/dist/runtime/page/captureEmulation.js +189 -0
  109. package/dist/runtime/page/captureScroll.d.ts +24 -0
  110. package/dist/runtime/page/captureScroll.js +124 -0
  111. package/dist/runtime/page/emulation.js +6 -5
  112. package/dist/runtime/page/screenshot.d.ts +41 -0
  113. package/dist/runtime/page/screenshot.js +394 -0
  114. package/dist/runtime/page/userAgent.d.ts +86 -2
  115. package/dist/runtime/page/userAgent.js +154 -33
  116. package/dist/session/paths.d.ts +52 -3
  117. package/dist/session/paths.js +179 -7
  118. package/dist/session/portClaims.d.ts +0 -8
  119. package/dist/session/portClaims.js +1 -22
  120. package/dist/session/sessionList.d.ts +5 -1
  121. package/dist/session/sessionList.js +5 -1
  122. package/dist/telemetry/downloads.d.ts +127 -0
  123. package/dist/telemetry/downloads.js +265 -0
  124. package/dist/telemetry/har/builder.d.ts +12 -1
  125. package/dist/telemetry/har/builder.js +32 -9
  126. package/dist/telemetry/har/sanitize.d.ts +28 -0
  127. package/dist/telemetry/har/sanitize.js +184 -0
  128. package/dist/telemetry/har/sanitizeBody.d.ts +78 -0
  129. package/dist/telemetry/har/sanitizeBody.js +541 -0
  130. package/dist/telemetry/har/types.d.ts +2 -0
  131. package/dist/telemetry/network.d.ts +4 -4
  132. package/dist/telemetry/network.js +38 -4
  133. package/dist/telemetry/networkRetention.d.ts +35 -14
  134. package/dist/telemetry/networkRetention.js +62 -26
  135. package/dist/types.d.ts +9 -14
  136. package/dist/ui/OutputBuilder.d.ts +3 -2
  137. package/dist/ui/OutputBuilder.js +4 -3
  138. package/dist/ui/formatters/cdp.d.ts +32 -9
  139. package/dist/ui/formatters/cdp.js +77 -6
  140. package/dist/ui/formatters/details.js +7 -15
  141. package/dist/ui/formatters/preview.d.ts +2 -0
  142. package/dist/ui/formatters/preview.js +7 -1
  143. package/dist/ui/formatters/sessions.d.ts +3 -2
  144. package/dist/ui/formatters/sessions.js +10 -3
  145. package/dist/ui/formatters/status.js +6 -1
  146. package/dist/ui/formatting.d.ts +7 -0
  147. package/dist/ui/formatting.js +13 -0
  148. package/dist/ui/logging/logger.d.ts +1 -1
  149. package/dist/ui/messages/chrome.d.ts +27 -6
  150. package/dist/ui/messages/chrome.js +78 -12
  151. package/dist/ui/messages/commands.d.ts +71 -3
  152. package/dist/ui/messages/commands.js +98 -3
  153. package/dist/ui/messages/networkMessages.d.ts +50 -5
  154. package/dist/ui/messages/networkMessages.js +50 -6
  155. package/dist/ui/messages/session.d.ts +8 -0
  156. package/dist/ui/messages/session.js +10 -0
  157. package/dist/utils/async.d.ts +3 -2
  158. package/dist/utils/async.js +16 -3
  159. package/dist/utils/atomicFile.d.ts +2 -1
  160. package/dist/utils/atomicFile.js +5 -2
  161. package/dist/utils/directories.d.ts +41 -0
  162. package/dist/utils/directories.js +48 -0
  163. package/dist/utils/http.d.ts +11 -4
  164. package/dist/utils/http.js +5 -3
  165. package/package.json +18 -4
  166. /package/dist/{commands/dom → runtime/page}/screenshotResize.d.ts +0 -0
  167. /package/dist/{commands/dom → runtime/page}/screenshotResize.js +0 -0
@@ -1,10 +1,11 @@
1
1
  import { OutputFormatter } from '../formatting.js';
2
- import { endedSessionText } from '../messages/session.js';
2
+ import { endedSessionText, untrustedSessionText } from '../messages/session.js';
3
3
  /** Label of the default session in the list */
4
4
  const DEFAULT_SESSION_LABEL = '(default)';
5
5
  /**
6
- * Format the sessions as a table, followed by why ended sessions ended and
7
- * the cleanup commands of crashed and stale sessions.
6
+ * Format the sessions as a table, followed by why ended sessions ended, why
7
+ * untrusted sessions' directories are not safe to use, and the cleanup
8
+ * commands of crashed and stale sessions.
8
9
  *
9
10
  * @param data - Sessions
10
11
  * @returns Human-readable list
@@ -38,6 +39,12 @@ export function formatSessionList(data) {
38
39
  if (ended.length > 0) {
39
40
  fmt.blank().section('Ended without bdg stop:', ended);
40
41
  }
42
+ const untrusted = data.sessions.flatMap(({ name, untrusted: why }) => why ? [untrustedSessionText(name ?? DEFAULT_SESSION_LABEL, why)] : []);
43
+ if (untrusted.length > 0) {
44
+ fmt
45
+ .blank()
46
+ .section('Directory not safe to use (not asked; bdg status --session <name> says how to fix it):', untrusted);
47
+ }
41
48
  const cleanups = data.sessions.flatMap((session) => (session.cleanup ? [session.cleanup] : []));
42
49
  if (cleanups.length > 0) {
43
50
  fmt.hints('Crashed or stale sessions; clean up with:', cleanups);
@@ -1,7 +1,7 @@
1
1
  import { describeRunningChrome } from '../../session/chrome.js';
2
2
  import { calculateDuration, formatTimeAgo } from '../../session/statusData.js';
3
3
  import { OutputFormatter } from '../formatting.js';
4
- import { colorSchemeLabel, sessionActiveLine } from '../messages/commands.js';
4
+ import { colorSchemeLabel, downloadsSummary, sessionActiveLine } from '../messages/commands.js';
5
5
  import { networkEvictedNote } from '../messages/networkMessages.js';
6
6
  import { lastSessionEndText } from '../messages/session.js';
7
7
  import { noActiveSessionMessage, sessionCommand } from '../messages/sessionCommand.js';
@@ -62,6 +62,11 @@ export function formatSessionStatus(metadata, pid, activity, pageState, verbose
62
62
  if (activity.lastConsoleMessageAt) {
63
63
  fmt.keyValue(' Last Message', formatTimeAgo(activity.lastConsoleMessageAt), 18);
64
64
  }
65
+ const downloadRows = [
66
+ ...(activity.downloads?.length ? [downloadsSummary(activity.downloads)] : []),
67
+ ...(activity.downloadsWarning ? [`⚠ ${activity.downloadsWarning}`] : []),
68
+ ];
69
+ downloadRows.forEach((row, index) => index === 0 ? fmt.keyValue('Downloads', row, 18) : fmt.text(`${' '.repeat(18)}${row}`));
65
70
  }
66
71
  fmt.blank().text('Collectors').separator('━', 50);
67
72
  const activeTelemetry = metadata.activeTelemetry ?? ['network', 'console', 'dom'];
@@ -83,4 +83,11 @@ export declare function truncateUrl(url: string, maxLength?: number): string;
83
83
  export declare function cutMiddle(text: string, maxLength: number): string;
84
84
  export declare function pluralize(count: number, singular: string, plural?: string): string;
85
85
  export declare function formatDuration(ms: number): string;
86
+ /**
87
+ * Format a byte count for humans.
88
+ *
89
+ * @param bytes - Byte count
90
+ * @returns e.g. "512 B", "12.3 KB"
91
+ */
92
+ export declare function formatBytes(bytes: number): string;
86
93
  //# sourceMappingURL=formatting.d.ts.map
@@ -216,4 +216,17 @@ export function formatDuration(ms) {
216
216
  }
217
217
  return `${ms}ms`;
218
218
  }
219
+ /**
220
+ * Format a byte count for humans.
221
+ *
222
+ * @param bytes - Byte count
223
+ * @returns e.g. "512 B", "12.3 KB"
224
+ */
225
+ export function formatBytes(bytes) {
226
+ if (bytes < 1024)
227
+ return `${bytes} B`;
228
+ return bytes < 1024 * 1024
229
+ ? `${(bytes / 1024).toFixed(1)} KB`
230
+ : `${(bytes / 1024 / 1024).toFixed(1)} MB`;
231
+ }
219
232
  //# sourceMappingURL=formatting.js.map
@@ -29,7 +29,7 @@ export type LogLevel = 'info' | 'debug';
29
29
  * Log contexts for different components.
30
30
  * Used to prefix log messages with component name.
31
31
  */
32
- export type LogContext = 'bdg' | 'launcher' | 'daemon' | 'client' | 'cleanup' | 'session' | 'chrome' | 'cdp' | 'ipc' | 'http' | 'dialogs' | 'navigation' | 'page-crash' | 'console' | 'dom' | 'network' | 'diagnostics' | 'readiness' | 'atomic-file' | 'object-expander' | 'fetcher' | 'targets';
32
+ export type LogContext = 'bdg' | 'launcher' | 'daemon' | 'client' | 'cleanup' | 'session' | 'chrome' | 'cdp' | 'ipc' | 'http' | 'dialogs' | 'downloads' | 'navigation' | 'page-crash' | 'console' | 'dom' | 'network' | 'diagnostics' | 'readiness' | 'atomic-file' | 'object-expander' | 'fetcher' | 'targets';
33
33
  /**
34
34
  * Logger instance with support for different log levels.
35
35
  */
@@ -20,8 +20,14 @@ export declare function formatChromeNotice(notice: NoticeDetails<ChromeNoticeCod
20
20
  * Called at the UI boundary when a ChromeLaunchError with `issue` details
21
21
  * reaches a CLI or daemon log sink. Core modules produce the IssueDetails;
22
22
  * this function is the only place wording is assembled.
23
+ *
24
+ * @param issue - Structured issue
25
+ * @param diagnostics - Source of Chrome installation diagnostics (tests stub it)
26
+ * @returns User-facing message
23
27
  */
24
- export declare function formatChromeIssue(issue: IssueDetails): string;
28
+ export declare function formatChromeIssue(issue: IssueDetails, diagnostics?: () => ChromeDiagnostics): string;
29
+ /** Chrome's launch ended because the session was stopped meanwhile */
30
+ export declare const CHROME_LAUNCH_ABORTED_MESSAGE = "Chrome launch aborted: the session was stopped";
25
31
  /**
26
32
  * Chrome exited before its debugging port opened.
27
33
  *
@@ -33,15 +39,19 @@ export declare function formatChromeIssue(issue: IssueDetails): string;
33
39
  */
34
40
  export declare function chromeExitedDuringStartupError(exitCode: number | null, output: string[], userDataDir: string, profileInUse: boolean): string;
35
41
  /**
36
- * Retrieve Chrome diagnostics and format for error messages.
37
- *
38
- * @returns Array of formatted diagnostic strings
42
+ * Options of {@link formatDiagnosticsForError}.
39
43
  */
40
- export declare function getFormattedDiagnostics(): string[];
44
+ export interface DiagnosticsFormatOptions {
45
+ /** OS the CHROME_PATH example is for (default: this one) */
46
+ platform?: NodeJS.Platform;
47
+ /** Suggest CHROME_PATH when no Chrome is found (off when CHROME_PATH is the problem) */
48
+ suggestChromePath?: boolean;
49
+ }
41
50
  /**
42
51
  * Format Chrome diagnostics for error reporting when Chrome launch fails.
43
52
  *
44
53
  * @param diagnostics - Chrome diagnostics information
54
+ * @param options - Platform of the example and whether to suggest CHROME_PATH
45
55
  * @returns Formatted error message lines with troubleshooting steps
46
56
  *
47
57
  * @example
@@ -51,7 +61,7 @@ export declare function getFormattedDiagnostics(): string[];
51
61
  * console.error(errorLines.join('\n'));
52
62
  * ```
53
63
  */
54
- export declare function formatDiagnosticsForError(diagnostics: ChromeDiagnostics): string[];
64
+ export declare function formatDiagnosticsForError(diagnostics: ChromeDiagnostics, { platform, suggestChromePath }?: DiagnosticsFormatOptions): string[];
55
65
  /**
56
66
  * Generate invalid port error message.
57
67
  *
@@ -180,6 +190,17 @@ export declare function portTakenByReason(answeredBy: 'browser' | 'process' | 'n
180
190
  * @returns Reason for the CHROME_LAUNCH_FAILED issue
181
191
  */
182
192
  export declare function chromeNotAnsweringReason(port: number, waitedMs: number): string;
193
+ /**
194
+ * Chrome is running but did not open its debugging port within
195
+ * chrome-launcher's readiness budget (a slow start, not a crash or a port
196
+ * conflict: nothing answered on the port, which was free before the launch).
197
+ *
198
+ * @param pid - Chrome's PID
199
+ * @param port - Debugging port
200
+ * @param waitedMs - The readiness budget
201
+ * @returns Error message
202
+ */
203
+ export declare function chromePortNotOpenedMessage(pid: number, port: number, waitedMs: number): string;
183
204
  /**
184
205
  * Warning when bdg's Chrome preferences (password manager and leak check
185
206
  * off, etc.) could not be written into the profile.
@@ -32,8 +32,12 @@ export function formatChromeNotice(notice) {
32
32
  * Called at the UI boundary when a ChromeLaunchError with `issue` details
33
33
  * reaches a CLI or daemon log sink. Core modules produce the IssueDetails;
34
34
  * this function is the only place wording is assembled.
35
+ *
36
+ * @param issue - Structured issue
37
+ * @param diagnostics - Source of Chrome installation diagnostics (tests stub it)
38
+ * @returns User-facing message
35
39
  */
36
- export function formatChromeIssue(issue) {
40
+ export function formatChromeIssue(issue, diagnostics = getChromeDiagnostics) {
37
41
  const ctx = issue.context ?? {};
38
42
  switch (issue.code) {
39
43
  case 'PORT_IN_USE':
@@ -52,16 +56,20 @@ export function formatChromeIssue(issue) {
52
56
  : reason
53
57
  ? chromeLaunchFailedError(reason)
54
58
  : `Chrome failed to launch`;
55
- const diagnostics = getFormattedDiagnostics();
56
- return joinLines(header, '', 'Possible causes:', ` - Port ${port} conflict (check: lsof -ti:${port})`, ` - Chrome binary not found`, ` - Insufficient permissions`, ` - Chrome crashed on startup`, '', ...diagnostics, '', 'Try:', ` - ${sessionCommand('bdg cleanup')}`, ` - See what uses the port: lsof -i :${port}`, ` - Use different port: ${sessionCommand(`bdg <url> --port ${port + 1}`)}`, ` - In a container where Chrome's sandbox fails: BDG_NO_SANDBOX=1 ${sessionCommand('bdg <url>')}`);
59
+ const found = diagnostics();
60
+ const diagnosticLines = formatDiagnosticsForError(found);
61
+ if (noChromeFound(found))
62
+ return joinLines(header, '', ...diagnosticLines);
63
+ return joinLines(header, '', 'Possible causes:', ` - Port ${port} conflict (check: lsof -ti:${port})`, ` - Chrome binary not found`, ` - Insufficient permissions`, ` - Chrome crashed on startup`, '', ...diagnosticLines, '', 'Try:', ` - ${sessionCommand('bdg cleanup')}`, ` - See what uses the port: lsof -i :${port}`, ` - Use different port: ${sessionCommand(`bdg <url> --port ${port + 1}`)}`, ` - In a container where Chrome's sandbox fails: BDG_NO_SANDBOX=1 ${sessionCommand('bdg <url>')}`);
57
64
  }
65
+ case 'CHROME_PORT_NOT_OPENED':
66
+ return joinLines(chromePortNotOpenedMessage(ctx['pid'], ctx['port'], ctx['waitedMs']), chromePortNotOpenedSuggestion());
58
67
  case 'CHROME_EXITED_DURING_STARTUP':
59
68
  return chromeExitedDuringStartupError(ctx['exitCode'], ctx['output'] ?? [], ctx['userDataDir'], ctx['profileInUse'] === true);
60
69
  case 'NO_PAGE_TARGET_FOUND':
61
70
  return noPageTargetFoundError(ctx['port'], ctx['availableTargets']);
62
71
  case 'CHROME_BINARY_NOT_FOUND': {
63
- const diagnostics = getFormattedDiagnostics();
64
- return joinLines(chromeBinaryOverrideNotFound(ctx['chromePath'], ctx['source']), '', ...diagnostics);
72
+ return joinLines(chromeBinaryOverrideNotFound(ctx['chromePath'], ctx['source']), '', ...formatDiagnosticsForError(diagnostics(), { suggestChromePath: false }));
65
73
  }
66
74
  case 'CHROME_BINARY_IS_DIRECTORY':
67
75
  return chromeBinaryOverrideIsDirectory(ctx['chromePath'], ctx['source']);
@@ -80,6 +88,8 @@ export function formatChromeIssue(issue) {
80
88
  return `Chrome preferences must be JSON-serializable: ${ctx['reason'] ?? 'unknown error'}`;
81
89
  }
82
90
  }
91
+ /** Chrome's launch ended because the session was stopped meanwhile */
92
+ export const CHROME_LAUNCH_ABORTED_MESSAGE = 'Chrome launch aborted: the session was stopped';
83
93
  /**
84
94
  * Chrome exited before its debugging port opened.
85
95
  *
@@ -96,17 +106,49 @@ export function chromeExitedDuringStartupError(exitCode, output, userDataDir, pr
96
106
  return joinLines(`Chrome exited during startup (exit code ${exitCode ?? 'none'})`, ...(output.length > 0 ? ['Chrome said:', ...output.map((line) => ` ${line}`)] : []), 'Check --chrome-flags and BDG_CHROME_FLAGS (an unknown flag or value can stop Chrome)');
97
107
  }
98
108
  /**
99
- * Retrieve Chrome diagnostics and format for error messages.
109
+ * Whether no Chrome was found at all: no installation and no default binary
110
+ * (chrome-launcher's default honors a valid CHROME_PATH).
111
+ *
112
+ * @param diagnostics - Chrome diagnostics
113
+ * @returns True when there is nothing to launch
114
+ */
115
+ function noChromeFound(diagnostics) {
116
+ return diagnostics.installationCount === 0 && !diagnostics.defaultPath;
117
+ }
118
+ /**
119
+ * A Chromium-based browser binary that chrome-launcher does not find on its
120
+ * own, as an example value for CHROME_PATH.
121
+ *
122
+ * @param platform - OS the path is for
123
+ * @returns Microsoft Edge's binary on macOS or Linux; none on other systems
124
+ */
125
+ function exampleChromePath(platform) {
126
+ if (platform === 'darwin')
127
+ return '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge';
128
+ if (platform === 'linux')
129
+ return '/usr/bin/microsoft-edge';
130
+ return undefined;
131
+ }
132
+ /**
133
+ * How to point bdg at another Chromium-based browser through CHROME_PATH.
100
134
  *
101
- * @returns Array of formatted diagnostic strings
135
+ * @param platform - OS the example is for
136
+ * @returns Message lines, with an example command where one is known
102
137
  */
103
- export function getFormattedDiagnostics() {
104
- return formatDiagnosticsForError(getChromeDiagnostics());
138
+ function chromePathSuggestion(platform) {
139
+ const example = exampleChromePath(platform);
140
+ if (!example)
141
+ return ['Set CHROME_PATH to a Chromium-based browser (Edge, Brave, Chromium)\n'];
142
+ return [
143
+ 'Set CHROME_PATH to a Chromium-based browser (Edge, Brave, Chromium), e.g.:',
144
+ ` CHROME_PATH="${example}" ${sessionCommand('bdg <url>')}\n`,
145
+ ];
105
146
  }
106
147
  /**
107
148
  * Format Chrome diagnostics for error reporting when Chrome launch fails.
108
149
  *
109
150
  * @param diagnostics - Chrome diagnostics information
151
+ * @param options - Platform of the example and whether to suggest CHROME_PATH
110
152
  * @returns Formatted error message lines with troubleshooting steps
111
153
  *
112
154
  * @example
@@ -116,11 +158,13 @@ export function getFormattedDiagnostics() {
116
158
  * console.error(errorLines.join('\n'));
117
159
  * ```
118
160
  */
119
- export function formatDiagnosticsForError(diagnostics) {
161
+ export function formatDiagnosticsForError(diagnostics, { platform = process.platform, suggestChromePath = true } = {}) {
120
162
  const lines = [];
121
- if (diagnostics.installationCount === 0) {
163
+ if (noChromeFound(diagnostics)) {
122
164
  lines.push('Error: No Chrome installations detected\n');
123
- lines.push('Install Chrome from:');
165
+ if (suggestChromePath)
166
+ lines.push(...chromePathSuggestion(platform));
167
+ lines.push(suggestChromePath ? 'Or install Chrome from:' : 'Install Chrome from:');
124
168
  lines.push(' https://www.google.com/chrome/\n');
125
169
  }
126
170
  else {
@@ -305,6 +349,28 @@ export function portTakenByReason(answeredBy, chromeHost) {
305
349
  export function chromeNotAnsweringReason(port, waitedMs) {
306
350
  return `Chrome announced port ${port} but did not answer on 127.0.0.1 within ${(waitedMs / 1000).toFixed(1)}s (slow start)`;
307
351
  }
352
+ /**
353
+ * Chrome is running but did not open its debugging port within
354
+ * chrome-launcher's readiness budget (a slow start, not a crash or a port
355
+ * conflict: nothing answered on the port, which was free before the launch).
356
+ *
357
+ * @param pid - Chrome's PID
358
+ * @param port - Debugging port
359
+ * @param waitedMs - The readiness budget
360
+ * @returns Error message
361
+ */
362
+ export function chromePortNotOpenedMessage(pid, port, waitedMs) {
363
+ return `Chrome started (pid ${pid}) but did not open its debugging port ${port} within ${Number((waitedMs / 1000).toFixed(1))} s`;
364
+ }
365
+ /**
366
+ * What to do when Chrome started but did not open its debugging port in time.
367
+ *
368
+ * @returns Suggestion
369
+ */
370
+ function chromePortNotOpenedSuggestion() {
371
+ return (`Retry: ${sessionCommand('bdg <url>')} (a first start on a cold machine can be slow). ` +
372
+ `If it keeps failing: ${sessionCommand('bdg cleanup')} && ${sessionCommand('bdg <url>')}`);
373
+ }
308
374
  /**
309
375
  * Warning when bdg's Chrome preferences (password manager and leak check
310
376
  * off, etc.) could not be written into the profile.
@@ -5,7 +5,7 @@
5
5
  * cleaning up stale files, and validating command arguments.
6
6
  */
7
7
  import type { DomFrame, PageLoadingState, PendingRequestInfo } from '../../ipc/protocol/commands.js';
8
- import type { ElementLayout, FillValueMismatch, LayoutSize, NewMessage, PageLayout, PageNavigation, PendingChanges, ShownElement } from '../../ipc/protocol/domTypes.js';
8
+ import type { DownloadInfo, ElementLayout, FillValueMismatch, LayoutSize, NewMessage, PageLayout, PageNavigation, PendingChanges, ShownElement } from '../../ipc/protocol/domTypes.js';
9
9
  import type { InspectVisibility } from '../../ipc/protocol/inspectTypes.js';
10
10
  import type { DelegationNote } from '../../runtime/dom/listenerSummary.js';
11
11
  import type { WaitCondition, WaitSnapshot } from '../../runtime/dom/waitCondition.js';
@@ -40,6 +40,34 @@ export declare function skillBackupMessage(path: string): string;
40
40
  export declare function domClickFallbackWarning(reason: string | null | undefined): string;
41
41
  /** Reason of a `bdg dom form` blocker for a required field left empty */
42
42
  export declare const REQUIRED_FIELD_EMPTY_REASON = "Required field is empty";
43
+ /**
44
+ * Warning for a `bdg cdp` method the bundled protocol lacks, sent anyway.
45
+ *
46
+ * @param method - Method sent
47
+ * @param protocolVersion - Bundled devtools-protocol version
48
+ * @returns Warning text
49
+ */
50
+ export declare function cdpUnlistedMethodWarning(method: string, protocolVersion: string): string;
51
+ /**
52
+ * `bdg cdp --describe` line for a redirect to a method the protocol lacks
53
+ * (e.g. Page.deleteCookie names Network.deleteCookie).
54
+ *
55
+ * @param method - Redirect target
56
+ * @returns Line text
57
+ */
58
+ export declare function cdpUnresolvedRedirectLine(method: string): string;
59
+ /**
60
+ * `bdg cdp --describe` title of a redirect to a method the protocol has.
61
+ *
62
+ * @param method - Redirect target
63
+ * @returns Title text
64
+ */
65
+ export declare function cdpRedirectTitle(method: string): string;
66
+ /**
67
+ * Lines of `bdg cdp --help` saying how names are matched (each short
68
+ * enough not to be wrapped).
69
+ */
70
+ export declare const CDP_EXECUTION_HELP: string;
43
71
  /**
44
72
  * Part of the `bdg dom form` summary naming the required fields left empty.
45
73
  *
@@ -101,6 +129,38 @@ export declare function shownElementText(element: ShownElement): string;
101
129
  * "URL changed to https://example.com/#/active (same document)"
102
130
  */
103
131
  export declare function pageNavigationText(navigation: PageNavigation): string;
132
+ /**
133
+ * A download an action started, as its output line reads.
134
+ *
135
+ * @param download - Download
136
+ * @returns e.g. `Download: report.txt → /Users/me/.bdg/downloads/report.txt (completed, 15 B)`,
137
+ * `Download: big.zip → … (inProgress, 9.8 KB so far)` (no size before the first
138
+ * bytes), `Download: report.txt (canceled)`
139
+ */
140
+ export declare function downloadText(download: DownloadInfo): string;
141
+ /**
142
+ * The session's downloads in one line: how many, and the last one.
143
+ *
144
+ * @param downloads - Downloads, oldest first (at least one)
145
+ * @returns e.g. `2 (last: report.txt → /Users/me/.bdg/downloads/report.txt, completed)`
146
+ */
147
+ export declare function downloadsSummary(downloads: DownloadInfo[]): string;
148
+ /**
149
+ * Warning while Chrome did not take bdg's download behavior, so downloads
150
+ * are not saved in the session directory.
151
+ *
152
+ * @param detail - Chrome's error
153
+ * @returns Warning shown by `bdg status`
154
+ */
155
+ export declare function downloadsNotRedirectedWarning(detail: string): string;
156
+ /**
157
+ * Why downloads are refused when the session's downloads directory could not
158
+ * be created.
159
+ *
160
+ * @param detail - What went wrong, e.g. `/home/me/.bdg/downloads is a file`
161
+ * @returns Reason recorded on each refused download
162
+ */
163
+ export declare function downloadsDirUnavailableReason(detail: string): string;
104
164
  /**
105
165
  * Last `New text:` row when an action made more messages appear than are listed.
106
166
  *
@@ -748,9 +808,17 @@ export declare function sessionFilesCleanedMessage(): string;
748
808
  */
749
809
  export declare function sessionOutputRemovedMessage(): string;
750
810
  /**
751
- * Generate session directory clean message.
811
+ * Cleanup left the session's downloaded files in place.
752
812
  *
753
- * @returns Formatted success message
813
+ * @param dir - Downloads directory
814
+ * @param files - Files in it
815
+ * @returns e.g. `Downloads kept: 2 files in /Users/me/.bdg/downloads (delete them yourself when done)`
816
+ */
817
+ export declare function downloadsKeptMessage(dir: string, files: number): string;
818
+ /**
819
+ * Cleanup finished and left nothing behind.
820
+ *
821
+ * @returns Message
754
822
  */
755
823
  export declare function sessionDirectoryCleanMessage(): string;
756
824
  /**
@@ -5,7 +5,7 @@
5
5
  * cleaning up stale files, and validating command arguments.
6
6
  */
7
7
  import { buildAgentDiscoveryHelp, buildCommonTaskExamples, buildUrlExamples, buildSessionManagementReminder, } from '../formatters/helpFormatters.js';
8
- import { formatDuration, joinLines, pluralize, truncateUrl } from '../formatting.js';
8
+ import { formatBytes, formatDuration, joinLines, pluralize, truncateUrl } from '../formatting.js';
9
9
  import { sessionCommand } from './sessionCommand.js';
10
10
  import { truncateByLength } from '../../utils/strings.js';
11
11
  /**
@@ -60,6 +60,41 @@ function fieldLabelList(labels) {
60
60
  .join(', ');
61
61
  return labels.length > 5 ? `${shown} and ${labels.length - 5} more` : shown;
62
62
  }
63
+ /**
64
+ * Warning for a `bdg cdp` method the bundled protocol lacks, sent anyway.
65
+ *
66
+ * @param method - Method sent
67
+ * @param protocolVersion - Bundled devtools-protocol version
68
+ * @returns Warning text
69
+ */
70
+ export function cdpUnlistedMethodWarning(method, protocolVersion) {
71
+ return `${method} is not in the bundled protocol (devtools-protocol ${protocolVersion}); sending it to Chrome as is`;
72
+ }
73
+ /**
74
+ * `bdg cdp --describe` line for a redirect to a method the protocol lacks
75
+ * (e.g. Page.deleteCookie names Network.deleteCookie).
76
+ *
77
+ * @param method - Redirect target
78
+ * @returns Line text
79
+ */
80
+ export function cdpUnresolvedRedirectLine(method) {
81
+ return `Redirect target ${method} is not in the protocol (unresolved redirect)`;
82
+ }
83
+ /**
84
+ * `bdg cdp --describe` title of a redirect to a method the protocol has.
85
+ *
86
+ * @param method - Redirect target
87
+ * @returns Title text
88
+ */
89
+ export function cdpRedirectTitle(method) {
90
+ return `Implemented by ${method} (redirect)`;
91
+ }
92
+ /**
93
+ * Lines of `bdg cdp --help` saying how names are matched (each short
94
+ * enough not to be wrapped).
95
+ */
96
+ export const CDP_EXECUTION_HELP = ' Execution: bundled methods are case-insensitive (network.getcookies works)\n' +
97
+ ' Other methods are sent as typed (case-sensitive)';
63
98
  /**
64
99
  * Part of the `bdg dom form` summary naming the required fields left empty.
65
100
  *
@@ -145,6 +180,56 @@ export function pageNavigationText(navigation) {
145
180
  const status = navigation.status === undefined ? '' : ` (${navigation.status})`;
146
181
  return `navigated to ${navigation.url}${status}`;
147
182
  }
183
+ /**
184
+ * A download an action started, as its output line reads.
185
+ *
186
+ * @param download - Download
187
+ * @returns e.g. `Download: report.txt → /Users/me/.bdg/downloads/report.txt (completed, 15 B)`,
188
+ * `Download: big.zip → … (inProgress, 9.8 KB so far)` (no size before the first
189
+ * bytes), `Download: report.txt (canceled)`
190
+ */
191
+ export function downloadText(download) {
192
+ const where = download.path === undefined ? '' : ` → ${download.path}`;
193
+ const running = download.state === 'inProgress';
194
+ const bytes = download.bytes === undefined || (running && download.bytes === 0)
195
+ ? ''
196
+ : `, ${formatBytes(download.bytes)}${running ? ' so far' : ''}`;
197
+ const reason = download.reason === undefined ? '' : `: ${download.reason}`;
198
+ return `Download: ${download.suggestedFilename}${where} (${download.state}${bytes}${reason})`;
199
+ }
200
+ /**
201
+ * The session's downloads in one line: how many, and the last one.
202
+ *
203
+ * @param downloads - Downloads, oldest first (at least one)
204
+ * @returns e.g. `2 (last: report.txt → /Users/me/.bdg/downloads/report.txt, completed)`
205
+ */
206
+ export function downloadsSummary(downloads) {
207
+ const last = downloads[downloads.length - 1];
208
+ if (!last)
209
+ return '0';
210
+ const where = last.path === undefined ? '' : ` → ${last.path}`;
211
+ return `${downloads.length} (last: ${last.suggestedFilename}${where}, ${last.state})`;
212
+ }
213
+ /**
214
+ * Warning while Chrome did not take bdg's download behavior, so downloads
215
+ * are not saved in the session directory.
216
+ *
217
+ * @param detail - Chrome's error
218
+ * @returns Warning shown by `bdg status`
219
+ */
220
+ export function downloadsNotRedirectedWarning(detail) {
221
+ return `downloads are not redirected to the session directory (Chrome refused the download behavior: ${detail}); Chrome saves them in its default folder, usually ~/Downloads`;
222
+ }
223
+ /**
224
+ * Why downloads are refused when the session's downloads directory could not
225
+ * be created.
226
+ *
227
+ * @param detail - What went wrong, e.g. `/home/me/.bdg/downloads is a file`
228
+ * @returns Reason recorded on each refused download
229
+ */
230
+ export function downloadsDirUnavailableReason(detail) {
231
+ return `bdg's downloads directory could not be created (${detail}), so downloads are refused`;
232
+ }
148
233
  /**
149
234
  * Last `New text:` row when an action made more messages appear than are listed.
150
235
  *
@@ -1161,9 +1246,19 @@ export function sessionOutputRemovedMessage() {
1161
1246
  return 'Session output file removed';
1162
1247
  }
1163
1248
  /**
1164
- * Generate session directory clean message.
1249
+ * Cleanup left the session's downloaded files in place.
1165
1250
  *
1166
- * @returns Formatted success message
1251
+ * @param dir - Downloads directory
1252
+ * @param files - Files in it
1253
+ * @returns e.g. `Downloads kept: 2 files in /Users/me/.bdg/downloads (delete them yourself when done)`
1254
+ */
1255
+ export function downloadsKeptMessage(dir, files) {
1256
+ return `Downloads kept: ${pluralize(files, 'file')} in ${dir} (delete them yourself when done)`;
1257
+ }
1258
+ /**
1259
+ * Cleanup finished and left nothing behind.
1260
+ *
1261
+ * @returns Message
1167
1262
  */
1168
1263
  export function sessionDirectoryCleanMessage() {
1169
1264
  return 'Session directory is now clean';
@@ -4,23 +4,42 @@
4
4
  * User-facing messages for the network list command output and formatting.
5
5
  */
6
6
  /**
7
- * Why a stored response body was replaced by a placeholder (shown by
8
- * `bdg details network <id>` as `bodyNotCaptured`).
7
+ * Why a stored request or response body was replaced by a placeholder
8
+ * (shown by `bdg details network <id>` as `requestBodyNotCaptured` or
9
+ * `bodyNotCaptured`).
9
10
  *
10
11
  * @param budgetBytes - Total body budget of the session
11
- * @returns e.g. `evicted: total body budget (bdg keeps the newest 100 MB of response bodies)`
12
+ * @returns e.g. `evicted: total body budget (bdg keeps the newest 100 MB of request and response bodies)`
12
13
  */
13
14
  export declare function bodyEvictedReason(budgetBytes: number): string;
15
+ /**
16
+ * Why a response body is missing when Chrome no longer had it
17
+ * (`Network.getResponseBody` failed with "No resource with given identifier
18
+ * found" or "No data found for resource with given identifier"), shown by
19
+ * `bdg details network <id>` as `bodyNotCaptured` and in the HAR as the
20
+ * content comment.
21
+ *
22
+ * @returns Reason text
23
+ */
24
+ export declare function bodyGoneReason(): string;
25
+ /**
26
+ * Why a response body is missing when `Network.getResponseBody` failed for
27
+ * another reason (a CDP timeout, the connection closing mid-fetch).
28
+ *
29
+ * @param errorMessage - The error Chrome or the connection gave
30
+ * @returns e.g. `Chrome did not return the body: CDP command timeout`
31
+ */
32
+ export declare function bodyFetchFailedReason(errorMessage: string): string;
14
33
  /** What a session's network capture let go at its limits */
15
34
  export interface NetworkEvictionCounts {
16
35
  /** Oldest finished requests dropped at the request cap */
17
36
  requestsDropped: number;
18
- /** Oldest response bodies evicted at the body budget */
37
+ /** Oldest request and response bodies evicted at the body budget */
19
38
  bodiesEvicted: number;
20
39
  }
21
40
  /**
22
41
  * Note that the session dropped its oldest requests or evicted its oldest
23
- * response bodies at its limits.
42
+ * request and response bodies at its limits.
24
43
  *
25
44
  * @param counts - Requests dropped and bodies evicted
26
45
  * @returns e.g. `⚠ 2000 older network requests were dropped: bdg keeps the newest 10000`;
@@ -53,4 +72,30 @@ export declare function headerRepeatedNote(count: number): string;
53
72
  * @returns Note text
54
73
  */
55
74
  export declare function localProxyNote(): string;
75
+ /**
76
+ * HAR log comment of a sanitized export.
77
+ *
78
+ * @returns Comment naming what was redacted and the flag that keeps it
79
+ */
80
+ export declare function harSanitizedComment(): string;
81
+ /**
82
+ * Result of a HAR export to a file.
83
+ */
84
+ export interface HarExportSummary {
85
+ /** Absolute path written */
86
+ file: string;
87
+ /** Requests exported */
88
+ entries: number;
89
+ /** Whether --filter left requests out */
90
+ filtered: boolean;
91
+ /** Whether credentials were redacted */
92
+ sanitized: boolean;
93
+ }
94
+ /**
95
+ * Success message of `bdg network har`.
96
+ *
97
+ * @param result - Export result
98
+ * @returns e.g. `✓ Exported 4 requests to /tmp/out.har` and a line on sanitization
99
+ */
100
+ export declare function harExportedMessage(result: HarExportSummary): string;
56
101
  //# sourceMappingURL=networkMessages.d.ts.map