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
@@ -25,4 +25,28 @@ export declare function stoppedFollowingConsoleMessage(): string;
25
25
  * @returns e.g. `[n] are positions in the session's message list; not listed in between: 1 message from another page load (-H lists all)`
26
26
  */
27
27
  export declare function consoleIndexGapNote(skipped: ConsoleSkipped): string;
28
+ /**
29
+ * Note that the session dropped its oldest console messages at the limit.
30
+ *
31
+ * @param dropped - Messages dropped
32
+ * @returns e.g. `⚠ 2000 older console messages were dropped: bdg keeps the newest 10000`
33
+ */
34
+ export declare function consoleDroppedNote(dropped: number): string;
35
+ /**
36
+ * Error for `bdg details console <n>` with the index of a dropped message.
37
+ *
38
+ * @param index - Index asked for
39
+ * @param dropped - Messages dropped (the first kept has this index)
40
+ * @returns Message
41
+ */
42
+ export declare function consoleMessageDroppedError(index: number, dropped: number): string;
43
+ /**
44
+ * Note under the console summary when it lists only the newest distinct
45
+ * errors or warnings.
46
+ *
47
+ * @param more - Distinct messages not listed
48
+ * @param level - `error` or `warning`
49
+ * @returns e.g. `(+120 earlier distinct errors; bdg console --level error --last 0 lists every one)`
50
+ */
51
+ export declare function consoleMoreGroupsNote(more: number, level: 'error' | 'warning'): string;
28
52
  //# sourceMappingURL=consoleMessages.d.ts.map
@@ -3,7 +3,9 @@
3
3
  *
4
4
  * User-facing messages for the console command output and formatting.
5
5
  */
6
+ import { MAX_CONSOLE_MESSAGES } from '../../constants.js';
6
7
  import { pluralize } from '../formatting.js';
8
+ import { sessionCommand } from './sessionCommand.js';
7
9
  /**
8
10
  * Generate message for following console output.
9
11
  *
@@ -36,4 +38,34 @@ export function consoleIndexGapNote(skipped) {
36
38
  ].filter(Boolean);
37
39
  return `[n] are positions in the session's message list; not listed in between: ${reasons.join(', ')}`;
38
40
  }
41
+ /**
42
+ * Note that the session dropped its oldest console messages at the limit.
43
+ *
44
+ * @param dropped - Messages dropped
45
+ * @returns e.g. `⚠ 2000 older console messages were dropped: bdg keeps the newest 10000`
46
+ */
47
+ export function consoleDroppedNote(dropped) {
48
+ return `⚠ ${pluralize(dropped, 'older console message')} ${dropped === 1 ? 'was' : 'were'} dropped: bdg keeps the newest ${MAX_CONSOLE_MESSAGES}`;
49
+ }
50
+ /**
51
+ * Error for `bdg details console <n>` with the index of a dropped message.
52
+ *
53
+ * @param index - Index asked for
54
+ * @param dropped - Messages dropped (the first kept has this index)
55
+ * @returns Message
56
+ */
57
+ export function consoleMessageDroppedError(index, dropped) {
58
+ return `Console message ${index} was dropped: bdg keeps the newest ${MAX_CONSOLE_MESSAGES} messages (the oldest kept is ${dropped})`;
59
+ }
60
+ /**
61
+ * Note under the console summary when it lists only the newest distinct
62
+ * errors or warnings.
63
+ *
64
+ * @param more - Distinct messages not listed
65
+ * @param level - `error` or `warning`
66
+ * @returns e.g. `(+120 earlier distinct errors; bdg console --level error --last 0 lists every one)`
67
+ */
68
+ export function consoleMoreGroupsNote(more, level) {
69
+ return `(+${more} earlier distinct ${more === 1 ? level : `${level}s`}; ${sessionCommand(`bdg console --level ${level} --last 0`)} lists every one)`;
70
+ }
39
71
  //# sourceMappingURL=consoleMessages.js.map
@@ -55,10 +55,22 @@ export declare function stoppedFollowingPreviewMessage(): string;
55
55
  * @param retryLabel - Human-readable retry interval (e.g. "1s", "500ms")
56
56
  */
57
57
  export declare function connectionLostRetryMessage(timestamp: string, retryLabel: string): string;
58
+ /**
59
+ * Follow mode stops because the session it followed ended.
60
+ *
61
+ * @returns Message
62
+ */
63
+ export declare function followedSessionEndedMessage(): string;
58
64
  /**
59
65
  * Generate follow-mode stop hint.
60
66
  *
61
67
  * @returns Message instructing the user how to stop follow mode
62
68
  */
63
69
  export declare function connectionLostStopHintMessage(): string;
70
+ /**
71
+ * Notice that `bdg tail` is deprecated (it still runs).
72
+ *
73
+ * @returns Notice for stderr
74
+ */
75
+ export declare function tailDeprecatedNotice(): string;
64
76
  //# sourceMappingURL=preview.d.ts.map
@@ -19,7 +19,7 @@ export const PREVIEW_HEADERS = {
19
19
  * @returns Single-line tip for basic peek usage
20
20
  */
21
21
  export function compactTipsMessage() {
22
- return `Tip: ${sessionCommand('bdg peek --last 50')} | ${sessionCommand('bdg peek --verbose')}`;
22
+ return `Tip: ${sessionCommand('bdg peek --last 50')} or ${sessionCommand('bdg peek --verbose')}`;
23
23
  }
24
24
  /**
25
25
  * Generate verbose mode commands section.
@@ -30,7 +30,7 @@ export function verboseCommandsMessage() {
30
30
  return [
31
31
  'Commands:',
32
32
  ` Full preview: ${sessionCommand('bdg peek --last 50')}`,
33
- ` Watch live: ${sessionCommand('bdg tail')}`,
33
+ ` Watch live: ${sessionCommand('bdg peek --follow')}`,
34
34
  ` End session: ${sessionCommand('bdg stop')}`,
35
35
  ].join('\n');
36
36
  }
@@ -71,6 +71,14 @@ export function stoppedFollowingPreviewMessage() {
71
71
  export function connectionLostRetryMessage(timestamp, retryLabel) {
72
72
  return `\n[${timestamp}] ⚠️ Connection lost, retrying every ${retryLabel}...`;
73
73
  }
74
+ /**
75
+ * Follow mode stops because the session it followed ended.
76
+ *
77
+ * @returns Message
78
+ */
79
+ export function followedSessionEndedMessage() {
80
+ return 'The session ended; stopped following';
81
+ }
74
82
  /**
75
83
  * Generate follow-mode stop hint.
76
84
  *
@@ -79,4 +87,12 @@ export function connectionLostRetryMessage(timestamp, retryLabel) {
79
87
  export function connectionLostStopHintMessage() {
80
88
  return 'Press Ctrl+C to stop';
81
89
  }
90
+ /**
91
+ * Notice that `bdg tail` is deprecated (it still runs).
92
+ *
93
+ * @returns Notice for stderr
94
+ */
95
+ export function tailDeprecatedNotice() {
96
+ return 'Note: "bdg tail" is deprecated and will be removed; use "bdg peek --follow" (same options: --last, --network, --console, --interval, --verbose)';
97
+ }
82
98
  //# sourceMappingURL=preview.js.map
@@ -78,7 +78,18 @@ export declare function lastSessionEndText(end: {
78
78
  endedAt: number;
79
79
  }): string;
80
80
  /**
81
- * Note after a failed start whose daemon had not exited when bdg stopped waiting.
81
+ * A session that ended without `bdg stop`, for `bdg sessions`.
82
+ *
83
+ * @param label - Session name as listed
84
+ * @param end - How and when it ended
85
+ * @returns One line
86
+ */
87
+ export declare function endedSessionText(label: string, end: {
88
+ reason: string;
89
+ endedAt: number;
90
+ }): string;
91
+ /**
92
+ * Note after a failed start or a stop whose daemon had not exited when bdg stopped waiting.
82
93
  *
83
94
  * @param pid - Daemon PID, when known
84
95
  * @param waitedMs - How long bdg waited
@@ -86,7 +97,7 @@ export declare function lastSessionEndText(end: {
86
97
  */
87
98
  export declare function daemonStillExitingHint(pid: number | undefined, waitedMs: number): string;
88
99
  /**
89
- * What to do about a daemon still shutting down after a failed start.
100
+ * What to do about a daemon still shutting down after a failed start or a stop.
90
101
  *
91
102
  * @returns Suggestion
92
103
  */
@@ -89,16 +89,35 @@ export function stopFailedError(reason) {
89
89
  * @returns One line
90
90
  */
91
91
  export function lastSessionEndText(end) {
92
+ return `The last session ended ${sessionEndText(end)}`;
93
+ }
94
+ /**
95
+ * A session that ended without `bdg stop`, for `bdg sessions`.
96
+ *
97
+ * @param label - Session name as listed
98
+ * @param end - How and when it ended
99
+ * @returns One line
100
+ */
101
+ export function endedSessionText(label, end) {
102
+ return `${label} ended ${sessionEndText(end)}`;
103
+ }
104
+ /**
105
+ * When and why a session ended without `bdg stop`.
106
+ *
107
+ * @param end - How and when it ended
108
+ * @returns `at <time>: <why>`
109
+ */
110
+ function sessionEndText(end) {
92
111
  const why = {
93
112
  crash: 'Chrome crashed or was closed',
94
113
  closed: 'its page was closed',
95
114
  timeout: 'the --timeout was reached',
96
115
  };
97
116
  const at = new Date(end.endedAt).toLocaleTimeString();
98
- return `The last session ended at ${at}: ${why[end.reason] ?? end.reason}`;
117
+ return `at ${at}: ${why[end.reason] ?? end.reason}`;
99
118
  }
100
119
  /**
101
- * Note after a failed start whose daemon had not exited when bdg stopped waiting.
120
+ * Note after a failed start or a stop whose daemon had not exited when bdg stopped waiting.
102
121
  *
103
122
  * @param pid - Daemon PID, when known
104
123
  * @param waitedMs - How long bdg waited
@@ -109,7 +128,7 @@ export function daemonStillExitingHint(pid, waitedMs) {
109
128
  return `${daemon} was still shutting down after ${waitedMs / 1000}s`;
110
129
  }
111
130
  /**
112
- * What to do about a daemon still shutting down after a failed start.
131
+ * What to do about a daemon still shutting down after a failed start or a stop.
113
132
  *
114
133
  * @returns Suggestion
115
134
  */
@@ -44,11 +44,42 @@ export function cssLength(value) {
44
44
  * @returns Normalized value
45
45
  */
46
46
  export function normalizeCssValue(value) {
47
- return hexColorsIn(value)
48
- .replace(PX_IN_VALUE, (_match, number) => String(round1(Number(number))))
47
+ return mapOutsideFunctions(hexColorsIn(value), UNIT_KEEPING_FUNCTIONS, (part) => part.replace(PX_IN_VALUE, (_match, number) => String(round1(Number(number)))))
49
48
  .replace(/\s+/g, ' ')
50
49
  .trim();
51
50
  }
51
+ /** Functions whose px stay written: math mixes units, URLs are names */
52
+ const UNIT_KEEPING_FUNCTIONS = new Set(['calc', 'min', 'max', 'clamp', 'url']);
53
+ /**
54
+ * Change the parts of a value that are not inside the given functions.
55
+ *
56
+ * @param value - CSS value
57
+ * @param functions - Function names whose arguments are kept as written
58
+ * @param change - Change for the other parts
59
+ * @returns Value
60
+ */
61
+ function mapOutsideFunctions(value, functions, change) {
62
+ let result = '';
63
+ let start = 0;
64
+ let depth = 0;
65
+ for (let i = 0; i < value.length; i++) {
66
+ if (value[i] === '(') {
67
+ const name = /([\w-]+)$/.exec(value.slice(start, i))?.[1]?.toLowerCase() ?? '';
68
+ if (depth === 0 && functions.has(name)) {
69
+ result += change(value.slice(start, i));
70
+ start = i;
71
+ depth = 1;
72
+ }
73
+ else if (depth > 0)
74
+ depth++;
75
+ }
76
+ else if (value[i] === ')' && depth > 0 && --depth === 0) {
77
+ result += value.slice(start, i + 1);
78
+ start = i + 1;
79
+ }
80
+ }
81
+ return result + (depth > 0 ? value.slice(start) : change(value.slice(start)));
82
+ }
52
83
  /**
53
84
  * 1-4 values the way CSS shorthands write them (top, right, bottom, left).
54
85
  *
@@ -190,9 +221,10 @@ function round3(value) {
190
221
  function decomposeMatrix([a = 1, b = 0, c = 0, d = 1, e = 0, f = 0]) {
191
222
  if (Math.abs(a * c + b * d) > 1e-6)
192
223
  return undefined;
193
- const scaleX = Math.hypot(a, b);
224
+ const mirrored = a * d - b * c < 0 && a < 0;
225
+ const scaleX = mirrored ? -Math.hypot(a, b) : Math.hypot(a, b);
194
226
  const scaleY = scaleX === 0 ? 0 : (a * d - b * c) / scaleX;
195
- const angle = round3((Math.atan2(b, a) * 180) / Math.PI);
227
+ const angle = round3((Math.atan2(mirrored ? -b : b, mirrored ? -a : a) * 180) / Math.PI);
196
228
  const parts = [];
197
229
  if (e !== 0 || f !== 0)
198
230
  parts.push(`translate(${round1(e)},${round1(f)})`);
@@ -121,11 +121,6 @@ export const DECISION_TREES = {
121
121
  yesCommand: 'peek --follow',
122
122
  noAction: 'next',
123
123
  },
124
- {
125
- question: 'Need continuous monitoring (like tail -f)?',
126
- yesCommand: 'tail',
127
- noAction: 'next',
128
- },
129
124
  {
130
125
  question: 'Need quick preview of recent data?',
131
126
  yesCommand: 'peek',
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Checks for a directory bdg is about to create and write (the session
3
+ * directory, a Chrome profile) before it touches it: `fs.mkdirSync` with
4
+ * `recursive` spins forever on Linux pseudo-filesystems (`/proc/x` kept a
5
+ * start at full CPU until SIGKILL), and a profile Chrome cannot use wedges
6
+ * the session.
7
+ */
8
+ /** Why a directory cannot be used, and whether it is a permission problem */
9
+ export interface DirectoryProblem {
10
+ /** e.g. `/proc is a pseudo-filesystem`, `/tmp/x is a file`, `EACCES` */
11
+ reason: string;
12
+ /** Permission denied (or a read-only file system) rather than a wrong path */
13
+ denied: boolean;
14
+ }
15
+ /**
16
+ * Why `dir` cannot be created or written: it (or the path to it) is on a
17
+ * pseudo-filesystem, a part of it is a file, or its nearest existing
18
+ * directory is not writable.
19
+ *
20
+ * @param dir - Directory path (resolved against the working directory)
21
+ * @returns The problem, or null when the directory can be used
22
+ */
23
+ export declare function directoryProblem(dir: string): DirectoryProblem | null;
24
+ /**
25
+ * Create a directory (and its parents) after {@link directoryProblem} found
26
+ * nothing wrong, so a pseudo-filesystem fails at once instead of spinning.
27
+ *
28
+ * @param dir - Directory to create
29
+ * @param mode - Permissions of the directories created
30
+ * @throws Error with `code` `EPSEUDOFS` (pseudo-filesystem), `ENOTDIR` (a
31
+ * file on the path), `EACCES` (not writable), or the `mkdir` error
32
+ */
33
+ export declare function makeDirectory(dir: string, mode?: number): void;
34
+ //# sourceMappingURL=directories.d.ts.map
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Checks for a directory bdg is about to create and write (the session
3
+ * directory, a Chrome profile) before it touches it: `fs.mkdirSync` with
4
+ * `recursive` spins forever on Linux pseudo-filesystems (`/proc/x` kept a
5
+ * start at full CPU until SIGKILL), and a profile Chrome cannot use wedges
6
+ * the session.
7
+ */
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+ /** Pseudo-filesystems no directory can be created on */
11
+ const PSEUDO_FILESYSTEM = /^\/(proc|sys)(\/|$)/;
12
+ /**
13
+ * Nearest path at or above `dir` that exists.
14
+ *
15
+ * @param dir - Absolute path
16
+ * @returns The path itself, or its nearest existing ancestor
17
+ */
18
+ function nearestExisting(dir) {
19
+ let current = dir;
20
+ while (!fs.existsSync(current)) {
21
+ const parent = path.dirname(current);
22
+ if (parent === current)
23
+ return current;
24
+ current = parent;
25
+ }
26
+ return current;
27
+ }
28
+ /**
29
+ * Why `dir` cannot be created or written: it (or the path to it) is on a
30
+ * pseudo-filesystem, a part of it is a file, or its nearest existing
31
+ * directory is not writable.
32
+ *
33
+ * @param dir - Directory path (resolved against the working directory)
34
+ * @returns The problem, or null when the directory can be used
35
+ */
36
+ export function directoryProblem(dir) {
37
+ const resolved = path.resolve(dir);
38
+ const existing = nearestExisting(resolved);
39
+ let real;
40
+ try {
41
+ real = fs.realpathSync(existing);
42
+ }
43
+ catch (error) {
44
+ return { reason: error.code ?? String(error), denied: false };
45
+ }
46
+ if (PSEUDO_FILESYSTEM.test(resolved) || PSEUDO_FILESYSTEM.test(real)) {
47
+ const root = (PSEUDO_FILESYSTEM.exec(resolved) ?? PSEUDO_FILESYSTEM.exec(real))?.[0] ?? '';
48
+ return { reason: `${root.replace(/\/$/, '')} is a pseudo-filesystem`, denied: false };
49
+ }
50
+ if (!fs.statSync(real).isDirectory()) {
51
+ return {
52
+ reason: existing === resolved ? 'it is a file' : `${existing} is a file`,
53
+ denied: false,
54
+ };
55
+ }
56
+ try {
57
+ fs.accessSync(real, fs.constants.W_OK);
58
+ }
59
+ catch (error) {
60
+ const code = error.code ?? String(error);
61
+ return { reason: `${existing} is not writable (${code})`, denied: true };
62
+ }
63
+ return null;
64
+ }
65
+ /**
66
+ * Create a directory (and its parents) after {@link directoryProblem} found
67
+ * nothing wrong, so a pseudo-filesystem fails at once instead of spinning.
68
+ *
69
+ * @param dir - Directory to create
70
+ * @param mode - Permissions of the directories created
71
+ * @throws Error with `code` `EPSEUDOFS` (pseudo-filesystem), `ENOTDIR` (a
72
+ * file on the path), `EACCES` (not writable), or the `mkdir` error
73
+ */
74
+ export function makeDirectory(dir, mode) {
75
+ if (fs.existsSync(dir))
76
+ return;
77
+ const problem = directoryProblem(dir);
78
+ if (problem) {
79
+ const code = problem.denied
80
+ ? 'EACCES'
81
+ : /pseudo-filesystem/.test(problem.reason)
82
+ ? 'EPSEUDOFS'
83
+ : 'ENOTDIR';
84
+ throw Object.assign(new Error(problem.reason), { code });
85
+ }
86
+ fs.mkdirSync(dir, { recursive: true, ...(mode !== undefined && { mode }) });
87
+ }
88
+ //# sourceMappingURL=directories.js.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Whether Chrome can show a window, which decides the default of
3
+ * `--headless`.
4
+ */
5
+ /**
6
+ * Whether a display is available for Chrome's window: on Linux an X11 or
7
+ * Wayland display (`DISPLAY`, `WAYLAND_DISPLAY`; WSLg sets them too), on
8
+ * macOS the desktop unless the shell came in over SSH (`SSH_CONNECTION`,
9
+ * `SSH_TTY`) or runs in CI (`CI`, unless `false` or `0`). Servers, containers and CI stay headless.
10
+ *
11
+ * @param env - Environment variables
12
+ * @param platform - Operating system (`process.platform`)
13
+ * @returns True when Chrome should show a window by default
14
+ */
15
+ export declare function hasDisplay(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
16
+ //# sourceMappingURL=display.d.ts.map
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Whether Chrome can show a window, which decides the default of
3
+ * `--headless`.
4
+ */
5
+ /**
6
+ * Whether an environment variable is set and not empty.
7
+ *
8
+ * @param env - Environment variables
9
+ * @param name - Variable name
10
+ * @returns True when set to a non-empty value
11
+ */
12
+ function isSet(env, name) {
13
+ const value = env[name];
14
+ return value !== undefined && value !== '';
15
+ }
16
+ /**
17
+ * Whether the environment says it is CI: `CI` set to anything but `false`
18
+ * or `0` (`CI=false` is how a CI flag is turned off).
19
+ *
20
+ * @param env - Environment variables
21
+ * @returns True in CI
22
+ */
23
+ function isCi(env) {
24
+ return isSet(env, 'CI') && !['false', '0'].includes(env['CI']?.toLowerCase() ?? '');
25
+ }
26
+ /**
27
+ * Whether a display is available for Chrome's window: on Linux an X11 or
28
+ * Wayland display (`DISPLAY`, `WAYLAND_DISPLAY`; WSLg sets them too), on
29
+ * macOS the desktop unless the shell came in over SSH (`SSH_CONNECTION`,
30
+ * `SSH_TTY`) or runs in CI (`CI`, unless `false` or `0`). Servers, containers and CI stay headless.
31
+ *
32
+ * @param env - Environment variables
33
+ * @param platform - Operating system (`process.platform`)
34
+ * @returns True when Chrome should show a window by default
35
+ */
36
+ export function hasDisplay(env = process.env, platform = process.platform) {
37
+ if (platform === 'darwin') {
38
+ return !isSet(env, 'SSH_CONNECTION') && !isSet(env, 'SSH_TTY') && !isCi(env);
39
+ }
40
+ return isSet(env, 'DISPLAY') || isSet(env, 'WAYLAND_DISPLAY');
41
+ }
42
+ //# sourceMappingURL=display.js.map
@@ -53,6 +53,7 @@ export declare const EXIT_CODES: {
53
53
  readonly UNHANDLED_EXCEPTION: 104;
54
54
  readonly SIGNAL_HANDLER_ERROR: 105;
55
55
  readonly SESSION_START_FAILURE: 106;
56
+ readonly PAGE_CRASHED: 107;
56
57
  readonly SOFTWARE_ERROR: 110;
57
58
  readonly INTERRUPTED: 130;
58
59
  readonly TERMINATED: 143;
@@ -53,6 +53,7 @@ export const EXIT_CODES = {
53
53
  UNHANDLED_EXCEPTION: 104,
54
54
  SIGNAL_HANDLER_ERROR: 105,
55
55
  SESSION_START_FAILURE: 106,
56
+ PAGE_CRASHED: 107,
56
57
  SOFTWARE_ERROR: 110,
57
58
  INTERRUPTED: 130,
58
59
  TERMINATED: 143,
@@ -165,6 +166,11 @@ export const EXIT_CODE_REGISTRY = [
165
166
  name: 'SESSION_START_FAILURE',
166
167
  description: 'Session failed to start (Chrome launch or CDP connection)',
167
168
  },
169
+ {
170
+ code: EXIT_CODES.PAGE_CRASHED,
171
+ name: 'PAGE_CRASHED',
172
+ description: 'The page crashed (its renderer is gone); bdg page reload brings it back',
173
+ },
168
174
  {
169
175
  code: EXIT_CODES.SOFTWARE_ERROR,
170
176
  name: 'SOFTWARE_ERROR',
@@ -52,4 +52,16 @@ export declare function killChromeProcess(pid: number, signal?: NodeJS.Signals):
52
52
  * @returns Command line, or null if unavailable (process gone, or unsupported platform)
53
53
  */
54
54
  export declare function getProcessCommand(pid: number): string | null;
55
+ /** A running process and its command line */
56
+ export interface ProcessEntry {
57
+ pid: number;
58
+ command: string;
59
+ }
60
+ /**
61
+ * Every running process with its command line: from `/proc` on Linux
62
+ * (minimal containers' BusyBox `ps` lacks `-o`), else from `ps` (macOS).
63
+ *
64
+ * @returns Processes, empty when they cannot be listed (Windows)
65
+ */
66
+ export declare function listProcesses(): ProcessEntry[];
55
67
  //# sourceMappingURL=process.d.ts.map
@@ -120,4 +120,29 @@ export function getProcessCommand(pid) {
120
120
  const command = result.stdout.trim();
121
121
  return command.length > 0 ? command : null;
122
122
  }
123
+ /**
124
+ * Every running process with its command line: from `/proc` on Linux
125
+ * (minimal containers' BusyBox `ps` lacks `-o`), else from `ps` (macOS).
126
+ *
127
+ * @returns Processes, empty when they cannot be listed (Windows)
128
+ */
129
+ export function listProcesses() {
130
+ if (process.platform === 'win32')
131
+ return [];
132
+ if (fs.existsSync('/proc/self/cmdline')) {
133
+ return fs
134
+ .readdirSync('/proc')
135
+ .filter((name) => /^\d+$/.test(name))
136
+ .map((name) => ({ pid: Number(name), command: getProcessCommand(Number(name)) ?? '' }))
137
+ .filter((entry) => entry.command !== '');
138
+ }
139
+ const result = spawnSync('ps', ['-A', '-ww', '-o', 'pid=,command='], { encoding: 'utf-8' });
140
+ if (result.error || result.status !== 0)
141
+ return [];
142
+ return result.stdout
143
+ .split('\n')
144
+ .map((line) => /^\s*(\d+)\s+(.*)$/.exec(line))
145
+ .filter((match) => match !== null)
146
+ .map(([, pid, command]) => ({ pid: Number(pid), command: command ?? '' }));
147
+ }
123
148
  //# sourceMappingURL=process.js.map
@@ -18,8 +18,10 @@ export declare function getSuggestion(input: string, candidates: readonly string
18
18
  /**
19
19
  * Names (ids or classes) similar to one that matched nothing, best first, at
20
20
  * most three: near-typos first (Levenshtein distance up to a fifth of the
21
- * length, at least 2), then names sharing a long end, then names sharing a
22
- * long start (at least a third of the name, at least 4 characters). The end
21
+ * length, at least 2), then names sharing a long end (at least a third of
22
+ * the name, at least 4 characters), then names sharing a long start (at
23
+ * least half: `--color-btn-inset-shadow` and `--color-bg-discussions-…` share
24
+ * only a namespace). The end
23
25
  * ranks before the start because ids tend to name an action before the item
24
26
  * (`add-to-cart-backpack` becomes `remove-backpack` once clicked).
25
27
  *
@@ -65,8 +65,10 @@ function commonSuffixLength(a, b) {
65
65
  /**
66
66
  * Names (ids or classes) similar to one that matched nothing, best first, at
67
67
  * most three: near-typos first (Levenshtein distance up to a fifth of the
68
- * length, at least 2), then names sharing a long end, then names sharing a
69
- * long start (at least a third of the name, at least 4 characters). The end
68
+ * length, at least 2), then names sharing a long end (at least a third of
69
+ * the name, at least 4 characters), then names sharing a long start (at
70
+ * least half: `--color-btn-inset-shadow` and `--color-bg-discussions-…` share
71
+ * only a namespace). The end
70
72
  * ranks before the start because ids tend to name an action before the item
71
73
  * (`add-to-cart-backpack` becomes `remove-backpack` once clicked).
72
74
  *
@@ -79,15 +81,15 @@ export function findSimilarNames(name, candidates) {
79
81
  const maxDistance = Math.max(2, Math.floor(name.length / 5));
80
82
  const minAffix = Math.max(4, Math.ceil(name.length / 3));
81
83
  const typos = findMatches(name, others, maxDistance, false).map((match) => match.value);
82
- const byAffix = (length) => others
84
+ const byAffix = (length, min = minAffix) => others
83
85
  .map((candidate) => ({ candidate, length: length(candidate) }))
84
- .filter((entry) => entry.length >= minAffix)
86
+ .filter((entry) => entry.length >= min)
85
87
  .sort((a, b) => b.length - a.length)
86
88
  .map((entry) => entry.candidate);
87
89
  const ranked = [
88
90
  ...typos,
89
91
  ...byAffix((candidate) => commonSuffixLength(name, candidate)),
90
- ...byAffix((candidate) => commonPrefixLength(name, candidate)),
92
+ ...byAffix((candidate) => commonPrefixLength(name, candidate), Math.max(minAffix, Math.ceil(name.length / 2))),
91
93
  ];
92
94
  return [...new Set(ranked)].slice(0, MAX_SIMILAR_NAMES);
93
95
  }
@@ -111,7 +111,7 @@ export const TASK_MAPPINGS = {
111
111
  cdpAlternative: 'Multiple IPC queries to session state',
112
112
  },
113
113
  live_monitoring: {
114
- commands: ['peek --follow', 'tail'],
114
+ commands: ['peek --follow'],
115
115
  description: 'Monitor data collection in real-time',
116
116
  cdpAlternative: 'CDP event subscriptions with custom handler',
117
117
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-debugger-cli",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "DevTools telemetry in your terminal. For humans and agents. Direct WebSocket to Chrome's debugging port.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -128,6 +128,7 @@
128
128
  "overrides": {
129
129
  "eslint-plugin-tsdoc": {
130
130
  "@typescript-eslint/utils": "^8.71.0"
131
- }
131
+ },
132
+ "shell-quote": "^1.12.0"
132
133
  }
133
134
  }