browser-debugger-cli 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/bdg/SKILL.md +2 -1
- package/dist/cdp/methodTarget.d.ts +92 -0
- package/dist/cdp/methodTarget.js +159 -0
- package/dist/cdp/protocol.d.ts +16 -1
- package/dist/cdp/protocol.js +21 -0
- package/dist/cdp/schema.d.ts +55 -1
- package/dist/cdp/schema.js +134 -25
- package/dist/cdp/types.d.ts +3 -1
- package/dist/commands/cdp.d.ts +38 -1
- package/dist/commands/cdp.js +200 -133
- package/dist/commands/cleanup.js +18 -4
- package/dist/commands/dom/formInteraction.js +8 -4
- package/dist/commands/dom/helpers/index.d.ts +4 -4
- package/dist/commands/dom/helpers/index.js +3 -3
- package/dist/commands/dom/helpers/query.d.ts +2 -2
- package/dist/commands/dom/helpers/query.js +2 -2
- package/dist/commands/dom/helpers/screenshot.d.ts +21 -26
- package/dist/commands/dom/helpers/screenshot.js +50 -668
- package/dist/commands/dom/screenshot.js +56 -36
- package/dist/commands/optionBehaviors.js +18 -8
- package/dist/commands/shared/CommandRunner.d.ts +5 -0
- package/dist/commands/shared/CommandRunner.js +18 -3
- package/dist/commands/shared/interrupt.d.ts +40 -0
- package/dist/commands/shared/interrupt.js +73 -0
- package/dist/commands/shared/optionTypes.d.ts +2 -0
- package/dist/commands/shared/startHelpers.d.ts +26 -3
- package/dist/commands/shared/startHelpers.js +145 -23
- package/dist/commands/types.d.ts +5 -0
- package/dist/connection/cdp.js +1 -16
- package/dist/connection/chromeIdentity.d.ts +24 -5
- package/dist/connection/chromeIdentity.js +53 -22
- package/dist/connection/launcher.d.ts +34 -1
- package/dist/connection/launcher.js +98 -10
- package/dist/connection/typed-cdp.d.ts +3 -2
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/daemon/SessionController.d.ts +10 -5
- package/dist/daemon/SessionController.js +15 -8
- package/dist/daemon/ipcServer.js +1 -1
- package/dist/daemon/launcher.d.ts +5 -0
- package/dist/daemon/launcher.js +8 -1
- package/dist/daemon/session/Session.d.ts +5 -1
- package/dist/daemon/session/Session.js +9 -8
- package/dist/daemon/session/TelemetryStore.d.ts +5 -0
- package/dist/daemon/session/TelemetryStore.js +4 -0
- package/dist/daemon/session/captureGate.d.ts +59 -0
- package/dist/daemon/session/captureGate.js +96 -0
- package/dist/daemon/session/chromeConnection.d.ts +16 -1
- package/dist/daemon/session/chromeConnection.js +34 -4
- package/dist/daemon/session/collectors.d.ts +15 -0
- package/dist/daemon/session/collectors.js +39 -2
- package/dist/daemon/session/commandRegistry.d.ts +14 -1
- package/dist/daemon/session/commandRegistry.js +46 -11
- package/dist/daemon/session/downloads.d.ts +32 -0
- package/dist/daemon/session/downloads.js +96 -0
- package/dist/daemon/session/interactions.d.ts +3 -2
- package/dist/daemon/session/interactions.js +7 -2
- package/dist/daemon/session/plugins.js +6 -0
- package/dist/daemon.js +12843 -11482
- package/dist/errors/CommandError.d.ts +2 -0
- package/dist/errors/issues.d.ts +1 -1
- package/dist/errors/messages.d.ts +58 -0
- package/dist/errors/messages.js +112 -0
- package/dist/index.js +999 -1020
- package/dist/ipc/client.d.ts +14 -1
- package/dist/ipc/client.js +21 -4
- package/dist/ipc/protocol/commands.d.ts +32 -2
- package/dist/ipc/protocol/commands.js +1 -0
- package/dist/ipc/protocol/domTypes.d.ts +24 -1
- package/dist/ipc/session/queries.d.ts +3 -0
- package/dist/ipc/session/types.d.ts +5 -0
- package/dist/ipc/transport/IPCError.d.ts +9 -0
- package/dist/ipc/transport/IPCError.js +12 -0
- package/dist/ipc/transport/errors.d.ts +2 -1
- package/dist/ipc/transport/errors.js +4 -1
- package/dist/ipc/transport/index.d.ts +4 -2
- package/dist/ipc/transport/index.js +13 -3
- package/dist/runtime/dom/actionEffects.d.ts +48 -9
- package/dist/runtime/dom/actionEffects.js +269 -34
- package/dist/runtime/dom/actionEffectsScripts.d.ts +45 -0
- package/dist/runtime/dom/actionEffectsScripts.js +101 -2
- package/dist/runtime/dom/captureArea.d.ts +35 -0
- package/dist/runtime/dom/captureArea.js +203 -0
- package/dist/runtime/dom/elementInfo.d.ts +10 -8
- package/dist/runtime/dom/elementInfo.js +8 -6
- package/dist/runtime/dom/formDiscovery.d.ts +1 -1
- package/dist/runtime/page/bdgWorld.d.ts +9 -0
- package/dist/runtime/page/bdgWorld.js +11 -0
- package/dist/runtime/page/captureEmulation.d.ts +119 -0
- package/dist/runtime/page/captureEmulation.js +189 -0
- package/dist/runtime/page/captureScroll.d.ts +24 -0
- package/dist/runtime/page/captureScroll.js +124 -0
- package/dist/runtime/page/screenshot.d.ts +41 -0
- package/dist/runtime/page/screenshot.js +394 -0
- package/dist/session/paths.d.ts +14 -0
- package/dist/session/paths.js +25 -0
- package/dist/telemetry/downloads.d.ts +127 -0
- package/dist/telemetry/downloads.js +265 -0
- package/dist/telemetry/har/builder.js +22 -7
- package/dist/telemetry/har/sanitize.d.ts +7 -3
- package/dist/telemetry/har/sanitize.js +52 -6
- package/dist/telemetry/har/sanitizeBody.d.ts +47 -7
- package/dist/telemetry/har/sanitizeBody.js +429 -56
- package/dist/telemetry/har/types.d.ts +2 -0
- package/dist/telemetry/network.d.ts +4 -4
- package/dist/telemetry/network.js +38 -4
- package/dist/telemetry/networkRetention.d.ts +35 -14
- package/dist/telemetry/networkRetention.js +62 -26
- package/dist/types.d.ts +9 -14
- package/dist/ui/OutputBuilder.d.ts +3 -2
- package/dist/ui/OutputBuilder.js +4 -3
- package/dist/ui/formatters/cdp.d.ts +32 -9
- package/dist/ui/formatters/cdp.js +77 -6
- package/dist/ui/formatters/details.js +7 -15
- package/dist/ui/formatters/preview.d.ts +2 -0
- package/dist/ui/formatters/preview.js +7 -1
- package/dist/ui/formatters/status.js +6 -1
- package/dist/ui/formatting.d.ts +7 -0
- package/dist/ui/formatting.js +13 -0
- package/dist/ui/logging/logger.d.ts +1 -1
- package/dist/ui/messages/chrome.d.ts +13 -0
- package/dist/ui/messages/chrome.js +26 -0
- package/dist/ui/messages/commands.d.ts +71 -3
- package/dist/ui/messages/commands.js +98 -3
- package/dist/ui/messages/networkMessages.d.ts +24 -5
- package/dist/ui/messages/networkMessages.js +31 -8
- package/dist/utils/async.d.ts +3 -2
- package/dist/utils/async.js +16 -3
- package/dist/utils/http.d.ts +11 -4
- package/dist/utils/http.js +5 -3
- package/package.json +18 -4
- /package/dist/{commands/dom → runtime/page}/screenshotResize.d.ts +0 -0
- /package/dist/{commands/dom → runtime/page}/screenshotResize.js +0 -0
package/dist/ui/formatting.d.ts
CHANGED
|
@@ -83,4 +83,11 @@ export declare function truncateUrl(url: string, maxLength?: number): string;
|
|
|
83
83
|
export declare function cutMiddle(text: string, maxLength: number): string;
|
|
84
84
|
export declare function pluralize(count: number, singular: string, plural?: string): string;
|
|
85
85
|
export declare function formatDuration(ms: number): string;
|
|
86
|
+
/**
|
|
87
|
+
* Format a byte count for humans.
|
|
88
|
+
*
|
|
89
|
+
* @param bytes - Byte count
|
|
90
|
+
* @returns e.g. "512 B", "12.3 KB"
|
|
91
|
+
*/
|
|
92
|
+
export declare function formatBytes(bytes: number): string;
|
|
86
93
|
//# sourceMappingURL=formatting.d.ts.map
|
package/dist/ui/formatting.js
CHANGED
|
@@ -216,4 +216,17 @@ export function formatDuration(ms) {
|
|
|
216
216
|
}
|
|
217
217
|
return `${ms}ms`;
|
|
218
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Format a byte count for humans.
|
|
221
|
+
*
|
|
222
|
+
* @param bytes - Byte count
|
|
223
|
+
* @returns e.g. "512 B", "12.3 KB"
|
|
224
|
+
*/
|
|
225
|
+
export function formatBytes(bytes) {
|
|
226
|
+
if (bytes < 1024)
|
|
227
|
+
return `${bytes} B`;
|
|
228
|
+
return bytes < 1024 * 1024
|
|
229
|
+
? `${(bytes / 1024).toFixed(1)} KB`
|
|
230
|
+
: `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
231
|
+
}
|
|
219
232
|
//# sourceMappingURL=formatting.js.map
|
|
@@ -29,7 +29,7 @@ export type LogLevel = 'info' | 'debug';
|
|
|
29
29
|
* Log contexts for different components.
|
|
30
30
|
* Used to prefix log messages with component name.
|
|
31
31
|
*/
|
|
32
|
-
export type LogContext = 'bdg' | 'launcher' | 'daemon' | 'client' | 'cleanup' | 'session' | 'chrome' | 'cdp' | 'ipc' | 'http' | 'dialogs' | 'navigation' | 'page-crash' | 'console' | 'dom' | 'network' | 'diagnostics' | 'readiness' | 'atomic-file' | 'object-expander' | 'fetcher' | 'targets';
|
|
32
|
+
export type LogContext = 'bdg' | 'launcher' | 'daemon' | 'client' | 'cleanup' | 'session' | 'chrome' | 'cdp' | 'ipc' | 'http' | 'dialogs' | 'downloads' | 'navigation' | 'page-crash' | 'console' | 'dom' | 'network' | 'diagnostics' | 'readiness' | 'atomic-file' | 'object-expander' | 'fetcher' | 'targets';
|
|
33
33
|
/**
|
|
34
34
|
* Logger instance with support for different log levels.
|
|
35
35
|
*/
|
|
@@ -26,6 +26,8 @@ export declare function formatChromeNotice(notice: NoticeDetails<ChromeNoticeCod
|
|
|
26
26
|
* @returns User-facing message
|
|
27
27
|
*/
|
|
28
28
|
export declare function formatChromeIssue(issue: IssueDetails, diagnostics?: () => ChromeDiagnostics): string;
|
|
29
|
+
/** Chrome's launch ended because the session was stopped meanwhile */
|
|
30
|
+
export declare const CHROME_LAUNCH_ABORTED_MESSAGE = "Chrome launch aborted: the session was stopped";
|
|
29
31
|
/**
|
|
30
32
|
* Chrome exited before its debugging port opened.
|
|
31
33
|
*
|
|
@@ -188,6 +190,17 @@ export declare function portTakenByReason(answeredBy: 'browser' | 'process' | 'n
|
|
|
188
190
|
* @returns Reason for the CHROME_LAUNCH_FAILED issue
|
|
189
191
|
*/
|
|
190
192
|
export declare function chromeNotAnsweringReason(port: number, waitedMs: number): string;
|
|
193
|
+
/**
|
|
194
|
+
* Chrome is running but did not open its debugging port within
|
|
195
|
+
* chrome-launcher's readiness budget (a slow start, not a crash or a port
|
|
196
|
+
* conflict: nothing answered on the port, which was free before the launch).
|
|
197
|
+
*
|
|
198
|
+
* @param pid - Chrome's PID
|
|
199
|
+
* @param port - Debugging port
|
|
200
|
+
* @param waitedMs - The readiness budget
|
|
201
|
+
* @returns Error message
|
|
202
|
+
*/
|
|
203
|
+
export declare function chromePortNotOpenedMessage(pid: number, port: number, waitedMs: number): string;
|
|
191
204
|
/**
|
|
192
205
|
* Warning when bdg's Chrome preferences (password manager and leak check
|
|
193
206
|
* off, etc.) could not be written into the profile.
|
|
@@ -62,6 +62,8 @@ export function formatChromeIssue(issue, diagnostics = getChromeDiagnostics) {
|
|
|
62
62
|
return joinLines(header, '', ...diagnosticLines);
|
|
63
63
|
return joinLines(header, '', 'Possible causes:', ` - Port ${port} conflict (check: lsof -ti:${port})`, ` - Chrome binary not found`, ` - Insufficient permissions`, ` - Chrome crashed on startup`, '', ...diagnosticLines, '', 'Try:', ` - ${sessionCommand('bdg cleanup')}`, ` - See what uses the port: lsof -i :${port}`, ` - Use different port: ${sessionCommand(`bdg <url> --port ${port + 1}`)}`, ` - In a container where Chrome's sandbox fails: BDG_NO_SANDBOX=1 ${sessionCommand('bdg <url>')}`);
|
|
64
64
|
}
|
|
65
|
+
case 'CHROME_PORT_NOT_OPENED':
|
|
66
|
+
return joinLines(chromePortNotOpenedMessage(ctx['pid'], ctx['port'], ctx['waitedMs']), chromePortNotOpenedSuggestion());
|
|
65
67
|
case 'CHROME_EXITED_DURING_STARTUP':
|
|
66
68
|
return chromeExitedDuringStartupError(ctx['exitCode'], ctx['output'] ?? [], ctx['userDataDir'], ctx['profileInUse'] === true);
|
|
67
69
|
case 'NO_PAGE_TARGET_FOUND':
|
|
@@ -86,6 +88,8 @@ export function formatChromeIssue(issue, diagnostics = getChromeDiagnostics) {
|
|
|
86
88
|
return `Chrome preferences must be JSON-serializable: ${ctx['reason'] ?? 'unknown error'}`;
|
|
87
89
|
}
|
|
88
90
|
}
|
|
91
|
+
/** Chrome's launch ended because the session was stopped meanwhile */
|
|
92
|
+
export const CHROME_LAUNCH_ABORTED_MESSAGE = 'Chrome launch aborted: the session was stopped';
|
|
89
93
|
/**
|
|
90
94
|
* Chrome exited before its debugging port opened.
|
|
91
95
|
*
|
|
@@ -345,6 +349,28 @@ export function portTakenByReason(answeredBy, chromeHost) {
|
|
|
345
349
|
export function chromeNotAnsweringReason(port, waitedMs) {
|
|
346
350
|
return `Chrome announced port ${port} but did not answer on 127.0.0.1 within ${(waitedMs / 1000).toFixed(1)}s (slow start)`;
|
|
347
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Chrome is running but did not open its debugging port within
|
|
354
|
+
* chrome-launcher's readiness budget (a slow start, not a crash or a port
|
|
355
|
+
* conflict: nothing answered on the port, which was free before the launch).
|
|
356
|
+
*
|
|
357
|
+
* @param pid - Chrome's PID
|
|
358
|
+
* @param port - Debugging port
|
|
359
|
+
* @param waitedMs - The readiness budget
|
|
360
|
+
* @returns Error message
|
|
361
|
+
*/
|
|
362
|
+
export function chromePortNotOpenedMessage(pid, port, waitedMs) {
|
|
363
|
+
return `Chrome started (pid ${pid}) but did not open its debugging port ${port} within ${Number((waitedMs / 1000).toFixed(1))} s`;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* What to do when Chrome started but did not open its debugging port in time.
|
|
367
|
+
*
|
|
368
|
+
* @returns Suggestion
|
|
369
|
+
*/
|
|
370
|
+
function chromePortNotOpenedSuggestion() {
|
|
371
|
+
return (`Retry: ${sessionCommand('bdg <url>')} (a first start on a cold machine can be slow). ` +
|
|
372
|
+
`If it keeps failing: ${sessionCommand('bdg cleanup')} && ${sessionCommand('bdg <url>')}`);
|
|
373
|
+
}
|
|
348
374
|
/**
|
|
349
375
|
* Warning when bdg's Chrome preferences (password manager and leak check
|
|
350
376
|
* off, etc.) could not be written into the profile.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* cleaning up stale files, and validating command arguments.
|
|
6
6
|
*/
|
|
7
7
|
import type { DomFrame, PageLoadingState, PendingRequestInfo } from '../../ipc/protocol/commands.js';
|
|
8
|
-
import type { ElementLayout, FillValueMismatch, LayoutSize, NewMessage, PageLayout, PageNavigation, PendingChanges, ShownElement } from '../../ipc/protocol/domTypes.js';
|
|
8
|
+
import type { DownloadInfo, ElementLayout, FillValueMismatch, LayoutSize, NewMessage, PageLayout, PageNavigation, PendingChanges, ShownElement } from '../../ipc/protocol/domTypes.js';
|
|
9
9
|
import type { InspectVisibility } from '../../ipc/protocol/inspectTypes.js';
|
|
10
10
|
import type { DelegationNote } from '../../runtime/dom/listenerSummary.js';
|
|
11
11
|
import type { WaitCondition, WaitSnapshot } from '../../runtime/dom/waitCondition.js';
|
|
@@ -40,6 +40,34 @@ export declare function skillBackupMessage(path: string): string;
|
|
|
40
40
|
export declare function domClickFallbackWarning(reason: string | null | undefined): string;
|
|
41
41
|
/** Reason of a `bdg dom form` blocker for a required field left empty */
|
|
42
42
|
export declare const REQUIRED_FIELD_EMPTY_REASON = "Required field is empty";
|
|
43
|
+
/**
|
|
44
|
+
* Warning for a `bdg cdp` method the bundled protocol lacks, sent anyway.
|
|
45
|
+
*
|
|
46
|
+
* @param method - Method sent
|
|
47
|
+
* @param protocolVersion - Bundled devtools-protocol version
|
|
48
|
+
* @returns Warning text
|
|
49
|
+
*/
|
|
50
|
+
export declare function cdpUnlistedMethodWarning(method: string, protocolVersion: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* `bdg cdp --describe` line for a redirect to a method the protocol lacks
|
|
53
|
+
* (e.g. Page.deleteCookie names Network.deleteCookie).
|
|
54
|
+
*
|
|
55
|
+
* @param method - Redirect target
|
|
56
|
+
* @returns Line text
|
|
57
|
+
*/
|
|
58
|
+
export declare function cdpUnresolvedRedirectLine(method: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* `bdg cdp --describe` title of a redirect to a method the protocol has.
|
|
61
|
+
*
|
|
62
|
+
* @param method - Redirect target
|
|
63
|
+
* @returns Title text
|
|
64
|
+
*/
|
|
65
|
+
export declare function cdpRedirectTitle(method: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* Lines of `bdg cdp --help` saying how names are matched (each short
|
|
68
|
+
* enough not to be wrapped).
|
|
69
|
+
*/
|
|
70
|
+
export declare const CDP_EXECUTION_HELP: string;
|
|
43
71
|
/**
|
|
44
72
|
* Part of the `bdg dom form` summary naming the required fields left empty.
|
|
45
73
|
*
|
|
@@ -101,6 +129,38 @@ export declare function shownElementText(element: ShownElement): string;
|
|
|
101
129
|
* "URL changed to https://example.com/#/active (same document)"
|
|
102
130
|
*/
|
|
103
131
|
export declare function pageNavigationText(navigation: PageNavigation): string;
|
|
132
|
+
/**
|
|
133
|
+
* A download an action started, as its output line reads.
|
|
134
|
+
*
|
|
135
|
+
* @param download - Download
|
|
136
|
+
* @returns e.g. `Download: report.txt → /Users/me/.bdg/downloads/report.txt (completed, 15 B)`,
|
|
137
|
+
* `Download: big.zip → … (inProgress, 9.8 KB so far)` (no size before the first
|
|
138
|
+
* bytes), `Download: report.txt (canceled)`
|
|
139
|
+
*/
|
|
140
|
+
export declare function downloadText(download: DownloadInfo): string;
|
|
141
|
+
/**
|
|
142
|
+
* The session's downloads in one line: how many, and the last one.
|
|
143
|
+
*
|
|
144
|
+
* @param downloads - Downloads, oldest first (at least one)
|
|
145
|
+
* @returns e.g. `2 (last: report.txt → /Users/me/.bdg/downloads/report.txt, completed)`
|
|
146
|
+
*/
|
|
147
|
+
export declare function downloadsSummary(downloads: DownloadInfo[]): string;
|
|
148
|
+
/**
|
|
149
|
+
* Warning while Chrome did not take bdg's download behavior, so downloads
|
|
150
|
+
* are not saved in the session directory.
|
|
151
|
+
*
|
|
152
|
+
* @param detail - Chrome's error
|
|
153
|
+
* @returns Warning shown by `bdg status`
|
|
154
|
+
*/
|
|
155
|
+
export declare function downloadsNotRedirectedWarning(detail: string): string;
|
|
156
|
+
/**
|
|
157
|
+
* Why downloads are refused when the session's downloads directory could not
|
|
158
|
+
* be created.
|
|
159
|
+
*
|
|
160
|
+
* @param detail - What went wrong, e.g. `/home/me/.bdg/downloads is a file`
|
|
161
|
+
* @returns Reason recorded on each refused download
|
|
162
|
+
*/
|
|
163
|
+
export declare function downloadsDirUnavailableReason(detail: string): string;
|
|
104
164
|
/**
|
|
105
165
|
* Last `New text:` row when an action made more messages appear than are listed.
|
|
106
166
|
*
|
|
@@ -748,9 +808,17 @@ export declare function sessionFilesCleanedMessage(): string;
|
|
|
748
808
|
*/
|
|
749
809
|
export declare function sessionOutputRemovedMessage(): string;
|
|
750
810
|
/**
|
|
751
|
-
*
|
|
811
|
+
* Cleanup left the session's downloaded files in place.
|
|
752
812
|
*
|
|
753
|
-
* @
|
|
813
|
+
* @param dir - Downloads directory
|
|
814
|
+
* @param files - Files in it
|
|
815
|
+
* @returns e.g. `Downloads kept: 2 files in /Users/me/.bdg/downloads (delete them yourself when done)`
|
|
816
|
+
*/
|
|
817
|
+
export declare function downloadsKeptMessage(dir: string, files: number): string;
|
|
818
|
+
/**
|
|
819
|
+
* Cleanup finished and left nothing behind.
|
|
820
|
+
*
|
|
821
|
+
* @returns Message
|
|
754
822
|
*/
|
|
755
823
|
export declare function sessionDirectoryCleanMessage(): string;
|
|
756
824
|
/**
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* cleaning up stale files, and validating command arguments.
|
|
6
6
|
*/
|
|
7
7
|
import { buildAgentDiscoveryHelp, buildCommonTaskExamples, buildUrlExamples, buildSessionManagementReminder, } from '../formatters/helpFormatters.js';
|
|
8
|
-
import { formatDuration, joinLines, pluralize, truncateUrl } from '../formatting.js';
|
|
8
|
+
import { formatBytes, formatDuration, joinLines, pluralize, truncateUrl } from '../formatting.js';
|
|
9
9
|
import { sessionCommand } from './sessionCommand.js';
|
|
10
10
|
import { truncateByLength } from '../../utils/strings.js';
|
|
11
11
|
/**
|
|
@@ -60,6 +60,41 @@ function fieldLabelList(labels) {
|
|
|
60
60
|
.join(', ');
|
|
61
61
|
return labels.length > 5 ? `${shown} and ${labels.length - 5} more` : shown;
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Warning for a `bdg cdp` method the bundled protocol lacks, sent anyway.
|
|
65
|
+
*
|
|
66
|
+
* @param method - Method sent
|
|
67
|
+
* @param protocolVersion - Bundled devtools-protocol version
|
|
68
|
+
* @returns Warning text
|
|
69
|
+
*/
|
|
70
|
+
export function cdpUnlistedMethodWarning(method, protocolVersion) {
|
|
71
|
+
return `${method} is not in the bundled protocol (devtools-protocol ${protocolVersion}); sending it to Chrome as is`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* `bdg cdp --describe` line for a redirect to a method the protocol lacks
|
|
75
|
+
* (e.g. Page.deleteCookie names Network.deleteCookie).
|
|
76
|
+
*
|
|
77
|
+
* @param method - Redirect target
|
|
78
|
+
* @returns Line text
|
|
79
|
+
*/
|
|
80
|
+
export function cdpUnresolvedRedirectLine(method) {
|
|
81
|
+
return `Redirect target ${method} is not in the protocol (unresolved redirect)`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* `bdg cdp --describe` title of a redirect to a method the protocol has.
|
|
85
|
+
*
|
|
86
|
+
* @param method - Redirect target
|
|
87
|
+
* @returns Title text
|
|
88
|
+
*/
|
|
89
|
+
export function cdpRedirectTitle(method) {
|
|
90
|
+
return `Implemented by ${method} (redirect)`;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Lines of `bdg cdp --help` saying how names are matched (each short
|
|
94
|
+
* enough not to be wrapped).
|
|
95
|
+
*/
|
|
96
|
+
export const CDP_EXECUTION_HELP = ' Execution: bundled methods are case-insensitive (network.getcookies works)\n' +
|
|
97
|
+
' Other methods are sent as typed (case-sensitive)';
|
|
63
98
|
/**
|
|
64
99
|
* Part of the `bdg dom form` summary naming the required fields left empty.
|
|
65
100
|
*
|
|
@@ -145,6 +180,56 @@ export function pageNavigationText(navigation) {
|
|
|
145
180
|
const status = navigation.status === undefined ? '' : ` (${navigation.status})`;
|
|
146
181
|
return `navigated to ${navigation.url}${status}`;
|
|
147
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* A download an action started, as its output line reads.
|
|
185
|
+
*
|
|
186
|
+
* @param download - Download
|
|
187
|
+
* @returns e.g. `Download: report.txt → /Users/me/.bdg/downloads/report.txt (completed, 15 B)`,
|
|
188
|
+
* `Download: big.zip → … (inProgress, 9.8 KB so far)` (no size before the first
|
|
189
|
+
* bytes), `Download: report.txt (canceled)`
|
|
190
|
+
*/
|
|
191
|
+
export function downloadText(download) {
|
|
192
|
+
const where = download.path === undefined ? '' : ` → ${download.path}`;
|
|
193
|
+
const running = download.state === 'inProgress';
|
|
194
|
+
const bytes = download.bytes === undefined || (running && download.bytes === 0)
|
|
195
|
+
? ''
|
|
196
|
+
: `, ${formatBytes(download.bytes)}${running ? ' so far' : ''}`;
|
|
197
|
+
const reason = download.reason === undefined ? '' : `: ${download.reason}`;
|
|
198
|
+
return `Download: ${download.suggestedFilename}${where} (${download.state}${bytes}${reason})`;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* The session's downloads in one line: how many, and the last one.
|
|
202
|
+
*
|
|
203
|
+
* @param downloads - Downloads, oldest first (at least one)
|
|
204
|
+
* @returns e.g. `2 (last: report.txt → /Users/me/.bdg/downloads/report.txt, completed)`
|
|
205
|
+
*/
|
|
206
|
+
export function downloadsSummary(downloads) {
|
|
207
|
+
const last = downloads[downloads.length - 1];
|
|
208
|
+
if (!last)
|
|
209
|
+
return '0';
|
|
210
|
+
const where = last.path === undefined ? '' : ` → ${last.path}`;
|
|
211
|
+
return `${downloads.length} (last: ${last.suggestedFilename}${where}, ${last.state})`;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Warning while Chrome did not take bdg's download behavior, so downloads
|
|
215
|
+
* are not saved in the session directory.
|
|
216
|
+
*
|
|
217
|
+
* @param detail - Chrome's error
|
|
218
|
+
* @returns Warning shown by `bdg status`
|
|
219
|
+
*/
|
|
220
|
+
export function downloadsNotRedirectedWarning(detail) {
|
|
221
|
+
return `downloads are not redirected to the session directory (Chrome refused the download behavior: ${detail}); Chrome saves them in its default folder, usually ~/Downloads`;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Why downloads are refused when the session's downloads directory could not
|
|
225
|
+
* be created.
|
|
226
|
+
*
|
|
227
|
+
* @param detail - What went wrong, e.g. `/home/me/.bdg/downloads is a file`
|
|
228
|
+
* @returns Reason recorded on each refused download
|
|
229
|
+
*/
|
|
230
|
+
export function downloadsDirUnavailableReason(detail) {
|
|
231
|
+
return `bdg's downloads directory could not be created (${detail}), so downloads are refused`;
|
|
232
|
+
}
|
|
148
233
|
/**
|
|
149
234
|
* Last `New text:` row when an action made more messages appear than are listed.
|
|
150
235
|
*
|
|
@@ -1161,9 +1246,19 @@ export function sessionOutputRemovedMessage() {
|
|
|
1161
1246
|
return 'Session output file removed';
|
|
1162
1247
|
}
|
|
1163
1248
|
/**
|
|
1164
|
-
*
|
|
1249
|
+
* Cleanup left the session's downloaded files in place.
|
|
1165
1250
|
*
|
|
1166
|
-
* @
|
|
1251
|
+
* @param dir - Downloads directory
|
|
1252
|
+
* @param files - Files in it
|
|
1253
|
+
* @returns e.g. `Downloads kept: 2 files in /Users/me/.bdg/downloads (delete them yourself when done)`
|
|
1254
|
+
*/
|
|
1255
|
+
export function downloadsKeptMessage(dir, files) {
|
|
1256
|
+
return `Downloads kept: ${pluralize(files, 'file')} in ${dir} (delete them yourself when done)`;
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Cleanup finished and left nothing behind.
|
|
1260
|
+
*
|
|
1261
|
+
* @returns Message
|
|
1167
1262
|
*/
|
|
1168
1263
|
export function sessionDirectoryCleanMessage() {
|
|
1169
1264
|
return 'Session directory is now clean';
|
|
@@ -4,23 +4,42 @@
|
|
|
4
4
|
* User-facing messages for the network list command output and formatting.
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
|
-
* Why a stored response body was replaced by a placeholder
|
|
8
|
-
* `bdg details network <id>` as `
|
|
7
|
+
* Why a stored request or response body was replaced by a placeholder
|
|
8
|
+
* (shown by `bdg details network <id>` as `requestBodyNotCaptured` or
|
|
9
|
+
* `bodyNotCaptured`).
|
|
9
10
|
*
|
|
10
11
|
* @param budgetBytes - Total body budget of the session
|
|
11
|
-
* @returns e.g. `evicted: total body budget (bdg keeps the newest 100 MB of response bodies)`
|
|
12
|
+
* @returns e.g. `evicted: total body budget (bdg keeps the newest 100 MB of request and response bodies)`
|
|
12
13
|
*/
|
|
13
14
|
export declare function bodyEvictedReason(budgetBytes: number): string;
|
|
15
|
+
/**
|
|
16
|
+
* Why a response body is missing when Chrome no longer had it
|
|
17
|
+
* (`Network.getResponseBody` failed with "No resource with given identifier
|
|
18
|
+
* found" or "No data found for resource with given identifier"), shown by
|
|
19
|
+
* `bdg details network <id>` as `bodyNotCaptured` and in the HAR as the
|
|
20
|
+
* content comment.
|
|
21
|
+
*
|
|
22
|
+
* @returns Reason text
|
|
23
|
+
*/
|
|
24
|
+
export declare function bodyGoneReason(): string;
|
|
25
|
+
/**
|
|
26
|
+
* Why a response body is missing when `Network.getResponseBody` failed for
|
|
27
|
+
* another reason (a CDP timeout, the connection closing mid-fetch).
|
|
28
|
+
*
|
|
29
|
+
* @param errorMessage - The error Chrome or the connection gave
|
|
30
|
+
* @returns e.g. `Chrome did not return the body: CDP command timeout`
|
|
31
|
+
*/
|
|
32
|
+
export declare function bodyFetchFailedReason(errorMessage: string): string;
|
|
14
33
|
/** What a session's network capture let go at its limits */
|
|
15
34
|
export interface NetworkEvictionCounts {
|
|
16
35
|
/** Oldest finished requests dropped at the request cap */
|
|
17
36
|
requestsDropped: number;
|
|
18
|
-
/** Oldest response bodies evicted at the body budget */
|
|
37
|
+
/** Oldest request and response bodies evicted at the body budget */
|
|
19
38
|
bodiesEvicted: number;
|
|
20
39
|
}
|
|
21
40
|
/**
|
|
22
41
|
* Note that the session dropped its oldest requests or evicted its oldest
|
|
23
|
-
* response bodies at its limits.
|
|
42
|
+
* request and response bodies at its limits.
|
|
24
43
|
*
|
|
25
44
|
* @param counts - Requests dropped and bodies evicted
|
|
26
45
|
* @returns e.g. `⚠ 2000 older network requests were dropped: bdg keeps the newest 10000`;
|
|
@@ -15,18 +15,41 @@ function megabytes(bytes) {
|
|
|
15
15
|
return `${Math.round(bytes / (1024 * 1024))} MB`;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Why a stored response body was replaced by a placeholder
|
|
19
|
-
* `bdg details network <id>` as `
|
|
18
|
+
* Why a stored request or response body was replaced by a placeholder
|
|
19
|
+
* (shown by `bdg details network <id>` as `requestBodyNotCaptured` or
|
|
20
|
+
* `bodyNotCaptured`).
|
|
20
21
|
*
|
|
21
22
|
* @param budgetBytes - Total body budget of the session
|
|
22
|
-
* @returns e.g. `evicted: total body budget (bdg keeps the newest 100 MB of response bodies)`
|
|
23
|
+
* @returns e.g. `evicted: total body budget (bdg keeps the newest 100 MB of request and response bodies)`
|
|
23
24
|
*/
|
|
24
25
|
export function bodyEvictedReason(budgetBytes) {
|
|
25
|
-
return `evicted: total body budget (bdg keeps the newest ${megabytes(budgetBytes)} of response bodies)`;
|
|
26
|
+
return `evicted: total body budget (bdg keeps the newest ${megabytes(budgetBytes)} of request and response bodies)`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Why a response body is missing when Chrome no longer had it
|
|
30
|
+
* (`Network.getResponseBody` failed with "No resource with given identifier
|
|
31
|
+
* found" or "No data found for resource with given identifier"), shown by
|
|
32
|
+
* `bdg details network <id>` as `bodyNotCaptured` and in the HAR as the
|
|
33
|
+
* content comment.
|
|
34
|
+
*
|
|
35
|
+
* @returns Reason text
|
|
36
|
+
*/
|
|
37
|
+
export function bodyGoneReason() {
|
|
38
|
+
return 'Chrome no longer had the body (its network buffer evicted it, or the request was cancelled)';
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Why a response body is missing when `Network.getResponseBody` failed for
|
|
42
|
+
* another reason (a CDP timeout, the connection closing mid-fetch).
|
|
43
|
+
*
|
|
44
|
+
* @param errorMessage - The error Chrome or the connection gave
|
|
45
|
+
* @returns e.g. `Chrome did not return the body: CDP command timeout`
|
|
46
|
+
*/
|
|
47
|
+
export function bodyFetchFailedReason(errorMessage) {
|
|
48
|
+
return `Chrome did not return the body: ${errorMessage}`;
|
|
26
49
|
}
|
|
27
50
|
/**
|
|
28
51
|
* Note that the session dropped its oldest requests or evicted its oldest
|
|
29
|
-
* response bodies at its limits.
|
|
52
|
+
* request and response bodies at its limits.
|
|
30
53
|
*
|
|
31
54
|
* @param counts - Requests dropped and bodies evicted
|
|
32
55
|
* @returns e.g. `⚠ 2000 older network requests were dropped: bdg keeps the newest 10000`;
|
|
@@ -35,7 +58,7 @@ export function bodyEvictedReason(budgetBytes) {
|
|
|
35
58
|
export function networkEvictedNote(counts) {
|
|
36
59
|
const { requestsDropped, bodiesEvicted } = counts;
|
|
37
60
|
const requests = pluralize(requestsDropped, 'older network request');
|
|
38
|
-
const bodies = pluralize(bodiesEvicted, 'older response body', 'older response bodies');
|
|
61
|
+
const bodies = pluralize(bodiesEvicted, 'older request/response body', 'older request/response bodies');
|
|
39
62
|
const budget = megabytes(MAX_TOTAL_BODY_BYTES);
|
|
40
63
|
if (requestsDropped > 0 && bodiesEvicted > 0) {
|
|
41
64
|
return `⚠ ${requests} dropped, ${bodies} evicted: bdg keeps the newest ${MAX_NETWORK_REQUESTS} requests and ${budget} of bodies`;
|
|
@@ -88,7 +111,7 @@ export function localProxyNote() {
|
|
|
88
111
|
* @returns Comment naming what was redacted and the flag that keeps it
|
|
89
112
|
*/
|
|
90
113
|
export function harSanitizedComment() {
|
|
91
|
-
return 'Sanitized by bdg: values of auth, cookie, API key, token and session headers, cookies, credential query parameters in URLs,
|
|
114
|
+
return 'Sanitized by bdg: values of auth, cookie, API key, token and session headers, cookies, credential query parameters in URLs, password/token/secret fields of JSON and form request and response bodies and WebSocket messages (also truncated JSON, JSON encoded in strings, socket.io, SockJS, server-sent events, NDJSON, and base64 bodies and binary messages that are UTF-8 text), and any JWT (also inside longer strings and form values) are [redacted] in place (by name, so some harmless values are too); everything else stays byte for byte. Not sanitized: other binary data, text that is not JSON or a form, and non-JSON syntax (single quotes, unquoted keys, JSONP, bare values with spaces); a body that could not be sanitized is [redacted] whole. Export with --include-sensitive to keep everything';
|
|
92
115
|
}
|
|
93
116
|
/**
|
|
94
117
|
* Success message of `bdg network har`.
|
|
@@ -99,7 +122,7 @@ export function harSanitizedComment() {
|
|
|
99
122
|
export function harExportedMessage(result) {
|
|
100
123
|
const filterNote = result.filtered ? ' (filtered)' : '';
|
|
101
124
|
const note = result.sanitized
|
|
102
|
-
? 'Credentials sanitized (auth/cookie/API key/token headers, cookies, URL tokens, password and token
|
|
125
|
+
? 'Credentials sanitized (auth/cookie/API key/token headers, cookies, URL tokens, password and token fields of bodies and WebSocket messages are [redacted]); --include-sensitive keeps them'
|
|
103
126
|
: '⚠ Includes credentials (--include-sensitive): share this file with care';
|
|
104
127
|
return `✓ Exported ${result.entries} requests${filterNote} to ${result.file}\n ${note}`;
|
|
105
128
|
}
|
package/dist/utils/async.d.ts
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* Async utilities for common patterns.
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
|
-
* Delay execution for a specified duration.
|
|
5
|
+
* Delay execution for a specified duration, ending early when `signal` aborts.
|
|
6
6
|
*
|
|
7
7
|
* @param ms - Milliseconds to delay
|
|
8
|
+
* @param signal - Optional abort signal (the delay resolves, not rejects, on abort)
|
|
8
9
|
*/
|
|
9
|
-
export declare function delay(ms: number): Promise<void>;
|
|
10
|
+
export declare function delay(ms: number, signal?: AbortSignal): Promise<void>;
|
|
10
11
|
/**
|
|
11
12
|
* Wait for a promise, giving up after a time; the timer is cleared either way.
|
|
12
13
|
*
|
package/dist/utils/async.js
CHANGED
|
@@ -2,12 +2,25 @@
|
|
|
2
2
|
* Async utilities for common patterns.
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
|
-
* Delay execution for a specified duration.
|
|
5
|
+
* Delay execution for a specified duration, ending early when `signal` aborts.
|
|
6
6
|
*
|
|
7
7
|
* @param ms - Milliseconds to delay
|
|
8
|
+
* @param signal - Optional abort signal (the delay resolves, not rejects, on abort)
|
|
8
9
|
*/
|
|
9
|
-
export function delay(ms) {
|
|
10
|
-
return new Promise((resolve) =>
|
|
10
|
+
export function delay(ms, signal) {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
if (signal?.aborted) {
|
|
13
|
+
resolve();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const done = () => {
|
|
17
|
+
clearTimeout(timer);
|
|
18
|
+
signal?.removeEventListener('abort', done);
|
|
19
|
+
resolve();
|
|
20
|
+
};
|
|
21
|
+
const timer = setTimeout(done, ms);
|
|
22
|
+
signal?.addEventListener('abort', done, { once: true });
|
|
23
|
+
});
|
|
11
24
|
}
|
|
12
25
|
/**
|
|
13
26
|
* Wait for a promise, giving up after a time; the timer is cleared either way.
|
package/dist/utils/http.d.ts
CHANGED
|
@@ -37,6 +37,13 @@ export interface FetchCDPTargetsOptions {
|
|
|
37
37
|
* - Network errors are logged to help diagnose Chrome connectivity issues
|
|
38
38
|
*/
|
|
39
39
|
export declare function fetchCDPTargets(port?: number, logger?: Logger, options?: FetchCDPTargetsOptions): Promise<CDPTarget[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Options for a DevTools endpoint probe.
|
|
42
|
+
*/
|
|
43
|
+
export interface DevToolsProbeOptions extends FetchCDPTargetsOptions {
|
|
44
|
+
/** Ends a pending request early (the probe then reports `unreachable`) */
|
|
45
|
+
signal?: AbortSignal | undefined;
|
|
46
|
+
}
|
|
40
47
|
/**
|
|
41
48
|
* What answers on a DevTools HTTP endpoint: a Chrome (with its browser-level
|
|
42
49
|
* WebSocket URL), an HTTP server that is not DevTools, or nothing.
|
|
@@ -54,19 +61,19 @@ export type DevToolsProbe = {
|
|
|
54
61
|
*
|
|
55
62
|
* @param port - Chrome debugging port
|
|
56
63
|
* @param logger - Optional logger for debug output
|
|
57
|
-
* @param options - Host, HTTPS and
|
|
64
|
+
* @param options - Host, HTTPS, request timeout and abort signal
|
|
58
65
|
* @returns What answered
|
|
59
66
|
*/
|
|
60
|
-
export declare function probeDevToolsEndpoint(port: number, logger?: Logger, options?:
|
|
67
|
+
export declare function probeDevToolsEndpoint(port: number, logger?: Logger, options?: DevToolsProbeOptions): Promise<DevToolsProbe>;
|
|
61
68
|
/**
|
|
62
69
|
* The browser-level DevTools WebSocket URL of a Chrome (`/json/version`).
|
|
63
70
|
*
|
|
64
71
|
* @param port - Chrome debugging port
|
|
65
72
|
* @param logger - Optional logger for debug output
|
|
66
|
-
* @param options - Host and
|
|
73
|
+
* @param options - Host, HTTPS and abort signal
|
|
67
74
|
* @returns The URL, or null if no Chrome answered
|
|
68
75
|
*/
|
|
69
|
-
export declare function fetchBrowserWsUrl(port: number, logger?: Logger, options?: Pick<
|
|
76
|
+
export declare function fetchBrowserWsUrl(port: number, logger?: Logger, options?: Pick<DevToolsProbeOptions, 'host' | 'secure' | 'signal'>): Promise<string | null>;
|
|
70
77
|
/**
|
|
71
78
|
* Fetch specific CDP target by ID from Chrome's HTTP API.
|
|
72
79
|
*
|
package/dist/utils/http.js
CHANGED
|
@@ -67,15 +67,17 @@ export async function fetchCDPTargets(port = DEFAULT_CDP_PORT, logger, options)
|
|
|
67
67
|
*
|
|
68
68
|
* @param port - Chrome debugging port
|
|
69
69
|
* @param logger - Optional logger for debug output
|
|
70
|
-
* @param options - Host, HTTPS and
|
|
70
|
+
* @param options - Host, HTTPS, request timeout and abort signal
|
|
71
71
|
* @returns What answered
|
|
72
72
|
*/
|
|
73
73
|
export async function probeDevToolsEndpoint(port, logger, options) {
|
|
74
74
|
const url = `${options?.secure ? 'https' : 'http'}://${options?.host ?? HTTP_LOCALHOST}:${port}/json/version`;
|
|
75
75
|
const timeoutMs = options?.timeoutMs ?? CDP_HTTP_TIMEOUT_MS;
|
|
76
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
77
|
+
const signal = options?.signal ? AbortSignal.any([timeout, options.signal]) : timeout;
|
|
76
78
|
let response;
|
|
77
79
|
try {
|
|
78
|
-
response = await fetch(url, { signal
|
|
80
|
+
response = await fetch(url, { signal });
|
|
79
81
|
}
|
|
80
82
|
catch (error) {
|
|
81
83
|
logger?.debug(`Chrome version request failed: ${getErrorMessage(error)} (${url})`);
|
|
@@ -97,7 +99,7 @@ export async function probeDevToolsEndpoint(port, logger, options) {
|
|
|
97
99
|
*
|
|
98
100
|
* @param port - Chrome debugging port
|
|
99
101
|
* @param logger - Optional logger for debug output
|
|
100
|
-
* @param options - Host and
|
|
102
|
+
* @param options - Host, HTTPS and abort signal
|
|
101
103
|
* @returns The URL, or null if no Chrome answered
|
|
102
104
|
*/
|
|
103
105
|
export async function fetchBrowserWsUrl(port, logger, options) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browser-debugger-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.16.0",
|
|
4
|
+
"description": "Let Claude Code and other coding agents drive and debug Chrome from the shell. DOM, network, console and raw CDP as short commands, no screenshots and no MCP needed.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"bdg": "dist/index.js"
|
|
@@ -37,10 +37,24 @@
|
|
|
37
37
|
"keywords": [
|
|
38
38
|
"cdp",
|
|
39
39
|
"chrome-devtools-protocol",
|
|
40
|
+
"chrome",
|
|
41
|
+
"devtools",
|
|
42
|
+
"debugging",
|
|
40
43
|
"browser",
|
|
41
|
-
"
|
|
44
|
+
"browser-automation",
|
|
45
|
+
"headless-chrome",
|
|
42
46
|
"cli",
|
|
43
|
-
"
|
|
47
|
+
"claude",
|
|
48
|
+
"claude-code",
|
|
49
|
+
"coding-agent",
|
|
50
|
+
"ai-agent",
|
|
51
|
+
"agent-skill",
|
|
52
|
+
"codex",
|
|
53
|
+
"css",
|
|
54
|
+
"figma",
|
|
55
|
+
"telemetry",
|
|
56
|
+
"puppeteer-alternative",
|
|
57
|
+
"playwright-alternative"
|
|
44
58
|
],
|
|
45
59
|
"license": "MIT",
|
|
46
60
|
"dependencies": {
|
|
File without changes
|
|
File without changes
|