browser-debugger-cli 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/.claude/skills/bdg/SKILL.md +4 -4
  2. package/README.md +143 -79
  3. package/dist/commands/cdp.d.ts +22 -1
  4. package/dist/commands/cdp.js +100 -43
  5. package/dist/commands/console.d.ts +12 -0
  6. package/dist/commands/console.js +62 -12
  7. package/dist/commands/css.d.ts +13 -0
  8. package/dist/commands/css.js +53 -0
  9. package/dist/commands/dom/DomElementResolver.d.ts +3 -1
  10. package/dist/commands/dom/DomElementResolver.js +10 -3
  11. package/dist/commands/dom/a11y.js +3 -2
  12. package/dist/commands/dom/audit.d.ts +14 -0
  13. package/dist/commands/dom/audit.js +87 -0
  14. package/dist/commands/dom/eval.d.ts +3 -2
  15. package/dist/commands/dom/eval.js +11 -5
  16. package/dist/commands/dom/form.js +10 -9
  17. package/dist/commands/dom/formInteraction.js +42 -11
  18. package/dist/commands/dom/get.js +8 -8
  19. package/dist/commands/dom/helpers/index.d.ts +1 -1
  20. package/dist/commands/dom/helpers/index.js +1 -1
  21. package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
  22. package/dist/commands/dom/helpers/keyAttributes.js +6 -4
  23. package/dist/commands/dom/helpers/query.d.ts +27 -3
  24. package/dist/commands/dom/helpers/query.js +152 -64
  25. package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
  26. package/dist/commands/dom/helpers/screenshot.js +169 -49
  27. package/dist/commands/dom/index.js +7 -2
  28. package/dist/commands/dom/query.d.ts +19 -2
  29. package/dist/commands/dom/query.js +37 -6
  30. package/dist/commands/dom/screenshot.js +12 -7
  31. package/dist/commands/dom/semanticUtils.d.ts +3 -2
  32. package/dist/commands/dom/semanticUtils.js +40 -9
  33. package/dist/commands/dom/wait.js +5 -3
  34. package/dist/commands/helpJson.d.ts +82 -19
  35. package/dist/commands/helpJson.js +112 -41
  36. package/dist/commands/helpTopic.d.ts +16 -1
  37. package/dist/commands/helpTopic.js +59 -1
  38. package/dist/commands/installSkill.d.ts +15 -5
  39. package/dist/commands/installSkill.js +86 -16
  40. package/dist/commands/network/list.js +22 -12
  41. package/dist/commands/optionBehaviors.js +53 -16
  42. package/dist/commands/page.js +7 -4
  43. package/dist/commands/peek.d.ts +7 -0
  44. package/dist/commands/peek.js +65 -23
  45. package/dist/commands/shared/daemonErrorHandler.d.ts +5 -2
  46. package/dist/commands/shared/daemonErrorHandler.js +20 -9
  47. package/dist/commands/shared/dataFetcher.d.ts +12 -4
  48. package/dist/commands/shared/dataFetcher.js +12 -4
  49. package/dist/commands/shared/followMode.d.ts +9 -1
  50. package/dist/commands/shared/followMode.js +22 -4
  51. package/dist/commands/shared/optionTypes.d.ts +9 -2
  52. package/dist/commands/shared/outputFile.js +6 -1
  53. package/dist/commands/start.d.ts +20 -5
  54. package/dist/commands/start.js +84 -23
  55. package/dist/commands/stop.d.ts +11 -0
  56. package/dist/commands/stop.js +24 -1
  57. package/dist/commands/tail.d.ts +7 -1
  58. package/dist/commands/tail.js +13 -62
  59. package/dist/commands.js +2 -0
  60. package/dist/connection/cdp.d.ts +7 -0
  61. package/dist/connection/cdp.js +9 -0
  62. package/dist/connection/launcher.js +3 -2
  63. package/dist/daemon/SessionController.js +6 -1
  64. package/dist/daemon/launcher.d.ts +3 -2
  65. package/dist/daemon/launcher.js +47 -3
  66. package/dist/daemon/session/Session.d.ts +4 -1
  67. package/dist/daemon/session/Session.js +33 -2
  68. package/dist/daemon/session/TelemetryStore.d.ts +8 -1
  69. package/dist/daemon/session/TelemetryStore.js +13 -1
  70. package/dist/daemon/session/commandRegistry.js +36 -14
  71. package/dist/daemon/session/interactions.d.ts +2 -1
  72. package/dist/daemon/session/interactions.js +13 -1
  73. package/dist/daemon/session/plugins.js +19 -53
  74. package/dist/daemon/session/teardown.js +1 -1
  75. package/dist/daemon.js +9234 -7222
  76. package/dist/errors/messages.d.ts +88 -15
  77. package/dist/errors/messages.js +177 -27
  78. package/dist/index.js +19322 -13961
  79. package/dist/ipc/client.d.ts +22 -2
  80. package/dist/ipc/client.js +34 -5
  81. package/dist/ipc/protocol/auditTypes.d.ts +135 -0
  82. package/dist/ipc/protocol/auditTypes.js +6 -0
  83. package/dist/ipc/protocol/commands.d.ts +35 -0
  84. package/dist/ipc/protocol/commands.js +2 -0
  85. package/dist/ipc/protocol/domTypes.d.ts +16 -0
  86. package/dist/ipc/protocol/inspectTypes.d.ts +73 -8
  87. package/dist/ipc/session/types.d.ts +2 -0
  88. package/dist/runtime/css/search.d.ts +39 -0
  89. package/dist/runtime/css/search.js +122 -0
  90. package/dist/runtime/dom/actionEffects.d.ts +9 -2
  91. package/dist/runtime/dom/actionEffects.js +30 -14
  92. package/dist/runtime/dom/audit.d.ts +19 -0
  93. package/dist/runtime/dom/audit.js +37 -0
  94. package/dist/runtime/dom/auditModel.d.ts +45 -0
  95. package/dist/runtime/dom/auditModel.js +220 -0
  96. package/dist/runtime/dom/auditScripts.d.ts +113 -0
  97. package/dist/runtime/dom/auditScripts.js +148 -0
  98. package/dist/runtime/dom/elementGeometry.d.ts +16 -3
  99. package/dist/runtime/dom/elementGeometry.js +49 -10
  100. package/dist/runtime/dom/elementInfo.d.ts +74 -17
  101. package/dist/runtime/dom/elementInfo.js +187 -34
  102. package/dist/runtime/dom/evalHelpers.d.ts +12 -2
  103. package/dist/runtime/dom/evalHelpers.js +67 -7
  104. package/dist/runtime/dom/formDiscovery.d.ts +6 -2
  105. package/dist/runtime/dom/formDiscovery.js +20 -3
  106. package/dist/runtime/dom/formFillHelpers/fill.js +8 -12
  107. package/dist/runtime/dom/formFillHelpers/pressKey.js +2 -2
  108. package/dist/runtime/dom/formFillHelpers/shared.d.ts +16 -10
  109. package/dist/runtime/dom/formFillHelpers/shared.js +19 -52
  110. package/dist/runtime/dom/formSubmitHelpers.js +4 -3
  111. package/dist/runtime/dom/frameLayout.js +1 -0
  112. package/dist/runtime/dom/inspect.d.ts +7 -0
  113. package/dist/runtime/dom/inspect.js +92 -28
  114. package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
  115. package/dist/runtime/dom/inspectAllStyles.js +90 -7
  116. package/dist/runtime/dom/inspectCascade.d.ts +19 -2
  117. package/dist/runtime/dom/inspectCascade.js +214 -44
  118. package/dist/runtime/dom/inspectCascadeModel.d.ts +8 -0
  119. package/dist/runtime/dom/inspectCascadeModel.js +108 -34
  120. package/dist/runtime/dom/inspectHints.d.ts +26 -3
  121. package/dist/runtime/dom/inspectHints.js +125 -9
  122. package/dist/runtime/dom/inspectModel.d.ts +5 -1
  123. package/dist/runtime/dom/inspectModel.js +37 -10
  124. package/dist/runtime/dom/inspectPaintModel.d.ts +50 -22
  125. package/dist/runtime/dom/inspectPaintModel.js +182 -68
  126. package/dist/runtime/dom/inspectRules.d.ts +19 -0
  127. package/dist/runtime/dom/inspectRules.js +21 -5
  128. package/dist/runtime/dom/inspectScripts.d.ts +112 -12
  129. package/dist/runtime/dom/inspectScripts.js +357 -32
  130. package/dist/runtime/dom/inspectTree.js +10 -2
  131. package/dist/runtime/dom/inspectWhyModel.d.ts +2 -1
  132. package/dist/runtime/dom/inspectWhyModel.js +52 -10
  133. package/dist/runtime/dom/layout.js +40 -16
  134. package/dist/runtime/dom/reactEventHelpers.d.ts +21 -4
  135. package/dist/runtime/dom/reactEventHelpers.js +90 -36
  136. package/dist/runtime/dom/targetNode.d.ts +18 -5
  137. package/dist/runtime/dom/targetNode.js +268 -8
  138. package/dist/runtime/dom/wait.js +2 -1
  139. package/dist/runtime/page/bdgWorld.d.ts +57 -0
  140. package/dist/runtime/page/bdgWorld.js +180 -0
  141. package/dist/runtime/page/emulation.d.ts +13 -4
  142. package/dist/runtime/page/emulation.js +69 -4
  143. package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
  144. package/dist/runtime/page/replacedBuiltins.js +136 -0
  145. package/dist/runtime/page/userAgent.d.ts +17 -0
  146. package/dist/runtime/page/userAgent.js +57 -0
  147. package/dist/session/QueryCacheManager.d.ts +4 -1
  148. package/dist/session/QueryCacheManager.js +5 -2
  149. package/dist/session/chrome.d.ts +4 -1
  150. package/dist/session/chrome.js +7 -1
  151. package/dist/session/cleanup/staleSession.d.ts +21 -4
  152. package/dist/session/cleanup/staleSession.js +79 -9
  153. package/dist/session/cleanup/userCommands.d.ts +4 -1
  154. package/dist/session/cleanup/userCommands.js +10 -5
  155. package/dist/session/daemonSocket.d.ts +10 -0
  156. package/dist/session/daemonSocket.js +22 -0
  157. package/dist/session/lastSession.d.ts +6 -3
  158. package/dist/session/lastSession.js +11 -5
  159. package/dist/session/paths.d.ts +3 -1
  160. package/dist/session/paths.js +5 -5
  161. package/dist/session/portClaims.js +4 -3
  162. package/dist/session/sessionList.d.ts +13 -5
  163. package/dist/session/sessionList.js +31 -7
  164. package/dist/telemetry/a11y.js +2 -2
  165. package/dist/telemetry/console.d.ts +2 -1
  166. package/dist/telemetry/console.js +30 -21
  167. package/dist/telemetry/pageCrash.d.ts +26 -0
  168. package/dist/telemetry/pageCrash.js +53 -0
  169. package/dist/types.d.ts +20 -0
  170. package/dist/ui/formatters/audit.d.ts +19 -0
  171. package/dist/ui/formatters/audit.js +115 -0
  172. package/dist/ui/formatters/cdp.d.ts +138 -0
  173. package/dist/ui/formatters/cdp.js +131 -0
  174. package/dist/ui/formatters/console/chronological.js +3 -1
  175. package/dist/ui/formatters/console/follow.d.ts +2 -1
  176. package/dist/ui/formatters/console/follow.js +2 -2
  177. package/dist/ui/formatters/console/json.d.ts +2 -2
  178. package/dist/ui/formatters/console/json.js +11 -5
  179. package/dist/ui/formatters/console/shared.d.ts +30 -0
  180. package/dist/ui/formatters/console/shared.js +16 -0
  181. package/dist/ui/formatters/console/summarize.d.ts +9 -2
  182. package/dist/ui/formatters/console/summarize.js +40 -9
  183. package/dist/ui/formatters/console.d.ts +2 -1
  184. package/dist/ui/formatters/console.js +7 -5
  185. package/dist/ui/formatters/details.js +3 -1
  186. package/dist/ui/formatters/dom.d.ts +2 -2
  187. package/dist/ui/formatters/dom.js +10 -8
  188. package/dist/ui/formatters/helpFormatters.js +1 -1
  189. package/dist/ui/formatters/inspect.js +50 -17
  190. package/dist/ui/formatters/installSkill.d.ts +9 -1
  191. package/dist/ui/formatters/installSkill.js +32 -6
  192. package/dist/ui/formatters/layout.js +2 -1
  193. package/dist/ui/formatters/networkList.d.ts +1 -1
  194. package/dist/ui/formatters/networkList.js +1 -2
  195. package/dist/ui/formatters/preview.d.ts +2 -0
  196. package/dist/ui/formatters/preview.js +17 -7
  197. package/dist/ui/formatters/sessions.d.ts +2 -2
  198. package/dist/ui/formatters/sessions.js +9 -2
  199. package/dist/ui/formatters/status.js +1 -1
  200. package/dist/ui/logging/logger.d.ts +1 -1
  201. package/dist/ui/messages/commands.d.ts +168 -11
  202. package/dist/ui/messages/commands.js +245 -18
  203. package/dist/ui/messages/consoleMessages.d.ts +24 -0
  204. package/dist/ui/messages/consoleMessages.js +32 -0
  205. package/dist/ui/messages/preview.d.ts +12 -0
  206. package/dist/ui/messages/preview.js +18 -2
  207. package/dist/ui/messages/session.d.ts +13 -2
  208. package/dist/ui/messages/session.js +22 -3
  209. package/dist/utils/cssValues.js +36 -4
  210. package/dist/utils/decisionTrees.js +0 -5
  211. package/dist/utils/directories.d.ts +34 -0
  212. package/dist/utils/directories.js +88 -0
  213. package/dist/utils/display.d.ts +16 -0
  214. package/dist/utils/display.js +42 -0
  215. package/dist/utils/exitCodes.d.ts +1 -0
  216. package/dist/utils/exitCodes.js +6 -0
  217. package/dist/utils/process.d.ts +12 -0
  218. package/dist/utils/process.js +25 -0
  219. package/dist/utils/suggestions.d.ts +4 -2
  220. package/dist/utils/suggestions.js +7 -5
  221. package/dist/utils/taskMappings.js +1 -1
  222. package/package.json +3 -2
@@ -16,7 +16,7 @@
16
16
  */
17
17
  import { containerKind } from '../../runtime/dom/inspectLayoutModel.js';
18
18
  import { joinLines } from '../formatting.js';
19
- import { inspectCascadeNote, inspectAnimatingBadge, inspectMidTransitionNote, inspectDarkThemeBadge, inspectedMatchAction, inspectVisibilityBadges, multipleMatchesWarning, } from '../messages/commands.js';
19
+ import { inspectCascadeNote, inspectAnimatingBadge, inspectMidTransitionNote, inspectDarkThemeBadge, inspectPseudoOfNote, inspectedMatchAction, inspectVisibilityBadges, multipleMatchesWarning, } from '../messages/commands.js';
20
20
  import { shadowText, sidesShorthand } from '../../utils/cssValues.js';
21
21
  import { truncateByLength } from '../../utils/strings.js';
22
22
  /** Longest parent label on the parent line (CSS-module class names run long) */
@@ -50,11 +50,12 @@ export function inspectHeader(data) {
50
50
  data.element,
51
51
  data.content !== undefined && `"${data.content}"`,
52
52
  data.placeholder !== undefined && `placeholder "${data.placeholder}"`,
53
- data.rect && `${data.rect.w}x${data.rect.h} @${data.rect.x},${data.rect.y}`,
53
+ data.rect &&
54
+ `${data.rect.w}x${data.rect.h}${data.rect.screen ? ` (${data.rect.screen.w}x${data.rect.screen.h} on screen)` : ''} @${data.rect.x},${data.rect.y}${data.rect.in ? ' (fixed: viewport position)' : ''}`,
54
55
  kind && `[${kind}]`,
55
56
  ...inspectVisibilityBadges(data.visibility),
56
57
  data.context && `in ${data.context}`,
57
- data.theme === 'dark' && inspectDarkThemeBadge(),
58
+ data.theme === 'dark' && inspectDarkThemeBadge(data.themeFrom === 'emulation'),
58
59
  data.animating && inspectAnimatingBadge(data.animating),
59
60
  ]
60
61
  .filter(Boolean)
@@ -177,9 +178,9 @@ function parentLine(layout) {
177
178
  ]);
178
179
  }
179
180
  /**
180
- * Font, weight and size as one phrase, e.g. `Inter (webfont) 600 italic 16/24`
181
- * or `Inter (rendered "Helvetica") 400 14/normal` when the text was drawn in
182
- * another font (a fallback, or the face's own name); for a container only
181
+ * Font, weight and size as one phrase, e.g. `Inter (webfont) 600 italic 16/24`,
182
+ * `Inter (rendered "Helvetica") 400 14/normal` when the text was drawn in a
183
+ * fallback, or `sans-serif (resolves to "Helvetica")`; for a container only
183
184
  * the fields it has.
184
185
  *
185
186
  * @param text - Text group
@@ -188,6 +189,7 @@ function parentLine(layout) {
188
189
  function fontParts(text) {
189
190
  const loaded = [
190
191
  text.rendered && `rendered "${text.rendered}"`,
192
+ text.resolved && `resolves to "${text.resolved}"`,
191
193
  text.webfont && 'webfont loaded',
192
194
  ].filter(Boolean);
193
195
  const family = text.family && [text.family, loaded.length > 0 && `(${loaded.join(', ')})`];
@@ -210,6 +212,9 @@ function fontParts(text) {
210
212
  }
211
213
  /**
212
214
  * A contrast as words, e.g. `contrast 4.47 fail on #fff (faded: opacity 0.4)`.
215
+ * An approximate one gets no pass/fail level, only the estimate and why
216
+ * (`contrast ≈1 on #fff (approximate: img.hero behind)`): what is behind
217
+ * the text is not a known color.
213
218
  *
214
219
  * @param contrast - Contrast
215
220
  * @returns Words, or undefined
@@ -218,10 +223,12 @@ function contrastText(contrast) {
218
223
  if (!contrast)
219
224
  return undefined;
220
225
  return [
221
- `contrast ${contrast.ratio} ${contrast.level}`,
222
- contrast.inherited && `on ${contrast.background}`,
223
- contrast.overImage && '(over image)',
226
+ contrast.approximate
227
+ ? `contrast ≈${contrast.ratio}`
228
+ : `contrast ${contrast.ratio} ${contrast.level}`,
229
+ `on ${contrast.background}`,
224
230
  contrast.opacity !== undefined && `(faded: opacity ${contrast.opacity})`,
231
+ contrast.approximate && `(approximate: ${contrast.approximate.join(', ')})`,
225
232
  ]
226
233
  .filter(Boolean)
227
234
  .join(' ');
@@ -234,6 +241,7 @@ function contrastText(contrast) {
234
241
  */
235
242
  function textLine(text) {
236
243
  return groupLine('text', [
244
+ text.holder && `in ${text.holder}`,
237
245
  ...fontParts(text),
238
246
  text.color && `color ${text.color}`,
239
247
  contrastText(text.contrast),
@@ -244,6 +252,8 @@ function textLine(text) {
244
252
  text.whiteSpace && `ws ${text.whiteSpace}`,
245
253
  text.overflow && `text-overflow ${text.overflow}`,
246
254
  text.clamp && `clamp ${text.clamp}`,
255
+ text.truncated && 'truncated',
256
+ text.gradientFill && 'filled by its background (background-clip: text; no contrast)',
247
257
  text.shadow && `shadow ${text.shadow}`,
248
258
  text.features && `features ${text.features}`,
249
259
  ]);
@@ -257,19 +267,31 @@ function textLine(text) {
257
267
  function fillText(fill) {
258
268
  if (fill.type === 'solid')
259
269
  return `bg ${fill.color}`;
260
- if (fill.type === 'gradient')
261
- return `bg-image ${fill.value}`;
262
- return `bg-image ${fill.value}${fill.size ? ` size ${fill.size}` : ''}`;
270
+ return [
271
+ `bg-image ${fill.value}`,
272
+ fill.size && `size ${fill.size}`,
273
+ fill.position && `at ${fill.position}`,
274
+ ]
275
+ .filter(Boolean)
276
+ .join(' ');
263
277
  }
264
278
  /**
265
- * The fill line: backgrounds, opacity and blend mode.
279
+ * The fill line: backgrounds (and that they are clipped to gradient text), opacity and blend mode.
266
280
  *
267
281
  * @param data - Inspect result
268
282
  * @returns Line
269
283
  */
270
284
  function fillLine(data) {
285
+ const paint = data.paint;
271
286
  return groupLine('fill', [
287
+ paint && `fill ${paint.fill}`,
288
+ paint &&
289
+ `stroke ${paint.stroke}${paint.strokeWidth !== undefined ? ` ${paint.strokeWidth}` : ''}`,
272
290
  ...(data.fills ?? []).map(fillText),
291
+ data.text?.gradientFill &&
292
+ !data.text.holder &&
293
+ (data.fills ?? []).length > 0 &&
294
+ 'clipped to the text',
273
295
  data.opacity !== undefined && `opacity ${data.opacity}`,
274
296
  data.blend && `blend ${data.blend}`,
275
297
  ]);
@@ -337,6 +359,7 @@ function pseudoText(pseudo) {
337
359
  pseudo.content !== undefined && `content ${pseudo.content}`,
338
360
  pseudo.display,
339
361
  pseudo.position,
362
+ pseudo.inset && `inset ${pseudo.inset}`,
340
363
  pseudo.size && `${pseudo.size.w}x${pseudo.size.h}`,
341
364
  pseudo.color && `color ${pseudo.color}`,
342
365
  pseudo.fontStyle,
@@ -370,7 +393,9 @@ function treeRow(node) {
370
393
  return [
371
394
  node.element,
372
395
  node.count !== undefined && `×${node.count}`,
373
- `${node.w}x${node.h}`,
396
+ node.contents ? '(contents)' : `${node.w}x${node.h}`,
397
+ node.shadow && '(shadow root)',
398
+ node.via && `via ${node.via}`,
374
399
  node.layout && `[${node.layout}]`,
375
400
  node.text && `"${node.text}"`,
376
401
  node.childCount !== undefined && `(${node.childCount})`,
@@ -422,7 +447,7 @@ function ruleScope(entry) {
422
447
  * @returns Lines
423
448
  */
424
449
  function cascadeBlock(data) {
425
- const hints = (data.hints ?? []).map((hint) => `${hint.property}: ${hint.value} ${hint.kind === 'not-inherited' ? "is the browser's" : 'has no effect'}: ${hint.reason} → ${hint.fix} · in ${hint.source}`);
450
+ const hints = (data.hints ?? []).map((hint) => `${hint.property}: ${hint.value} ${hint.kind === 'not-inherited' ? "is the browser's" : hint.only ? `has no effect on ${hint.only.join(', ')}` : 'has no effect'}: ${hint.reason} → ${hint.fix} · in ${hint.source}`);
426
451
  return [
427
452
  ...labelledLines('hints', data.hints?.length === 0 ? ['none'] : hints),
428
453
  ...labelledLines('rules', (data.rules ?? []).map(ruleLine)),
@@ -472,14 +497,19 @@ function whyLines(why, data) {
472
497
  entry.important ? ' !important' : '',
473
498
  ` ${entry.source}${entry.specificity ? ` [${entry.specificity.join(',')}]` : ''}${ruleScope(entry)}`,
474
499
  entry.status === 'inherited' ? ' (inherited)' : '',
500
+ entry.note ? ` (${entry.note})` : '',
475
501
  ].join('');
476
502
  });
477
503
  const variables = (why.variables ?? []).map((variable) => ` ${variable.name}: ${truncateByLength(variable.value, CASCADE_VALUE_WIDTH)} ${variable.source}${variable.inherited !== undefined ? ` (inherited from ${levelsUp(variable.inherited)})` : ''}`);
478
504
  return [
479
505
  `${why.property} = ${why.computed}${midTransition(why.property, data.animating) ? ` ${inspectMidTransitionNote()}` : ''}`,
480
506
  ...entries.slice(0, 1),
507
+ ...(why.chain[0]?.rule
508
+ ? [` in ${truncateByLength(why.chain[0].rule, RULE_LINE_WIDTH)}`]
509
+ : []),
481
510
  ...variables,
482
511
  ...entries.slice(1),
512
+ ...(why.inactive ?? []).map((rule) => ` - ${truncateByLength(rule.value, CASCADE_VALUE_WIDTH)} ${rule.selector} ${rule.condition} (does not apply now)`),
483
513
  ...(why.chain.length === 0 ? [' no author declaration: the default or inherited value'] : []),
484
514
  ];
485
515
  }
@@ -505,6 +535,8 @@ function midTransition(property, animating) {
505
535
  function levelsUp(levels) {
506
536
  return levels === 1 ? 'the parent' : `${levels} levels up`;
507
537
  }
538
+ /** Longest rule text shown under the winner in `--why` (JSON has up to 300 characters) */
539
+ const RULE_LINE_WIDTH = 120;
508
540
  /** Longest declared value shown in `--rules` and `--why` lines (font stacks run long) */
509
541
  const CASCADE_VALUE_WIDTH = 60;
510
542
  /**
@@ -589,8 +621,9 @@ export function formatInspect(data) {
589
621
  const note = data.picked
590
622
  ? multipleMatchesWarning(data.count, inspectedMatchAction(data.picked, data.index))
591
623
  : undefined;
624
+ const pseudoNote = data.pseudoOf && inspectPseudoOfNote(data.pseudoOf);
592
625
  if (data.why)
593
- return joinLines(inspectHeader(data), ...cascadeBlock(data), note);
594
- return joinLines(inspectHeader(data), ...body, ...cascadeBlock(data), ...treeBlock(data), note);
626
+ return joinLines(inspectHeader(data), ...cascadeBlock(data), note, pseudoNote);
627
+ return joinLines(inspectHeader(data), ...body, ...cascadeBlock(data), ...treeBlock(data), note, pseudoNote);
595
628
  }
596
629
  //# sourceMappingURL=inspect.js.map
@@ -1,6 +1,14 @@
1
1
  import type { InstalledSkill } from '../../types.js';
2
2
  /**
3
- * Format where the skill was installed, one line per agent.
3
+ * Format the targets the skill was written for, as a failed install lists
4
+ * the ones that succeeded.
5
+ *
6
+ * @param skills - Install results
7
+ * @returns Human-readable lines
8
+ */
9
+ export declare function formatSkillTargets(skills: InstalledSkill[]): string;
10
+ /**
11
+ * Format where the skill was installed, plus what to do next.
4
12
  *
5
13
  * @param data - Install results
6
14
  * @returns Human-readable summary
@@ -1,5 +1,8 @@
1
1
  import { homedir } from 'os';
2
2
  import { OutputFormatter } from '../formatting.js';
3
+ import { skillBackupMessage } from '../messages/commands.js';
4
+ /** Indent of the path column, so a backup line sits under the path it belongs to */
5
+ const PATH_INDENT = ' '.repeat(2 + 6 + 2 + 9 + 2);
3
6
  /**
4
7
  * Shorten a path under the home directory to `~/...`.
5
8
  *
@@ -11,17 +14,40 @@ function tildePath(path) {
11
14
  return path.startsWith(`${home}/`) ? `~${path.slice(home.length)}` : path;
12
15
  }
13
16
  /**
14
- * Format where the skill was installed, one line per agent.
17
+ * Add where the skill was installed, one line per agent, plus where a
18
+ * replaced copy was kept.
19
+ *
20
+ * @param fmt - Formatter to add the lines to
21
+ * @param skills - Install results
22
+ * @returns The formatter
23
+ */
24
+ function addSkillTargets(fmt, skills) {
25
+ fmt.text('bdg skill:');
26
+ for (const skill of skills) {
27
+ fmt.text(` ${skill.target.padEnd(6)} ${skill.status.padEnd(9)} ${tildePath(skill.path)}`);
28
+ if (skill.backup)
29
+ fmt.text(`${PATH_INDENT}${skillBackupMessage(tildePath(skill.backup))}`);
30
+ }
31
+ return fmt;
32
+ }
33
+ /**
34
+ * Format the targets the skill was written for, as a failed install lists
35
+ * the ones that succeeded.
36
+ *
37
+ * @param skills - Install results
38
+ * @returns Human-readable lines
39
+ */
40
+ export function formatSkillTargets(skills) {
41
+ return addSkillTargets(new OutputFormatter(), skills).build();
42
+ }
43
+ /**
44
+ * Format where the skill was installed, plus what to do next.
15
45
  *
16
46
  * @param data - Install results
17
47
  * @returns Human-readable summary
18
48
  */
19
49
  export function formatInstalledSkills(data) {
20
- const fmt = new OutputFormatter().text('bdg skill:');
21
- for (const skill of data.skills) {
22
- fmt.text(` ${skill.target.padEnd(6)} ${skill.status.padEnd(9)} ${tildePath(skill.path)}`);
23
- }
24
- return fmt
50
+ return addSkillTargets(new OutputFormatter(), data.skills)
25
51
  .hints('Next:', [
26
52
  'Start a new agent session to load it (running sessions keep the old list)',
27
53
  'After upgrading bdg, run bdg install-skill again',
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { indexSourceText } from '../../errors/messages.js';
5
5
  import { OutputFormatter } from '../formatting.js';
6
- import { coverText, indexLayoutHeadline, layoutHeadline, layoutPositionLabel, moreMatchesNote, pageLayoutLine, } from '../messages/commands.js';
6
+ import { coverText, indexLayoutHeadline, layoutHeadline, layoutPositionLabel, maskedText, moreMatchesNote, pageLayoutLine, } from '../messages/commands.js';
7
7
  /** Elements listed in human output (JSON has up to 100) */
8
8
  const LAYOUT_DISPLAY_LIMIT = 20;
9
9
  /**
@@ -26,6 +26,7 @@ export function layoutLine(element, viewport) {
26
26
  element.coveredBy && coverText(element.coveredBy, element.coverTransparent),
27
27
  element.inert && 'inert',
28
28
  element.invisible,
29
+ element.masked && maskedText(element.masked),
29
30
  element.context && element.context !== element.clippedBy && `in ${element.context}`,
30
31
  ]
31
32
  .filter(Boolean)
@@ -41,7 +41,7 @@ export declare function pageStartOf(requests: NetworkRequest[]): PageStart | und
41
41
  export declare function formatStartOffset(request: NetworkRequest, pageStart: PageStart | undefined): string;
42
42
  /**
43
43
  * Rows of the network stream: requests that finished since the last poll,
44
- * with the column header the first time.
44
+ * with the column header the first time (the stream banner is on stderr).
45
45
  *
46
46
  * @param requests - Newly finished requests
47
47
  * @param options - `header` the first time; `verbose` for full URLs; the page start for START
@@ -170,7 +170,7 @@ function formatNetworkListHuman(requests, options) {
170
170
  const FOLLOW_ID_WIDTH = 14;
171
171
  /**
172
172
  * Rows of the network stream: requests that finished since the last poll,
173
- * with the column header the first time.
173
+ * with the column header the first time (the stream banner is on stderr).
174
174
  *
175
175
  * @param requests - Newly finished requests
176
176
  * @param options - `header` the first time; `verbose` for full URLs; the page start for START
@@ -180,7 +180,6 @@ export function formatNetworkFollowRows(requests, options = {}) {
180
180
  const fmt = new OutputFormatter();
181
181
  const widths = columnWidths(requests, FOLLOW_ID_WIDTH);
182
182
  if (options.header) {
183
- fmt.text('Streaming network requests... (Ctrl+C to stop)');
184
183
  fmt.text(formatColumnHeader(widths));
185
184
  fmt.separator('─', SEPARATOR_WIDTH);
186
185
  }
@@ -53,6 +53,8 @@ export interface PreviewJsonData {
53
53
  target: BdgOutput['target'];
54
54
  partial?: boolean;
55
55
  totals?: BdgOutput['totals'];
56
+ /** When the page's renderer crashed (epoch ms), while it is not loaded again */
57
+ pageCrashedAt?: number;
56
58
  network?: BdgOutput['data']['network'];
57
59
  console?: BdgOutput['data']['console'];
58
60
  }
@@ -3,7 +3,9 @@ import { buildSuccessResponse } from '../OutputBuilder.js';
3
3
  import { formatTimestamp } from './console/shared.js';
4
4
  import { failureReason, formatRequestStatus, getRequestState, } from './requestStatus.js';
5
5
  import { OutputFormatter, truncateUrl, truncateText } from '../formatting.js';
6
+ import { withPageCrashedNote } from '../messages/commands.js';
6
7
  import { PREVIEW_EMPTY_STATES, PREVIEW_HEADERS, compactTipsMessage, verboseCommandsMessage, } from '../messages/preview.js';
8
+ import { consoleDroppedNote } from '../messages/consoleMessages.js';
7
9
  import { sessionCommand } from '../messages/sessionCommand.js';
8
10
  /**
9
11
  * Infer resource type from MIME type when CDP doesn't provide it.
@@ -69,6 +71,7 @@ export function buildPreviewJsonData(output, options) {
69
71
  target: output.target,
70
72
  ...(output.partial !== undefined && { partial: output.partial }),
71
73
  ...(output.totals && { totals: output.totals }),
74
+ ...(output.pageCrashedAt !== undefined && { pageCrashedAt: output.pageCrashedAt }),
72
75
  ...(pick('network') && output.data.network && { network: last(output.data.network) }),
73
76
  ...(pick('console') && output.data.console && { console: last(output.data.console) }),
74
77
  };
@@ -78,19 +81,22 @@ export function buildPreviewJsonData(output, options) {
78
81
  *
79
82
  * @param output - Preview output
80
83
  * @param options - Preview options
81
- * @returns Pretty-printed `{ version, success, data }` envelope
84
+ * @returns `{ version, success, data }` envelope, pretty-printed, or on one
85
+ * line in follow mode (one object per line, NDJSON)
82
86
  */
83
87
  function formatPreviewAsJson(output, options) {
84
- return JSON.stringify(buildSuccessResponse(buildPreviewJsonData(output, options)), null, 2);
88
+ const envelope = buildSuccessResponse(buildPreviewJsonData(output, options));
89
+ return options.follow ? JSON.stringify(envelope) : JSON.stringify(envelope, null, 2);
85
90
  }
86
91
  /**
87
- * Format preview as human-readable output
92
+ * Format preview as human-readable output, after a warning when the page
93
+ * crashed (what is shown was collected before).
88
94
  */
89
95
  function formatPreviewHumanReadable(output, options) {
90
- if (options.verbose) {
91
- return formatPreviewVerbose(output, options);
92
- }
93
- return formatPreviewCompact(output, options);
96
+ const body = options.verbose
97
+ ? formatPreviewVerbose(output, options)
98
+ : formatPreviewCompact(output, options);
99
+ return withPageCrashedNote(body, output.pageCrashedAt);
94
100
  }
95
101
  /**
96
102
  * Format preview in compact format (default)
@@ -145,6 +151,8 @@ function formatPreviewCompact(output, options) {
145
151
  const totalCount = output.totals?.console ?? output.data.console.length;
146
152
  const limitHint = formatLimitHint(showingCount, totalCount);
147
153
  fmt.text(`CONSOLE (${showingCount}/${totalCount})${limitHint}:`);
154
+ if (output.totals?.consoleDropped)
155
+ fmt.text(` ${consoleDroppedNote(output.totals.consoleDropped)}`);
148
156
  if (messages.length === 0) {
149
157
  fmt.text(` ${PREVIEW_EMPTY_STATES.NO_DATA}`);
150
158
  }
@@ -232,6 +240,8 @@ function formatPreviewVerbose(output, options) {
232
240
  ? `Console Messages (all ${messages.length})`
233
241
  : `Console Messages (last ${messages.length} of ${output.totals?.console ?? output.data.console.length})`;
234
242
  fmt.text(title).separator('━', 50);
243
+ if (output.totals?.consoleDropped)
244
+ fmt.text(consoleDroppedNote(output.totals.consoleDropped));
235
245
  if (messages.length === 0) {
236
246
  fmt.text(PREVIEW_EMPTY_STATES.NO_CONSOLE_MESSAGES);
237
247
  }
@@ -1,7 +1,7 @@
1
1
  import type { RunningSessionInfo } from '../../session/sessionList.js';
2
2
  /**
3
- * Format the sessions as a table, followed by the cleanup commands of
4
- * crashed and stale sessions.
3
+ * Format the sessions as a table, followed by why ended sessions ended and
4
+ * the cleanup commands of crashed and stale sessions.
5
5
  *
6
6
  * @param data - Sessions
7
7
  * @returns Human-readable list
@@ -1,9 +1,10 @@
1
1
  import { OutputFormatter } from '../formatting.js';
2
+ import { endedSessionText } from '../messages/session.js';
2
3
  /** Label of the default session in the list */
3
4
  const DEFAULT_SESSION_LABEL = '(default)';
4
5
  /**
5
- * Format the sessions as a table, followed by the cleanup commands of
6
- * crashed and stale sessions.
6
+ * Format the sessions as a table, followed by why ended sessions ended and
7
+ * the cleanup commands of crashed and stale sessions.
7
8
  *
8
9
  * @param data - Sessions
9
10
  * @returns Human-readable list
@@ -31,6 +32,12 @@ export function formatSessionList(data) {
31
32
  .join(' ')
32
33
  .trimEnd());
33
34
  }
35
+ const ended = data.sessions.flatMap(({ name, endReason, endedAt }) => endReason && endedAt !== undefined
36
+ ? [endedSessionText(name ?? DEFAULT_SESSION_LABEL, { reason: endReason, endedAt })]
37
+ : []);
38
+ if (ended.length > 0) {
39
+ fmt.blank().section('Ended without bdg stop:', ended);
40
+ }
34
41
  const cleanups = data.sessions.flatMap((session) => (session.cleanup ? [session.cleanup] : []));
35
42
  if (cleanups.length > 0) {
36
43
  fmt.hints('Crashed or stale sessions; clean up with:', cleanups);
@@ -98,7 +98,7 @@ export function appearanceLines(metadata, pageState) {
98
98
  ? [
99
99
  [
100
100
  'Viewport',
101
- `${viewport.width}×${viewport.height}${metadata.viewport ? ` (emulated ${metadata.viewport.width}x${metadata.viewport.height})` : ''}`,
101
+ `${viewport.width}×${viewport.height}${metadata.viewport ? ` (emulated ${metadata.viewport.width}x${metadata.viewport.height}${metadata.viewport.mobile ? ', phone' : ''})` : ''}`,
102
102
  ],
103
103
  ]
104
104
  : []),
@@ -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' | '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' | '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
  */