browser-debugger-cli 0.8.0 → 0.9.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 (251) hide show
  1. package/README.md +4 -1
  2. package/dist/cdp/schema.d.ts +4 -1
  3. package/dist/cdp/schema.js +48 -7
  4. package/dist/commands/cdp.js +3 -2
  5. package/dist/commands/cleanup.d.ts +11 -0
  6. package/dist/commands/cleanup.js +161 -57
  7. package/dist/commands/console.d.ts +20 -1
  8. package/dist/commands/console.js +57 -17
  9. package/dist/commands/details.js +3 -2
  10. package/dist/commands/dom/DomElementResolver.d.ts +10 -3
  11. package/dist/commands/dom/DomElementResolver.js +35 -17
  12. package/dist/commands/dom/a11y.d.ts +10 -0
  13. package/dist/commands/dom/a11y.js +27 -5
  14. package/dist/commands/dom/eval.d.ts +3 -1
  15. package/dist/commands/dom/eval.js +29 -4
  16. package/dist/commands/dom/form.js +16 -62
  17. package/dist/commands/dom/formInteraction.js +152 -113
  18. package/dist/commands/dom/formSummary.d.ts +49 -0
  19. package/dist/commands/dom/formSummary.js +180 -0
  20. package/dist/commands/dom/frames.d.ts +2 -1
  21. package/dist/commands/dom/frames.js +17 -2
  22. package/dist/commands/dom/get.d.ts +6 -5
  23. package/dist/commands/dom/get.js +92 -82
  24. package/dist/commands/dom/helpers/index.d.ts +1 -1
  25. package/dist/commands/dom/helpers/index.js +1 -1
  26. package/dist/commands/dom/helpers/query.d.ts +44 -17
  27. package/dist/commands/dom/helpers/query.js +244 -97
  28. package/dist/commands/dom/helpers/runElementCommand.d.ts +10 -2
  29. package/dist/commands/dom/helpers/runElementCommand.js +97 -30
  30. package/dist/commands/dom/helpers/screenshot.d.ts +4 -1
  31. package/dist/commands/dom/helpers/screenshot.js +164 -49
  32. package/dist/commands/dom/index.d.ts +3 -1
  33. package/dist/commands/dom/index.js +16 -6
  34. package/dist/commands/dom/layout.d.ts +14 -0
  35. package/dist/commands/dom/layout.js +54 -0
  36. package/dist/commands/dom/listeners.d.ts +5 -1
  37. package/dist/commands/dom/listeners.js +13 -3
  38. package/dist/commands/dom/query.js +2 -3
  39. package/dist/commands/dom/screenshot.d.ts +12 -2
  40. package/dist/commands/dom/screenshot.js +27 -3
  41. package/dist/commands/dom/semanticUtils.d.ts +6 -13
  42. package/dist/commands/dom/semanticUtils.js +15 -19
  43. package/dist/commands/dom/wait.d.ts +13 -0
  44. package/dist/commands/dom/wait.js +83 -0
  45. package/dist/commands/helpJson.js +2 -2
  46. package/dist/commands/network/list.js +4 -11
  47. package/dist/commands/optionBehaviors.js +112 -21
  48. package/dist/commands/page.d.ts +2 -1
  49. package/dist/commands/page.js +41 -5
  50. package/dist/commands/peek.js +4 -11
  51. package/dist/commands/sessions.d.ts +8 -0
  52. package/dist/commands/sessions.js +19 -0
  53. package/dist/commands/shared/CommandRunner.js +4 -4
  54. package/dist/commands/shared/dataFetcher.js +2 -2
  55. package/dist/commands/shared/followMode.d.ts +21 -1
  56. package/dist/commands/shared/followMode.js +29 -2
  57. package/dist/commands/shared/handleValidationError.d.ts +2 -2
  58. package/dist/commands/shared/handleValidationError.js +12 -3
  59. package/dist/commands/shared/optionTypes.d.ts +40 -5
  60. package/dist/commands/shared/startHelpers.js +12 -3
  61. package/dist/commands/shared/validation.d.ts +3 -2
  62. package/dist/commands/shared/validation.js +4 -3
  63. package/dist/commands/start.d.ts +63 -0
  64. package/dist/commands/start.js +115 -15
  65. package/dist/commands/status.js +29 -7
  66. package/dist/commands/stop.js +7 -6
  67. package/dist/commands/tail.js +4 -11
  68. package/dist/commands/types.d.ts +2 -0
  69. package/dist/commands.js +2 -0
  70. package/dist/connection/chromeIdentity.d.ts +65 -0
  71. package/dist/connection/chromeIdentity.js +143 -0
  72. package/dist/connection/launcher/profilePreferences.d.ts +47 -0
  73. package/dist/connection/launcher/profilePreferences.js +151 -0
  74. package/dist/connection/launcher.d.ts +21 -2
  75. package/dist/connection/launcher.js +42 -16
  76. package/dist/connection/portReservation.d.ts +14 -4
  77. package/dist/connection/portReservation.js +21 -6
  78. package/dist/connection/startupExit.d.ts +8 -0
  79. package/dist/connection/startupExit.js +15 -6
  80. package/dist/constants.d.ts +6 -2
  81. package/dist/constants.js +9 -2
  82. package/dist/daemon/SessionController.js +23 -7
  83. package/dist/daemon/errors.d.ts +1 -1
  84. package/dist/daemon/errors.js +1 -1
  85. package/dist/daemon/launcher.d.ts +2 -1
  86. package/dist/daemon/launcher.js +5 -6
  87. package/dist/daemon/server/SocketServer.js +1 -2
  88. package/dist/daemon/session/Session.d.ts +13 -0
  89. package/dist/daemon/session/Session.js +57 -8
  90. package/dist/daemon/session/chromeConnection.d.ts +9 -0
  91. package/dist/daemon/session/chromeConnection.js +45 -8
  92. package/dist/daemon/session/commandRegistry.js +52 -62
  93. package/dist/daemon/session/interactions.d.ts +35 -9
  94. package/dist/daemon/session/interactions.js +36 -9
  95. package/dist/daemon/session/triggeredRequests.d.ts +67 -0
  96. package/dist/daemon/session/triggeredRequests.js +157 -0
  97. package/dist/daemon/session/types.d.ts +5 -1
  98. package/dist/daemon.js +5393 -1600
  99. package/dist/errors/messages.d.ts +387 -24
  100. package/dist/errors/messages.js +761 -67
  101. package/dist/index.js +3976 -1558
  102. package/dist/ipc/client.d.ts +12 -1
  103. package/dist/ipc/client.js +22 -3
  104. package/dist/ipc/protocol/commands.d.ts +89 -4
  105. package/dist/ipc/protocol/commands.js +2 -0
  106. package/dist/ipc/protocol/domTypes.d.ts +258 -7
  107. package/dist/ipc/session/lifecycle.d.ts +8 -1
  108. package/dist/ipc/session/queries.d.ts +5 -1
  109. package/dist/ipc/session/types.d.ts +5 -0
  110. package/dist/ipc/transport/index.d.ts +2 -1
  111. package/dist/ipc/transport/index.js +2 -2
  112. package/dist/runtime/dom/actionEffects.d.ts +106 -0
  113. package/dist/runtime/dom/actionEffects.js +256 -0
  114. package/dist/runtime/dom/actionEffectsScripts.d.ts +52 -0
  115. package/dist/runtime/dom/actionEffectsScripts.js +234 -0
  116. package/dist/runtime/dom/elementGeometry.d.ts +170 -0
  117. package/dist/runtime/dom/elementGeometry.js +553 -0
  118. package/dist/runtime/dom/elementInfo.d.ts +77 -0
  119. package/dist/runtime/dom/elementInfo.js +191 -0
  120. package/dist/runtime/dom/evalHelpers.d.ts +51 -6
  121. package/dist/runtime/dom/evalHelpers.js +136 -26
  122. package/dist/runtime/dom/eventListeners.d.ts +2 -1
  123. package/dist/runtime/dom/eventListeners.js +174 -47
  124. package/dist/runtime/dom/formDiscovery.d.ts +1 -1
  125. package/dist/runtime/dom/formDiscovery.js +116 -16
  126. package/dist/runtime/dom/formFillHelpers/fill.d.ts +10 -0
  127. package/dist/runtime/dom/formFillHelpers/fill.js +125 -10
  128. package/dist/runtime/dom/formFillHelpers/index.d.ts +2 -2
  129. package/dist/runtime/dom/formFillHelpers/index.js +2 -2
  130. package/dist/runtime/dom/formFillHelpers/pressKey.js +14 -3
  131. package/dist/runtime/dom/formFillHelpers/scroll.d.ts +3 -0
  132. package/dist/runtime/dom/formFillHelpers/scroll.js +60 -18
  133. package/dist/runtime/dom/formFillHelpers/shared.d.ts +17 -0
  134. package/dist/runtime/dom/formFillHelpers/shared.js +25 -1
  135. package/dist/runtime/dom/formFillHelpers/stability.d.ts +20 -6
  136. package/dist/runtime/dom/formFillHelpers/stability.js +50 -19
  137. package/dist/runtime/dom/formSubmitHelpers.d.ts +3 -0
  138. package/dist/runtime/dom/formSubmitHelpers.js +89 -15
  139. package/dist/runtime/dom/frameLayout.d.ts +60 -0
  140. package/dist/runtime/dom/frameLayout.js +140 -0
  141. package/dist/runtime/dom/frameOrigin.d.ts +50 -0
  142. package/dist/runtime/dom/frameOrigin.js +62 -0
  143. package/dist/runtime/dom/frameScopedConnection.d.ts +92 -0
  144. package/dist/runtime/dom/frameScopedConnection.js +252 -0
  145. package/dist/runtime/dom/frameSelection.d.ts +1 -1
  146. package/dist/runtime/dom/frameSelection.js +2 -2
  147. package/dist/runtime/dom/frames.d.ts +25 -2
  148. package/dist/runtime/dom/frames.js +202 -63
  149. package/dist/runtime/dom/layout.d.ts +67 -0
  150. package/dist/runtime/dom/layout.js +333 -0
  151. package/dist/runtime/dom/listenerPageScripts.d.ts +66 -0
  152. package/dist/runtime/dom/listenerPageScripts.js +279 -0
  153. package/dist/runtime/dom/listenerSummary.d.ts +132 -11
  154. package/dist/runtime/dom/listenerSummary.js +344 -22
  155. package/dist/runtime/dom/pageActivity.d.ts +41 -0
  156. package/dist/runtime/dom/pageActivity.js +123 -0
  157. package/dist/runtime/dom/reactEventHelpers.d.ts +58 -2
  158. package/dist/runtime/dom/reactEventHelpers.js +212 -41
  159. package/dist/runtime/dom/targetNode.d.ts +80 -27
  160. package/dist/runtime/dom/targetNode.js +249 -33
  161. package/dist/runtime/dom/wait.d.ts +25 -0
  162. package/dist/runtime/dom/wait.js +199 -0
  163. package/dist/runtime/dom/waitCondition.d.ts +71 -0
  164. package/dist/runtime/dom/waitCondition.js +75 -0
  165. package/dist/runtime/page/emulation.d.ts +51 -0
  166. package/dist/runtime/page/emulation.js +80 -0
  167. package/dist/runtime/page/loadingState.d.ts +36 -0
  168. package/dist/runtime/page/loadingState.js +86 -0
  169. package/dist/runtime/page/navigation.d.ts +46 -2
  170. package/dist/runtime/page/navigation.js +69 -33
  171. package/dist/session/QueryCacheManager.d.ts +11 -1
  172. package/dist/session/QueryCacheManager.js +25 -3
  173. package/dist/session/chromeOwners.d.ts +34 -0
  174. package/dist/session/chromeOwners.js +51 -0
  175. package/dist/session/cleanup/staleSession.d.ts +11 -1
  176. package/dist/session/cleanup/staleSession.js +17 -6
  177. package/dist/session/cleanup/userCommands.js +2 -4
  178. package/dist/session/metadata.d.ts +5 -1
  179. package/dist/session/metadata.js +2 -1
  180. package/dist/session/paths.d.ts +77 -3
  181. package/dist/session/paths.js +111 -5
  182. package/dist/session/port.d.ts +31 -7
  183. package/dist/session/port.js +50 -43
  184. package/dist/session/portClaims.d.ts +66 -0
  185. package/dist/session/portClaims.js +284 -0
  186. package/dist/session/sessionList.d.ts +58 -0
  187. package/dist/session/sessionList.js +199 -0
  188. package/dist/session/sessionName.d.ts +46 -0
  189. package/dist/session/sessionName.js +97 -0
  190. package/dist/telemetry/a11y.d.ts +8 -3
  191. package/dist/telemetry/a11y.js +92 -28
  192. package/dist/telemetry/requestKinds.d.ts +32 -0
  193. package/dist/telemetry/requestKinds.js +61 -0
  194. package/dist/telemetry/requestState.d.ts +31 -0
  195. package/dist/telemetry/requestState.js +38 -0
  196. package/dist/types.d.ts +80 -3
  197. package/dist/ui/formatters/a11y.js +3 -0
  198. package/dist/ui/formatters/console/chronological.d.ts +8 -0
  199. package/dist/ui/formatters/console/chronological.js +17 -4
  200. package/dist/ui/formatters/console/json.js +3 -4
  201. package/dist/ui/formatters/console/shared.d.ts +12 -0
  202. package/dist/ui/formatters/console.d.ts +2 -2
  203. package/dist/ui/formatters/console.js +1 -1
  204. package/dist/ui/formatters/details.js +2 -1
  205. package/dist/ui/formatters/dom.d.ts +26 -14
  206. package/dist/ui/formatters/dom.js +65 -52
  207. package/dist/ui/formatters/form.js +29 -18
  208. package/dist/ui/formatters/layout.d.ts +31 -0
  209. package/dist/ui/formatters/layout.js +53 -0
  210. package/dist/ui/formatters/listeners.d.ts +3 -2
  211. package/dist/ui/formatters/listeners.js +73 -9
  212. package/dist/ui/formatters/networkHeaders.js +13 -0
  213. package/dist/ui/formatters/preview.js +2 -1
  214. package/dist/ui/formatters/requestStatus.d.ts +1 -17
  215. package/dist/ui/formatters/requestStatus.js +2 -30
  216. package/dist/ui/formatters/sessions.d.ts +12 -0
  217. package/dist/ui/formatters/sessions.js +40 -0
  218. package/dist/ui/formatters/status.d.ts +21 -2
  219. package/dist/ui/formatters/status.js +47 -10
  220. package/dist/ui/formatters/triggeredRequests.d.ts +36 -0
  221. package/dist/ui/formatters/triggeredRequests.js +65 -0
  222. package/dist/ui/formatting.d.ts +10 -0
  223. package/dist/ui/formatting.js +28 -36
  224. package/dist/ui/messages/chrome.d.ts +9 -0
  225. package/dist/ui/messages/chrome.js +17 -5
  226. package/dist/ui/messages/commands.d.ts +388 -14
  227. package/dist/ui/messages/commands.js +664 -21
  228. package/dist/ui/messages/consoleMessages.d.ts +10 -0
  229. package/dist/ui/messages/consoleMessages.js +17 -0
  230. package/dist/ui/messages/hints.js +2 -1
  231. package/dist/ui/messages/preview.js +5 -4
  232. package/dist/ui/messages/session.d.ts +16 -21
  233. package/dist/ui/messages/session.js +28 -26
  234. package/dist/ui/messages/sessionCommand.d.ts +43 -0
  235. package/dist/ui/messages/sessionCommand.js +52 -0
  236. package/dist/utils/async.d.ts +8 -0
  237. package/dist/utils/async.js +19 -0
  238. package/dist/utils/http.d.ts +22 -1
  239. package/dist/utils/http.js +28 -9
  240. package/dist/utils/selectorFilters.d.ts +36 -8
  241. package/dist/utils/selectorFilters.js +267 -53
  242. package/dist/utils/shellDetection.d.ts +8 -2
  243. package/dist/utils/shellDetection.js +120 -33
  244. package/dist/utils/suggestions.d.ts +26 -0
  245. package/dist/utils/suggestions.js +73 -0
  246. package/dist/utils/taskMappings.js +10 -0
  247. package/dist/utils/url.d.ts +12 -2
  248. package/dist/utils/url.js +69 -7
  249. package/package.json +1 -1
  250. package/dist/ui/formatters/sessionFormatters.d.ts +0 -58
  251. package/dist/ui/formatters/sessionFormatters.js +0 -121
@@ -7,11 +7,12 @@ import { startSessionViaDaemon } from './shared/startHelpers.js';
7
7
  import { positiveIntRule } from './shared/validation.js';
8
8
  import { PORT_OPTION_DESCRIPTION } from '../constants.js';
9
9
  import { CommandError } from '../errors/index.js';
10
- import { chromeWsUrlConflictError, invalidChromeFlagError, invalidUserDataDirError, missingStartUrlError, unknownCommandError, } from '../errors/messages.js';
10
+ import { chromeWsUrlConflictError, externalChromeUnreachableError, invalidChromeFlagError, notDevToolsEndpointError, invalidColorSchemeError, invalidUserDataDirError, invalidViewportError, missingStartUrlError, unknownCommandError, } from '../errors/messages.js';
11
11
  import { startCommandHelpMessage } from '../ui/messages/commands.js';
12
12
  import { EXIT_CODES } from '../utils/exitCodes.js';
13
+ import { probeDevToolsEndpoint } from '../utils/http.js';
13
14
  import { findSimilar } from '../utils/suggestions.js';
14
- import { validateChromeWsUrl, validateUrl } from '../utils/url.js';
15
+ import { devToolsHttpEndpoint, validateChromeWsUrl, validateUrl } from '../utils/url.js';
15
16
  /**
16
17
  * Check if a display server (X11 or Wayland) is available.
17
18
  * Used to determine default headless mode.
@@ -63,7 +64,7 @@ export function extractUserDataDirFromFlags(flags) {
63
64
  * @param command - Commander.js Command instance to apply options to
64
65
  * @returns The modified Command instance with all telemetry options applied
65
66
  */
66
- function applyCollectorOptions(command) {
67
+ export function applyCollectorOptions(command) {
67
68
  // Default to headless if no display available
68
69
  const defaultHeadless = !hasDisplay();
69
70
  return command
@@ -75,10 +76,48 @@ function applyCollectorOptions(command) {
75
76
  .addOption(new Option('--compact', 'No effect; kept for compatibility').hideHelp())
76
77
  .option('--headless', 'Run Chrome without a window (default unless DISPLAY or WAYLAND_DISPLAY is set)', defaultHeadless)
77
78
  .option('--no-headless', 'Show browser window')
78
- .option('--chrome-ws-url <url>', 'Connect to existing Chrome via its DevTools WebSocket URL: browser (ws://host:port/devtools/browser/<id>, uses the first tab) or page (.../devtools/page/<id>)')
79
+ .option('--chrome-ws-url <url>', 'Connect to an existing Chrome: its DevTools port (9222, host:port, http://host:port), or a WebSocket URL: browser (ws://host:port/devtools/browser/<id>, uses the first tab) or page (.../devtools/page/<id>)')
79
80
  .option('-q, --quiet', 'Quiet mode - minimal output for AI agents', false)
80
81
  .addOption(jsonOption())
81
- .option('--chrome-flags <flags>', 'Custom Chrome flags (space-separated, e.g., --chrome-flags="--ignore-certificate-errors --disable-web-security")');
82
+ .option('--chrome-flags <flags>', 'Custom Chrome flags (space-separated, e.g., --chrome-flags="--ignore-certificate-errors --disable-web-security")')
83
+ .option('--viewport <WxH>', 'Viewport size in CSS px for the whole session, e.g. 1280x800 (default: 1920x1080 window)')
84
+ .option('--color-scheme <scheme>', 'Emulate prefers-color-scheme for the session: light or dark (default: the system setting)');
85
+ }
86
+ /** Largest viewport side accepted by `--viewport` (CSS px) */
87
+ const MAX_VIEWPORT_SIDE = 10000;
88
+ /** Values of `--color-scheme` */
89
+ const COLOR_SCHEMES = ['light', 'dark'];
90
+ /**
91
+ * Parse a `--viewport` value: width and height in CSS px joined by `x`
92
+ * (`1280x800`; `X`, `×` and `,` work too).
93
+ *
94
+ * @param value - Option value
95
+ * @returns Viewport size
96
+ * @throws CommandError (81) for anything else, or a side outside 1-10000
97
+ */
98
+ export function parseViewport(value) {
99
+ const match = /^\s*(\d+)\s*[xX×,]\s*(\d+)\s*$/.exec(value);
100
+ const width = Number(match?.[1]);
101
+ const height = Number(match?.[2]);
102
+ const valid = (side) => side >= 1 && side <= MAX_VIEWPORT_SIDE;
103
+ if (match && valid(width) && valid(height))
104
+ return { width, height };
105
+ const err = invalidViewportError(value, MAX_VIEWPORT_SIDE);
106
+ throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
107
+ }
108
+ /**
109
+ * Parse a `--color-scheme` value (case-insensitive).
110
+ *
111
+ * @param value - Option value
112
+ * @returns The scheme
113
+ * @throws CommandError (81) for another value, suggesting the closest one
114
+ */
115
+ export function parseColorScheme(value) {
116
+ const scheme = COLOR_SCHEMES.find((candidate) => candidate === value.trim().toLowerCase());
117
+ if (scheme)
118
+ return scheme;
119
+ const err = invalidColorSchemeError(value, findSimilar(value, [...COLOR_SCHEMES]), COLOR_SCHEMES);
120
+ throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
82
121
  }
83
122
  /**
84
123
  * Transform CLI options into session options
@@ -118,6 +157,8 @@ function buildSessionOptions(options) {
118
157
  quiet: options.quiet ?? false,
119
158
  json: options.json ?? false,
120
159
  chromeFlags,
160
+ viewport: options.viewport !== undefined ? parseViewport(options.viewport) : undefined,
161
+ colorScheme: options.colorScheme !== undefined ? parseColorScheme(options.colorScheme) : undefined,
121
162
  };
122
163
  }
123
164
  /** Telemetry collected by every session. */
@@ -166,20 +207,21 @@ export function assertNotGroupSubcommand(program, argv) {
166
207
  *
167
208
  * @param url - Target URL
168
209
  * @param options - Parsed command-line options from Commander
169
- * @param commandNames - Registered top-level command names (for typo detection)
210
+ * @param program - Root command (registered command names for typo detection,
211
+ * and which options came from the command line)
170
212
  * @returns The URL and normalized session options
171
213
  * @throws CommandError on any invalid input
172
214
  */
173
- function validateStartInput(url, options, commandNames) {
215
+ function validateStartInput(url, options, program) {
174
216
  if (url === undefined) {
175
217
  const err = missingStartUrlError();
176
218
  throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
177
219
  }
178
- assertNotCommandTypo(url, commandNames);
220
+ assertNotCommandTypo(url, program.commands.map((command) => command.name()));
179
221
  assertValidUrl(url);
180
222
  if (options.chromeWsUrl !== undefined) {
181
223
  assertValidChromeWsUrl(options.chromeWsUrl);
182
- assertNoLaunchOptions(options);
224
+ assertNoLaunchOptions(options, program);
183
225
  }
184
226
  if (options.userDataDir !== undefined)
185
227
  assertUserDataDir(options.userDataDir);
@@ -204,7 +246,8 @@ export function registerStartCommands(program) {
204
246
  }
205
247
  let validated;
206
248
  try {
207
- validated = validateStartInput(url, options, program.commands.map((command) => command.name()));
249
+ validated = validateStartInput(url, options, program);
250
+ validated.sessionOptions.chromeWsUrl = await resolveChromeWsUrl(validated.sessionOptions.chromeWsUrl);
208
251
  }
209
252
  catch (error) {
210
253
  handleValidationError(error, options.json ?? false);
@@ -212,6 +255,49 @@ export function registerStartCommands(program) {
212
255
  await startSessionViaDaemon(validated.url, validated.sessionOptions, SESSION_TELEMETRY);
213
256
  });
214
257
  }
258
+ /**
259
+ * Turn a `--chrome-ws-url` that names the HTTP DevTools endpoint (a port,
260
+ * `host:port`, or `http://host:port`) into the browser's WebSocket URL from
261
+ * its `/json/version`, so users need not look it up themselves.
262
+ *
263
+ * @param value - Option value (WebSocket URLs are returned unchanged)
264
+ * @returns WebSocket URL, or undefined without the option
265
+ * @throws CommandError (101) when the endpoint does not answer or is not DevTools
266
+ */
267
+ async function resolveChromeWsUrl(value) {
268
+ const endpoint = value === undefined ? null : devToolsHttpEndpoint(value);
269
+ if (endpoint === null)
270
+ return value;
271
+ const { hostname, port, protocol } = new URL(endpoint);
272
+ const secure = protocol === 'https:';
273
+ const defaultPort = secure ? 443 : 80;
274
+ const probe = await probeDevToolsEndpoint(Number(port) || defaultPort, undefined, {
275
+ host: hostname,
276
+ secure,
277
+ });
278
+ if (probe.kind === 'devtools')
279
+ return atEndpoint(probe.wsUrl, endpoint);
280
+ const err = probe.kind === 'not-devtools'
281
+ ? notDevToolsEndpointError(endpoint)
282
+ : externalChromeUnreachableError(endpoint, false);
283
+ throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.CDP_CONNECTION_FAILURE);
284
+ }
285
+ /**
286
+ * Point a WebSocket URL Chrome reported at the endpoint the user gave: Chrome
287
+ * reports its own host and port, which differ behind a proxy or forwarded
288
+ * port, and only ws:// (an https endpoint is a TLS proxy, so wss://).
289
+ *
290
+ * @param wsUrl - URL from `/json/version`
291
+ * @param endpoint - Endpoint origin the user gave
292
+ * @returns The URL with the endpoint's host, port and matching scheme
293
+ */
294
+ function atEndpoint(wsUrl, endpoint) {
295
+ const resolved = new URL(wsUrl);
296
+ const target = new URL(endpoint);
297
+ resolved.protocol = target.protocol === 'https:' ? 'wss:' : 'ws:';
298
+ resolved.host = target.host;
299
+ return resolved.toString();
300
+ }
215
301
  /**
216
302
  * Whether any start option was given on the command line (`bdg --port 9333`
217
303
  * without a URL is an error, a bare `bdg` shows help).
@@ -244,16 +330,30 @@ function assertUserDataDir(value) {
244
330
  throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
245
331
  }
246
332
  /**
247
- * Reject options that only apply to a Chrome bdg launches.
333
+ * Options given that only apply to a Chrome bdg launches. `--headless` has a
334
+ * default, so it counts only when given on the command line.
248
335
  *
249
- * @param options - Parsed options (with `--chrome-ws-url`)
250
- * @throws CommandError (81) for `--port` or `-u`
336
+ * @param options - Parsed options
337
+ * @param program - Command the options were parsed by
338
+ * @returns The conflicting flags, e.g. ["--port", "--headless"]
251
339
  */
252
- function assertNoLaunchOptions(options) {
253
- const conflicts = [
340
+ export function launchOptionConflicts(options, program) {
341
+ const headlessGiven = program.getOptionValueSource('headless') === 'cli';
342
+ return [
254
343
  ...(options.port !== undefined ? ['--port'] : []),
255
344
  ...(options.userDataDir !== undefined ? ['--user-data-dir'] : []),
345
+ ...(headlessGiven ? [options.headless ? '--headless' : '--no-headless'] : []),
256
346
  ];
347
+ }
348
+ /**
349
+ * Reject options that only apply to a Chrome bdg launches.
350
+ *
351
+ * @param options - Parsed options (with `--chrome-ws-url`)
352
+ * @param program - Command the options were parsed by
353
+ * @throws CommandError (81) for `--port`, `-u` or `--[no-]headless`
354
+ */
355
+ function assertNoLaunchOptions(options, program) {
356
+ const conflicts = launchOptionConflicts(options, program);
257
357
  if (conflicts.length === 0)
258
358
  return;
259
359
  const err = chromeWsUrlConflictError(conflicts);
@@ -6,7 +6,9 @@ import { IPCTimeoutError } from '../ipc/transport/IPCError.js';
6
6
  import { describeRunningChrome } from '../session/chrome.js';
7
7
  import { findOrphanedChrome, removeStaleDaemonFiles } from '../session/cleanup/staleSession.js';
8
8
  import { readLastSessionEnd } from '../session/lastSession.js';
9
+ import { getSessionName } from '../session/paths.js';
9
10
  import { formatSessionStatus, formatStatusAsJson, formatNoSessionMessage, } from '../ui/formatters/status.js';
11
+ import { sessionCommand } from '../ui/messages/sessionCommand.js';
10
12
  import { getErrorMessage } from '../utils/errors.js';
11
13
  import { EXIT_CODES } from '../utils/exitCodes.js';
12
14
  /**
@@ -30,6 +32,24 @@ function inactiveStatus(starting, ending) {
30
32
  ...(lastSession && { lastSession }),
31
33
  };
32
34
  }
35
+ /**
36
+ * Add the selected session's name (named sessions only).
37
+ *
38
+ * @param data - Status data
39
+ * @returns Status data with `session` for a named session
40
+ */
41
+ function withSessionName(data) {
42
+ const session = getSessionName();
43
+ return session === null ? data : { session, ...data };
44
+ }
45
+ /**
46
+ * Force-clean the selected session and start it again.
47
+ *
48
+ * @returns Command line
49
+ */
50
+ function resetCommand() {
51
+ return `${sessionCommand('bdg cleanup --force')} && ${sessionCommand('bdg <url>')}`;
52
+ }
33
53
  /**
34
54
  * Register status command
35
55
  *
@@ -56,7 +76,7 @@ export function registerStatusCommand(program) {
56
76
  error: `Daemon error: ${response.error ?? 'Unknown error'}`,
57
77
  exitCode: EXIT_CODES.SOFTWARE_ERROR,
58
78
  errorContext: {
59
- suggestion: 'Try: bdg cleanup --force && bdg <url>',
79
+ suggestion: `Try: ${resetCommand()}`,
60
80
  },
61
81
  };
62
82
  }
@@ -67,7 +87,7 @@ export function registerStatusCommand(program) {
67
87
  error: invalidResponseError('missing data'),
68
88
  exitCode: EXIT_CODES.SOFTWARE_ERROR,
69
89
  errorContext: {
70
- suggestion: 'This is unexpected. Try: bdg cleanup --force && bdg <url>',
90
+ suggestion: `This is unexpected. Try: ${resetCommand()}`,
71
91
  },
72
92
  };
73
93
  }
@@ -83,7 +103,7 @@ export function registerStatusCommand(program) {
83
103
  if (data.pageState) {
84
104
  jsonOutput.pageState = data.pageState;
85
105
  }
86
- return { success: true, data: jsonOutput };
106
+ return { success: true, data: withSessionName(jsonOutput) };
87
107
  }
88
108
  const metadata = {
89
109
  bdgPid: data.sessionMetadata.bdgPid,
@@ -94,6 +114,8 @@ export function registerStatusCommand(program) {
94
114
  webSocketDebuggerUrl: data.sessionMetadata.webSocketDebuggerUrl,
95
115
  activeTelemetry: data.sessionMetadata.activeTelemetry,
96
116
  autoStopAt: data.sessionMetadata.autoStopAt,
117
+ viewport: data.sessionMetadata.viewport,
118
+ colorScheme: data.sessionMetadata.colorScheme,
97
119
  };
98
120
  latestMetadata = metadata;
99
121
  latestSessionPid = data.sessionPid;
@@ -109,7 +131,7 @@ export function registerStatusCommand(program) {
109
131
  if (data.pageState) {
110
132
  jsonOutput.pageState = data.pageState;
111
133
  }
112
- return { success: true, data: jsonOutput };
134
+ return { success: true, data: withSessionName(jsonOutput) };
113
135
  }
114
136
  catch (error) {
115
137
  const errorMessage = getErrorMessage(error);
@@ -126,14 +148,14 @@ export function registerStatusCommand(program) {
126
148
  await removeStaleDaemonFiles();
127
149
  latestMetadata = undefined;
128
150
  latestSessionPid = undefined;
129
- return { success: true, data: inactiveStatus() };
151
+ return { success: true, data: withSessionName(inactiveStatus()) };
130
152
  }
131
153
  return {
132
154
  success: false,
133
155
  error: `Error checking status: ${errorMessage}`,
134
156
  exitCode: EXIT_CODES.SOFTWARE_ERROR,
135
157
  errorContext: {
136
- suggestion: 'Try: bdg cleanup --force to reset session state',
158
+ suggestion: `Try: ${sessionCommand('bdg cleanup --force')} to reset session state`,
137
159
  },
138
160
  };
139
161
  }
@@ -144,7 +166,7 @@ export function registerStatusCommand(program) {
144
166
  if (!latestMetadata || latestSessionPid === undefined) {
145
167
  return formatNoSessionMessage();
146
168
  }
147
- return formatSessionStatus(latestMetadata, latestSessionPid, latestActivity, latestPageState, options.verbose ?? false);
169
+ return formatSessionStatus(latestMetadata, latestSessionPid, latestActivity, latestPageState, options.verbose ?? false, data.session);
148
170
  });
149
171
  });
150
172
  }
@@ -6,6 +6,7 @@ import { IPCTimeoutError } from '../ipc/transport/index.js';
6
6
  import { joinLines } from '../ui/formatting.js';
7
7
  import { chromeClosedMessage, orphanedDaemonsCleanedMessage, warningMessage, } from '../ui/messages/commands.js';
8
8
  import { sessionStopped, STOP_MESSAGES, stopFailedError } from '../ui/messages/session.js';
9
+ import { noActiveSessionMessage, sessionCommand, startSessionSuggestion, } from '../ui/messages/sessionCommand.js';
9
10
  import { getExitCodeForIPCError, isDaemonNotRunningError } from '../utils/errorMapping.js';
10
11
  import { getErrorMessage } from '../utils/errors.js';
11
12
  import { EXIT_CODES } from '../utils/exitCodes.js';
@@ -54,10 +55,10 @@ export function registerStopCommand(program) {
54
55
  if (response.errorCode === IPCErrorCode.NO_SESSION) {
55
56
  return {
56
57
  success: false,
57
- error: response.message ?? STOP_MESSAGES.NO_SESSION,
58
+ error: response.message ?? noActiveSessionMessage(),
58
59
  exitCode: EXIT_CODES.RESOURCE_NOT_FOUND,
59
60
  errorContext: {
60
- suggestion: 'Start a session with: bdg <url>',
61
+ suggestion: startSessionSuggestion(),
61
62
  },
62
63
  };
63
64
  }
@@ -67,7 +68,7 @@ export function registerStopCommand(program) {
67
68
  error: response.message ?? STOP_MESSAGES.FAILED,
68
69
  exitCode,
69
70
  errorContext: {
70
- suggestion: 'Check session status with: bdg status',
71
+ suggestion: `Check session status with: ${sessionCommand('bdg status')}`,
71
72
  },
72
73
  };
73
74
  }
@@ -79,10 +80,10 @@ export function registerStopCommand(program) {
79
80
  if (isDaemonNotRunningError(errorMessage)) {
80
81
  return {
81
82
  success: false,
82
- error: STOP_MESSAGES.DAEMON_NOT_RUNNING,
83
+ error: noActiveSessionMessage(),
83
84
  exitCode: EXIT_CODES.RESOURCE_NOT_FOUND,
84
85
  errorContext: {
85
- suggestion: 'Start a session with: bdg <url>',
86
+ suggestion: startSessionSuggestion(),
86
87
  },
87
88
  };
88
89
  }
@@ -91,7 +92,7 @@ export function registerStopCommand(program) {
91
92
  error: stopFailedError(errorMessage),
92
93
  exitCode: EXIT_CODES.SOFTWARE_ERROR,
93
94
  errorContext: {
94
- suggestion: 'Try: bdg cleanup --force to reset session state',
95
+ suggestion: `Try: ${sessionCommand('bdg cleanup --force')} to reset session state`,
95
96
  },
96
97
  };
97
98
  }
@@ -2,9 +2,9 @@
2
2
  * Tail command for continuous session monitoring.
3
3
  */
4
4
  import { jsonOption, showBothSectionsWhenBothRequested } from './shared/commonOptions.js';
5
- import { handleDaemonConnectionError, noteFollowConnected, } from './shared/daemonErrorHandler.js';
5
+ import { noteFollowConnected } from './shared/daemonErrorHandler.js';
6
6
  import { fetchPreviewOutput } from './shared/dataFetcher.js';
7
- import { setupFollowMode } from './shared/followMode.js';
7
+ import { followFetchFailure, setupFollowMode, } from './shared/followMode.js';
8
8
  import { handleValidationError } from './shared/handleValidationError.js';
9
9
  import { MAX_LAST_ITEMS, positiveIntRule } from './shared/validation.js';
10
10
  import { formatPreview } from '../ui/formatters/preview.js';
@@ -59,20 +59,13 @@ export function registerTailCommand(program) {
59
59
  const showPreview = async () => {
60
60
  const result = await fetchPreviewOutput({ lastN });
61
61
  if (!result.success) {
62
- const errorResult = handleDaemonConnectionError(result.error, {
63
- json: options.json,
64
- follow: true,
65
- retryIntervalMs: interval,
66
- exitCode: result.exitCode,
67
- });
68
- if (errorResult.shouldExit)
69
- process.exit(errorResult.exitCode);
70
- return;
62
+ return followFetchFailure(result, { json: options.json, retryIntervalMs: interval });
71
63
  }
72
64
  noteFollowConnected();
73
65
  if (!options.json)
74
66
  console.clear();
75
67
  console.log(formatPreview(result.data, createPreviewOptions(options, lastN)));
68
+ return undefined;
76
69
  };
77
70
  await setupFollowMode(showPreview, {
78
71
  startMessage: followingPreviewMessage,
@@ -34,6 +34,8 @@ export interface CleanupResult {
34
34
  chrome: boolean;
35
35
  daemons: boolean;
36
36
  };
37
+ /** Directory deleted by `--purge` */
38
+ purged?: string;
37
39
  message: string;
38
40
  warnings?: string[];
39
41
  }
package/dist/commands.js CHANGED
@@ -7,6 +7,7 @@ import { registerDomCommands } from './commands/dom/index.js';
7
7
  import { registerNetworkCommands } from './commands/network/index.js';
8
8
  import { registerPageCommands } from './commands/page.js';
9
9
  import { registerPeekCommand } from './commands/peek.js';
10
+ import { registerSessionsCommand } from './commands/sessions.js';
10
11
  import { registerStartCommands } from './commands/start.js';
11
12
  import { registerStatusCommand } from './commands/status.js';
12
13
  import { registerStopCommand } from './commands/stop.js';
@@ -27,6 +28,7 @@ export const commandRegistry = [
27
28
  registerStartCommands,
28
29
  addCommandGroup('Session Management:'),
29
30
  registerStatusCommand,
31
+ registerSessionsCommand,
30
32
  registerStopCommand,
31
33
  registerCleanupCommand,
32
34
  addCommandGroup('Data Inspection:'),
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Identity of the Chrome bdg launched.
3
+ *
4
+ * chrome-launcher only waits until something answers on 127.0.0.1:<port>. That
5
+ * may be another process: when 127.0.0.1:<port> is taken, Chrome listens on
6
+ * [::1]:<port> instead and the readiness check is answered by the other one.
7
+ *
8
+ * Chrome prints `DevTools listening on ws://<host>:<port>/devtools/browser/<id>`
9
+ * to stderr (chrome-launcher writes it to `chrome-err.log` in the profile).
10
+ * The browser path is unique per browser, so comparing it with the one
11
+ * 127.0.0.1:<port> reports tells whether that is the launched Chrome.
12
+ * (Chrome's `DevToolsActivePort` file holds the same, but Chrome writes it only
13
+ * for `--remote-debugging-port=0`.)
14
+ *
15
+ * A Chrome whose output lacks the line (stderr redirected, a Chromium build
16
+ * that does not print it) is accepted with a warning when its browser answers
17
+ * on 127.0.0.1 and nothing listens on [::1]: the port was free on both right
18
+ * before the launch, and a second listener is how a conflict shows.
19
+ */
20
+ import { type StartupLogs } from './startupExit.js';
21
+ /**
22
+ * The debugging endpoint a Chrome announced for itself.
23
+ */
24
+ export interface DevToolsEndpoint {
25
+ /** Address the debugging server listens on (`127.0.0.1`, `[::1]`) */
26
+ host: string;
27
+ /** Port the debugging server listens on */
28
+ port: number;
29
+ /** Browser WebSocket path, `/devtools/browser/<id>` */
30
+ browserPath: string;
31
+ }
32
+ /**
33
+ * Find the endpoint Chrome announced in its output (the last announcement).
34
+ *
35
+ * @param lines - Chrome's output lines
36
+ * @returns Endpoint, or null if there is no complete announcement
37
+ */
38
+ export declare function parseDevToolsListening(lines: readonly string[]): DevToolsEndpoint | null;
39
+ /**
40
+ * Wait for Chrome to announce its endpoint.
41
+ *
42
+ * @param logs - Chrome's log positions from before the launch
43
+ * @param isRunning - Whether Chrome is still running (waiting stops when not)
44
+ * @param timeoutMs - Longest wait
45
+ * @returns Endpoint, or null if Chrome exited or announced none in time
46
+ */
47
+ export declare function waitForDevToolsEndpoint(logs: StartupLogs, isRunning: () => boolean, timeoutMs?: number): Promise<DevToolsEndpoint | null>;
48
+ /**
49
+ * Check that the Chrome answering on 127.0.0.1:<port> is the one just
50
+ * launched, so bdg never drives another session's browser.
51
+ *
52
+ * @param options - Chrome's log positions from before the launch, requested
53
+ * port (free on 127.0.0.1 and ::1 right before the launch), Chrome's PID and
54
+ * longest wait for its announcement
55
+ * @throws ChromeLaunchError: CHROME_DIED_AFTER_LAUNCH if Chrome exits first;
56
+ * PORT_IN_USE if another browser or process answers on the port;
57
+ * CHROME_LAUNCH_FAILED if Chrome announced nothing and nothing answers
58
+ */
59
+ export declare function verifyLaunchedChrome(options: {
60
+ logs: StartupLogs;
61
+ port: number;
62
+ pid: number;
63
+ timeoutMs?: number;
64
+ }): Promise<void>;
65
+ //# sourceMappingURL=chromeIdentity.d.ts.map
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Identity of the Chrome bdg launched.
3
+ *
4
+ * chrome-launcher only waits until something answers on 127.0.0.1:<port>. That
5
+ * may be another process: when 127.0.0.1:<port> is taken, Chrome listens on
6
+ * [::1]:<port> instead and the readiness check is answered by the other one.
7
+ *
8
+ * Chrome prints `DevTools listening on ws://<host>:<port>/devtools/browser/<id>`
9
+ * to stderr (chrome-launcher writes it to `chrome-err.log` in the profile).
10
+ * The browser path is unique per browser, so comparing it with the one
11
+ * 127.0.0.1:<port> reports tells whether that is the launched Chrome.
12
+ * (Chrome's `DevToolsActivePort` file holds the same, but Chrome writes it only
13
+ * for `--remote-debugging-port=0`.)
14
+ *
15
+ * A Chrome whose output lacks the line (stderr redirected, a Chromium build
16
+ * that does not print it) is accepted with a warning when its browser answers
17
+ * on 127.0.0.1 and nothing listens on [::1]: the port was free on both right
18
+ * before the launch, and a second listener is how a conflict shows.
19
+ */
20
+ import { createLogger } from '../ui/logging/index.js';
21
+ import { delay } from '../utils/async.js';
22
+ import { fetchBrowserWsUrl } from '../utils/http.js';
23
+ import { isProcessAlive } from '../utils/process.js';
24
+ import { ChromeLaunchError } from './errors.js';
25
+ import { acceptsConnections } from './portReservation.js';
26
+ import { readStartupLines } from './startupExit.js';
27
+ /** Chrome's stderr line announcing its debugging endpoint */
28
+ const LISTENING_PATTERN = /^DevTools listening on (ws:\/\/\S+)/;
29
+ /** How long to wait for Chrome to announce its endpoint after the port answered */
30
+ const ENDPOINT_WAIT_MS = 10000;
31
+ const ENDPOINT_POLL_MS = 50;
32
+ const log = createLogger('chrome');
33
+ /**
34
+ * Find the endpoint Chrome announced in its output (the last announcement).
35
+ *
36
+ * @param lines - Chrome's output lines
37
+ * @returns Endpoint, or null if there is no complete announcement
38
+ */
39
+ export function parseDevToolsListening(lines) {
40
+ for (const line of [...lines].reverse()) {
41
+ const match = LISTENING_PATTERN.exec(line.trim());
42
+ if (!match?.[1])
43
+ continue;
44
+ try {
45
+ const url = new URL(match[1]);
46
+ const port = Number(url.port);
47
+ if (!port || !url.pathname.startsWith('/devtools/browser/'))
48
+ continue;
49
+ return { host: url.hostname, port, browserPath: url.pathname };
50
+ }
51
+ catch (error) {
52
+ log.debug(`Ignoring malformed DevTools announcement: ${String(error)}`);
53
+ }
54
+ }
55
+ return null;
56
+ }
57
+ /**
58
+ * Wait for Chrome to announce its endpoint.
59
+ *
60
+ * @param logs - Chrome's log positions from before the launch
61
+ * @param isRunning - Whether Chrome is still running (waiting stops when not)
62
+ * @param timeoutMs - Longest wait
63
+ * @returns Endpoint, or null if Chrome exited or announced none in time
64
+ */
65
+ export async function waitForDevToolsEndpoint(logs, isRunning, timeoutMs = ENDPOINT_WAIT_MS) {
66
+ const deadline = Date.now() + timeoutMs;
67
+ for (;;) {
68
+ const endpoint = parseDevToolsListening(readStartupLines(logs));
69
+ if (endpoint || !isRunning() || Date.now() >= deadline)
70
+ return endpoint;
71
+ await delay(ENDPOINT_POLL_MS);
72
+ }
73
+ }
74
+ /**
75
+ * Check that the Chrome answering on 127.0.0.1:<port> is the one just
76
+ * launched, so bdg never drives another session's browser.
77
+ *
78
+ * @param options - Chrome's log positions from before the launch, requested
79
+ * port (free on 127.0.0.1 and ::1 right before the launch), Chrome's PID and
80
+ * longest wait for its announcement
81
+ * @throws ChromeLaunchError: CHROME_DIED_AFTER_LAUNCH if Chrome exits first;
82
+ * PORT_IN_USE if another browser or process answers on the port;
83
+ * CHROME_LAUNCH_FAILED if Chrome announced nothing and nothing answers
84
+ */
85
+ export async function verifyLaunchedChrome(options) {
86
+ const { logs, port, pid, timeoutMs } = options;
87
+ const isRunning = () => isProcessAlive(pid);
88
+ const endpoint = await waitForDevToolsEndpoint(logs, isRunning, timeoutMs);
89
+ if (!endpoint && !isRunning()) {
90
+ throw new ChromeLaunchError(`Chrome died immediately after launch (PID: ${pid})`, {
91
+ issue: { code: 'CHROME_DIED_AFTER_LAUNCH', context: { port, pid } },
92
+ });
93
+ }
94
+ if (!endpoint)
95
+ return acceptUnannouncedChrome(logs, port);
96
+ if (endpoint.port !== port) {
97
+ throw portTakenError(port, `Chrome listens on port ${endpoint.port} instead`);
98
+ }
99
+ const answering = await fetchBrowserWsUrl(port, log);
100
+ if (!answering || new URL(answering).pathname !== endpoint.browserPath) {
101
+ throw portTakenError(port, `another process answers on 127.0.0.1 (Chrome listens on ${endpoint.host})`);
102
+ }
103
+ log.debug(`Chrome on port ${port} is the launched one (${endpoint.browserPath})`);
104
+ }
105
+ /**
106
+ * Accept a Chrome that did not announce its endpoint, if a browser answers on
107
+ * 127.0.0.1:<port> and nothing listens on [::1]:<port> (two listeners mean a
108
+ * conflict).
109
+ *
110
+ * @param logs - Chrome's log positions (the stderr log is named in messages)
111
+ * @param port - Requested port
112
+ * @throws ChromeLaunchError: PORT_IN_USE for a second listener;
113
+ * CHROME_LAUNCH_FAILED if no browser answers
114
+ */
115
+ async function acceptUnannouncedChrome(logs, port) {
116
+ const errLog = logs.files[0]?.file ?? 'chrome-err.log';
117
+ const missing = `Chrome did not print "DevTools listening on" to ${errLog}`;
118
+ if (!(await fetchBrowserWsUrl(port, log))) {
119
+ throw new ChromeLaunchError(`${missing}, and no browser answers on port ${port}`, {
120
+ issue: {
121
+ code: 'CHROME_LAUNCH_FAILED',
122
+ context: { port, reason: `${missing}, and no browser answers on port ${port}` },
123
+ },
124
+ });
125
+ }
126
+ if (await acceptsConnections(port, '::1')) {
127
+ throw portTakenError(port, `${missing}, and both 127.0.0.1 and [::1] answer`);
128
+ }
129
+ log.info(`Warning: ${missing}; using the browser on 127.0.0.1:${port}, which was free before the launch`);
130
+ }
131
+ /**
132
+ * The error for a port another process answers on.
133
+ *
134
+ * @param port - Requested port
135
+ * @param reason - What was found
136
+ * @returns Launch error with the PORT_IN_USE issue
137
+ */
138
+ function portTakenError(port, reason) {
139
+ return new ChromeLaunchError(`Port ${port} is already in use: ${reason}`, {
140
+ issue: { code: 'PORT_IN_USE', context: { port, reason } },
141
+ });
142
+ }
143
+ //# sourceMappingURL=chromeIdentity.js.map