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.
- package/.claude/skills/bdg/SKILL.md +4 -4
- package/README.md +143 -79
- package/dist/commands/cdp.d.ts +22 -1
- package/dist/commands/cdp.js +100 -43
- package/dist/commands/console.d.ts +12 -0
- package/dist/commands/console.js +62 -12
- package/dist/commands/css.d.ts +13 -0
- package/dist/commands/css.js +53 -0
- package/dist/commands/dom/DomElementResolver.d.ts +3 -1
- package/dist/commands/dom/DomElementResolver.js +10 -3
- package/dist/commands/dom/a11y.js +3 -2
- package/dist/commands/dom/audit.d.ts +14 -0
- package/dist/commands/dom/audit.js +87 -0
- package/dist/commands/dom/eval.d.ts +3 -2
- package/dist/commands/dom/eval.js +11 -5
- package/dist/commands/dom/form.js +10 -9
- package/dist/commands/dom/formInteraction.js +42 -11
- package/dist/commands/dom/get.js +8 -8
- package/dist/commands/dom/helpers/index.d.ts +1 -1
- package/dist/commands/dom/helpers/index.js +1 -1
- package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
- package/dist/commands/dom/helpers/keyAttributes.js +6 -4
- package/dist/commands/dom/helpers/query.d.ts +27 -3
- package/dist/commands/dom/helpers/query.js +152 -64
- package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
- package/dist/commands/dom/helpers/screenshot.js +169 -49
- package/dist/commands/dom/index.js +7 -2
- package/dist/commands/dom/query.d.ts +19 -2
- package/dist/commands/dom/query.js +37 -6
- package/dist/commands/dom/screenshot.js +12 -7
- package/dist/commands/dom/semanticUtils.d.ts +3 -2
- package/dist/commands/dom/semanticUtils.js +40 -9
- package/dist/commands/dom/wait.js +5 -3
- package/dist/commands/helpJson.d.ts +82 -19
- package/dist/commands/helpJson.js +112 -41
- package/dist/commands/helpTopic.d.ts +16 -1
- package/dist/commands/helpTopic.js +59 -1
- package/dist/commands/installSkill.d.ts +15 -5
- package/dist/commands/installSkill.js +86 -16
- package/dist/commands/network/list.js +22 -12
- package/dist/commands/optionBehaviors.js +53 -16
- package/dist/commands/page.js +7 -4
- package/dist/commands/peek.d.ts +7 -0
- package/dist/commands/peek.js +65 -23
- package/dist/commands/shared/daemonErrorHandler.d.ts +5 -2
- package/dist/commands/shared/daemonErrorHandler.js +20 -9
- package/dist/commands/shared/dataFetcher.d.ts +12 -4
- package/dist/commands/shared/dataFetcher.js +12 -4
- package/dist/commands/shared/followMode.d.ts +9 -1
- package/dist/commands/shared/followMode.js +22 -4
- package/dist/commands/shared/optionTypes.d.ts +9 -2
- package/dist/commands/shared/outputFile.js +6 -1
- package/dist/commands/start.d.ts +20 -5
- package/dist/commands/start.js +84 -23
- package/dist/commands/stop.d.ts +11 -0
- package/dist/commands/stop.js +24 -1
- package/dist/commands/tail.d.ts +7 -1
- package/dist/commands/tail.js +13 -62
- package/dist/commands.js +2 -0
- package/dist/connection/cdp.d.ts +7 -0
- package/dist/connection/cdp.js +9 -0
- package/dist/connection/launcher.js +3 -2
- package/dist/daemon/SessionController.js +6 -1
- package/dist/daemon/launcher.d.ts +3 -2
- package/dist/daemon/launcher.js +47 -3
- package/dist/daemon/session/Session.d.ts +4 -1
- package/dist/daemon/session/Session.js +33 -2
- package/dist/daemon/session/TelemetryStore.d.ts +8 -1
- package/dist/daemon/session/TelemetryStore.js +13 -1
- package/dist/daemon/session/commandRegistry.js +36 -14
- package/dist/daemon/session/interactions.d.ts +2 -1
- package/dist/daemon/session/interactions.js +13 -1
- package/dist/daemon/session/plugins.js +19 -53
- package/dist/daemon/session/teardown.js +1 -1
- package/dist/daemon.js +9234 -7222
- package/dist/errors/messages.d.ts +88 -15
- package/dist/errors/messages.js +177 -27
- package/dist/index.js +19322 -13961
- package/dist/ipc/client.d.ts +22 -2
- package/dist/ipc/client.js +34 -5
- package/dist/ipc/protocol/auditTypes.d.ts +135 -0
- package/dist/ipc/protocol/auditTypes.js +6 -0
- package/dist/ipc/protocol/commands.d.ts +35 -0
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +16 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +73 -8
- package/dist/ipc/session/types.d.ts +2 -0
- package/dist/runtime/css/search.d.ts +39 -0
- package/dist/runtime/css/search.js +122 -0
- package/dist/runtime/dom/actionEffects.d.ts +9 -2
- package/dist/runtime/dom/actionEffects.js +30 -14
- package/dist/runtime/dom/audit.d.ts +19 -0
- package/dist/runtime/dom/audit.js +37 -0
- package/dist/runtime/dom/auditModel.d.ts +45 -0
- package/dist/runtime/dom/auditModel.js +220 -0
- package/dist/runtime/dom/auditScripts.d.ts +113 -0
- package/dist/runtime/dom/auditScripts.js +148 -0
- package/dist/runtime/dom/elementGeometry.d.ts +16 -3
- package/dist/runtime/dom/elementGeometry.js +49 -10
- package/dist/runtime/dom/elementInfo.d.ts +74 -17
- package/dist/runtime/dom/elementInfo.js +187 -34
- package/dist/runtime/dom/evalHelpers.d.ts +12 -2
- package/dist/runtime/dom/evalHelpers.js +67 -7
- package/dist/runtime/dom/formDiscovery.d.ts +6 -2
- package/dist/runtime/dom/formDiscovery.js +20 -3
- package/dist/runtime/dom/formFillHelpers/fill.js +8 -12
- package/dist/runtime/dom/formFillHelpers/pressKey.js +2 -2
- package/dist/runtime/dom/formFillHelpers/shared.d.ts +16 -10
- package/dist/runtime/dom/formFillHelpers/shared.js +19 -52
- package/dist/runtime/dom/formSubmitHelpers.js +4 -3
- package/dist/runtime/dom/frameLayout.js +1 -0
- package/dist/runtime/dom/inspect.d.ts +7 -0
- package/dist/runtime/dom/inspect.js +92 -28
- package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
- package/dist/runtime/dom/inspectAllStyles.js +90 -7
- package/dist/runtime/dom/inspectCascade.d.ts +19 -2
- package/dist/runtime/dom/inspectCascade.js +214 -44
- package/dist/runtime/dom/inspectCascadeModel.d.ts +8 -0
- package/dist/runtime/dom/inspectCascadeModel.js +108 -34
- package/dist/runtime/dom/inspectHints.d.ts +26 -3
- package/dist/runtime/dom/inspectHints.js +125 -9
- package/dist/runtime/dom/inspectModel.d.ts +5 -1
- package/dist/runtime/dom/inspectModel.js +37 -10
- package/dist/runtime/dom/inspectPaintModel.d.ts +50 -22
- package/dist/runtime/dom/inspectPaintModel.js +182 -68
- package/dist/runtime/dom/inspectRules.d.ts +19 -0
- package/dist/runtime/dom/inspectRules.js +21 -5
- package/dist/runtime/dom/inspectScripts.d.ts +112 -12
- package/dist/runtime/dom/inspectScripts.js +357 -32
- package/dist/runtime/dom/inspectTree.js +10 -2
- package/dist/runtime/dom/inspectWhyModel.d.ts +2 -1
- package/dist/runtime/dom/inspectWhyModel.js +52 -10
- package/dist/runtime/dom/layout.js +40 -16
- package/dist/runtime/dom/reactEventHelpers.d.ts +21 -4
- package/dist/runtime/dom/reactEventHelpers.js +90 -36
- package/dist/runtime/dom/targetNode.d.ts +18 -5
- package/dist/runtime/dom/targetNode.js +268 -8
- package/dist/runtime/dom/wait.js +2 -1
- package/dist/runtime/page/bdgWorld.d.ts +57 -0
- package/dist/runtime/page/bdgWorld.js +180 -0
- package/dist/runtime/page/emulation.d.ts +13 -4
- package/dist/runtime/page/emulation.js +69 -4
- package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
- package/dist/runtime/page/replacedBuiltins.js +136 -0
- package/dist/runtime/page/userAgent.d.ts +17 -0
- package/dist/runtime/page/userAgent.js +57 -0
- package/dist/session/QueryCacheManager.d.ts +4 -1
- package/dist/session/QueryCacheManager.js +5 -2
- package/dist/session/chrome.d.ts +4 -1
- package/dist/session/chrome.js +7 -1
- package/dist/session/cleanup/staleSession.d.ts +21 -4
- package/dist/session/cleanup/staleSession.js +79 -9
- package/dist/session/cleanup/userCommands.d.ts +4 -1
- package/dist/session/cleanup/userCommands.js +10 -5
- package/dist/session/daemonSocket.d.ts +10 -0
- package/dist/session/daemonSocket.js +22 -0
- package/dist/session/lastSession.d.ts +6 -3
- package/dist/session/lastSession.js +11 -5
- package/dist/session/paths.d.ts +3 -1
- package/dist/session/paths.js +5 -5
- package/dist/session/portClaims.js +4 -3
- package/dist/session/sessionList.d.ts +13 -5
- package/dist/session/sessionList.js +31 -7
- package/dist/telemetry/a11y.js +2 -2
- package/dist/telemetry/console.d.ts +2 -1
- package/dist/telemetry/console.js +30 -21
- package/dist/telemetry/pageCrash.d.ts +26 -0
- package/dist/telemetry/pageCrash.js +53 -0
- package/dist/types.d.ts +20 -0
- package/dist/ui/formatters/audit.d.ts +19 -0
- package/dist/ui/formatters/audit.js +115 -0
- package/dist/ui/formatters/cdp.d.ts +138 -0
- package/dist/ui/formatters/cdp.js +131 -0
- package/dist/ui/formatters/console/chronological.js +3 -1
- package/dist/ui/formatters/console/follow.d.ts +2 -1
- package/dist/ui/formatters/console/follow.js +2 -2
- package/dist/ui/formatters/console/json.d.ts +2 -2
- package/dist/ui/formatters/console/json.js +11 -5
- package/dist/ui/formatters/console/shared.d.ts +30 -0
- package/dist/ui/formatters/console/shared.js +16 -0
- package/dist/ui/formatters/console/summarize.d.ts +9 -2
- package/dist/ui/formatters/console/summarize.js +40 -9
- package/dist/ui/formatters/console.d.ts +2 -1
- package/dist/ui/formatters/console.js +7 -5
- package/dist/ui/formatters/details.js +3 -1
- package/dist/ui/formatters/dom.d.ts +2 -2
- package/dist/ui/formatters/dom.js +10 -8
- package/dist/ui/formatters/helpFormatters.js +1 -1
- package/dist/ui/formatters/inspect.js +50 -17
- package/dist/ui/formatters/installSkill.d.ts +9 -1
- package/dist/ui/formatters/installSkill.js +32 -6
- package/dist/ui/formatters/layout.js +2 -1
- package/dist/ui/formatters/networkList.d.ts +1 -1
- package/dist/ui/formatters/networkList.js +1 -2
- package/dist/ui/formatters/preview.d.ts +2 -0
- package/dist/ui/formatters/preview.js +17 -7
- package/dist/ui/formatters/sessions.d.ts +2 -2
- package/dist/ui/formatters/sessions.js +9 -2
- package/dist/ui/formatters/status.js +1 -1
- package/dist/ui/logging/logger.d.ts +1 -1
- package/dist/ui/messages/commands.d.ts +168 -11
- package/dist/ui/messages/commands.js +245 -18
- package/dist/ui/messages/consoleMessages.d.ts +24 -0
- package/dist/ui/messages/consoleMessages.js +32 -0
- package/dist/ui/messages/preview.d.ts +12 -0
- package/dist/ui/messages/preview.js +18 -2
- package/dist/ui/messages/session.d.ts +13 -2
- package/dist/ui/messages/session.js +22 -3
- package/dist/utils/cssValues.js +36 -4
- package/dist/utils/decisionTrees.js +0 -5
- package/dist/utils/directories.d.ts +34 -0
- package/dist/utils/directories.js +88 -0
- package/dist/utils/display.d.ts +16 -0
- package/dist/utils/display.js +42 -0
- package/dist/utils/exitCodes.d.ts +1 -0
- package/dist/utils/exitCodes.js +6 -0
- package/dist/utils/process.d.ts +12 -0
- package/dist/utils/process.js +25 -0
- package/dist/utils/suggestions.d.ts +4 -2
- package/dist/utils/suggestions.js +7 -5
- package/dist/utils/taskMappings.js +1 -1
- package/package.json +3 -2
|
@@ -9,9 +9,13 @@
|
|
|
9
9
|
/**
|
|
10
10
|
* Remove per-session files (metadata, query cache).
|
|
11
11
|
*
|
|
12
|
-
* Called by the daemon when its session ends, and by `bdg cleanup`.
|
|
12
|
+
* Called by the daemon when its session ends, and by `bdg cleanup`. A daemon
|
|
13
|
+
* passes its PID, so it never removes the files of another daemon that
|
|
14
|
+
* started in the same directory meanwhile.
|
|
15
|
+
*
|
|
16
|
+
* @param ownerPid - The daemon whose files these must be; omitted by cleanup
|
|
13
17
|
*/
|
|
14
|
-
export declare function removeSessionFiles(): void;
|
|
18
|
+
export declare function removeSessionFiles(ownerPid?: number): void;
|
|
15
19
|
/**
|
|
16
20
|
* Remove daemon and session files if no daemon is listening.
|
|
17
21
|
*
|
|
@@ -43,8 +47,21 @@ export declare function isSessionChrome(pid: number, sessionDir: string): boolea
|
|
|
43
47
|
*/
|
|
44
48
|
export declare function killOrphanedChrome(): boolean;
|
|
45
49
|
/**
|
|
46
|
-
* Kill
|
|
47
|
-
*
|
|
50
|
+
* Kill every Chrome launched for a session directory (its marker flag on the
|
|
51
|
+
* command line), whatever chrome.pid says: a Chrome whose PID file another
|
|
52
|
+
* daemon removed, or that a second start left behind, is found too. Only
|
|
53
|
+
* browser processes are signalled; their helper processes exit with them.
|
|
54
|
+
*
|
|
55
|
+
* @param sessionDir - Session directory
|
|
56
|
+
* @returns PIDs of the processes killed
|
|
57
|
+
*/
|
|
58
|
+
export declare function killSessionChromes(sessionDir?: string): number[];
|
|
59
|
+
/**
|
|
60
|
+
* Kill the Chromes this session left behind and wait until they have exited,
|
|
61
|
+
* so their profile and debugging port are free for the next launch. The
|
|
62
|
+
* Chrome recorded in chrome.pid is killed (see {@link killOrphanedChrome});
|
|
63
|
+
* without a record (chrome.pid lost with its daemon), every Chrome carrying
|
|
64
|
+
* the session's marker is, unless another daemon of the session still runs.
|
|
48
65
|
*
|
|
49
66
|
* @returns True if a Chrome process was killed
|
|
50
67
|
*/
|
|
@@ -10,13 +10,14 @@ import { chromeSessionMarkerFlag } from '../../connection/launcher/flagsBuilder.
|
|
|
10
10
|
import { QueryCacheManager } from '../QueryCacheManager.js';
|
|
11
11
|
import { clearChromePid, readChromePid } from '../chrome.js';
|
|
12
12
|
import { probeDaemonSocket } from '../daemonSocket.js';
|
|
13
|
+
import { readSessionMetadata } from '../metadata.js';
|
|
13
14
|
import { getSessionDir, getSessionFilePath, sessionFilePathIn } from '../paths.js';
|
|
14
15
|
import { readPidFromFile } from '../pid.js';
|
|
15
16
|
import { createLogger, logDebugError } from '../../ui/logging/index.js';
|
|
16
17
|
import { delay } from '../../utils/async.js';
|
|
17
18
|
import { safeRemoveFile } from '../../utils/file.js';
|
|
18
19
|
import { DAEMON_SCRIPT_PATH } from '../../utils/packageRoot.js';
|
|
19
|
-
import { getProcessCommand, isProcessAlive, killChromeProcess } from '../../utils/process.js';
|
|
20
|
+
import { getProcessCommand, isProcessAlive, killChromeProcess, listProcesses, } from '../../utils/process.js';
|
|
20
21
|
const log = createLogger('cleanup');
|
|
21
22
|
/**
|
|
22
23
|
* Check whether a process command line contains an exact argument.
|
|
@@ -30,12 +31,39 @@ function hasArgument(command, arg) {
|
|
|
30
31
|
return false;
|
|
31
32
|
return command.includes(`${arg} `) || command.endsWith(arg);
|
|
32
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Check whether a command line contains an exact argument, followed by the
|
|
36
|
+
* end or by another option: a session directory with a space in it
|
|
37
|
+
* (`/tmp/a b`) does not match the marker of `/tmp/a`.
|
|
38
|
+
*
|
|
39
|
+
* @param command - Full command line
|
|
40
|
+
* @param arg - Argument to look for
|
|
41
|
+
* @returns True if `arg` appears as a whole argument
|
|
42
|
+
*/
|
|
43
|
+
function hasWholeArgument(command, arg) {
|
|
44
|
+
for (let at = command.indexOf(arg); at !== -1; at = command.indexOf(arg, at + 1)) {
|
|
45
|
+
const before = at === 0 || command[at - 1] === ' ';
|
|
46
|
+
const rest = command.slice(at + arg.length);
|
|
47
|
+
if (before && (rest === '' || rest.startsWith(' -')))
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
33
52
|
/**
|
|
34
53
|
* Remove per-session files (metadata, query cache).
|
|
35
54
|
*
|
|
36
|
-
* Called by the daemon when its session ends, and by `bdg cleanup`.
|
|
55
|
+
* Called by the daemon when its session ends, and by `bdg cleanup`. A daemon
|
|
56
|
+
* passes its PID, so it never removes the files of another daemon that
|
|
57
|
+
* started in the same directory meanwhile.
|
|
58
|
+
*
|
|
59
|
+
* @param ownerPid - The daemon whose files these must be; omitted by cleanup
|
|
37
60
|
*/
|
|
38
|
-
export function removeSessionFiles() {
|
|
61
|
+
export function removeSessionFiles(ownerPid) {
|
|
62
|
+
if (ownerPid !== undefined) {
|
|
63
|
+
const owner = readSessionMetadata()?.bdgPid;
|
|
64
|
+
if (owner !== undefined && owner !== ownerPid)
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
39
67
|
safeRemoveFile(getSessionFilePath('METADATA'), 'metadata file', log);
|
|
40
68
|
void QueryCacheManager.getInstance()
|
|
41
69
|
.clear()
|
|
@@ -103,22 +131,64 @@ export function killOrphanedChrome() {
|
|
|
103
131
|
clearChromePid();
|
|
104
132
|
return true;
|
|
105
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Kill every Chrome launched for a session directory (its marker flag on the
|
|
136
|
+
* command line), whatever chrome.pid says: a Chrome whose PID file another
|
|
137
|
+
* daemon removed, or that a second start left behind, is found too. Only
|
|
138
|
+
* browser processes are signalled; their helper processes exit with them.
|
|
139
|
+
*
|
|
140
|
+
* @param sessionDir - Session directory
|
|
141
|
+
* @returns PIDs of the processes killed
|
|
142
|
+
*/
|
|
143
|
+
export function killSessionChromes(sessionDir = getSessionDir()) {
|
|
144
|
+
const marker = chromeSessionMarkerFlag(sessionDir);
|
|
145
|
+
const killed = [];
|
|
146
|
+
for (const { pid, command } of listProcesses()) {
|
|
147
|
+
if (pid === process.pid || !hasWholeArgument(command, marker) || / --type=/.test(command)) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
killChromeProcess(pid, 'SIGKILL');
|
|
152
|
+
killed.push(pid);
|
|
153
|
+
log.info(`Killed Chrome of this session (PID ${pid})`);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
logDebugError(log, `kill Chrome ${pid}`, error);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (killed.length > 0)
|
|
160
|
+
clearChromePid();
|
|
161
|
+
return killed;
|
|
162
|
+
}
|
|
106
163
|
/** How long to wait for a killed orphaned Chrome to exit (and free its port) */
|
|
107
164
|
const ORPHAN_EXIT_WAIT_MS = 5000;
|
|
108
165
|
/**
|
|
109
|
-
* Kill
|
|
110
|
-
*
|
|
166
|
+
* Kill the Chromes this session left behind and wait until they have exited,
|
|
167
|
+
* so their profile and debugging port are free for the next launch. The
|
|
168
|
+
* Chrome recorded in chrome.pid is killed (see {@link killOrphanedChrome});
|
|
169
|
+
* without a record (chrome.pid lost with its daemon), every Chrome carrying
|
|
170
|
+
* the session's marker is, unless another daemon of the session still runs.
|
|
111
171
|
*
|
|
112
172
|
* @returns True if a Chrome process was killed
|
|
113
173
|
*/
|
|
114
174
|
export async function reapOrphanedChrome() {
|
|
115
175
|
const chromePid = findOrphanedChrome();
|
|
116
|
-
|
|
117
|
-
return false;
|
|
176
|
+
const killed = chromePid !== null ? (killOrphanedChrome() ? [chromePid] : []) : killUnrecordedChromes();
|
|
118
177
|
const deadline = Date.now() + ORPHAN_EXIT_WAIT_MS;
|
|
119
|
-
while (isProcessAlive
|
|
178
|
+
while (killed.some(isProcessAlive) && Date.now() < deadline)
|
|
120
179
|
await delay(50);
|
|
121
|
-
return
|
|
180
|
+
return killed.length > 0;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Kill the session's Chromes found by their marker (see
|
|
184
|
+
* {@link killSessionChromes}), unless a daemon of the session other than
|
|
185
|
+
* this process still runs: they could be its Chrome.
|
|
186
|
+
*
|
|
187
|
+
* @returns PIDs of the processes killed
|
|
188
|
+
*/
|
|
189
|
+
function killUnrecordedChromes() {
|
|
190
|
+
const daemonPid = readLiveDaemonPid();
|
|
191
|
+
return daemonPid === null || daemonPid === process.pid ? killSessionChromes() : [];
|
|
122
192
|
}
|
|
123
193
|
/**
|
|
124
194
|
* Read the PID of a live bdg daemon from daemon.pid.
|
|
@@ -27,7 +27,10 @@ export interface SessionCleanupResult {
|
|
|
27
27
|
*
|
|
28
28
|
* With `force`, a live daemon is killed first (after verifying its command
|
|
29
29
|
* line). Then stale daemon files are removed and an orphaned bdg Chrome, if
|
|
30
|
-
* any, is killed
|
|
30
|
+
* any, is killed, and, once no daemon of the session runs, every other
|
|
31
|
+
* Chrome launched for this session directory (one chrome.pid lost track of).
|
|
32
|
+
* The record of a session that ended without `bdg stop` is removed too, and
|
|
33
|
+
* counts as cleaned session files.
|
|
31
34
|
*
|
|
32
35
|
* @param options - Cleanup options
|
|
33
36
|
* @returns What was cleaned, plus warnings
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Cleanup behind the user-facing `bdg cleanup` command.
|
|
3
3
|
*/
|
|
4
4
|
import * as fs from 'fs';
|
|
5
|
-
import { killOrphanedChrome, readLiveDaemonPid, removeStaleDaemonFiles, } from './staleSession.js';
|
|
5
|
+
import { killOrphanedChrome, killSessionChromes, readLiveDaemonPid, removeStaleDaemonFiles, } from './staleSession.js';
|
|
6
6
|
import { isDaemonAlive } from '../daemonSocket.js';
|
|
7
7
|
import { clearLastSessionEnd } from '../lastSession.js';
|
|
8
8
|
import { SESSION_STATE_FILES, getSessionFilePath } from '../paths.js';
|
|
@@ -15,7 +15,10 @@ const DAEMON_EXIT_WAIT_MS = 2000;
|
|
|
15
15
|
*
|
|
16
16
|
* With `force`, a live daemon is killed first (after verifying its command
|
|
17
17
|
* line). Then stale daemon files are removed and an orphaned bdg Chrome, if
|
|
18
|
-
* any, is killed
|
|
18
|
+
* any, is killed, and, once no daemon of the session runs, every other
|
|
19
|
+
* Chrome launched for this session directory (one chrome.pid lost track of).
|
|
20
|
+
* The record of a session that ended without `bdg stop` is removed too, and
|
|
21
|
+
* counts as cleaned session files.
|
|
19
22
|
*
|
|
20
23
|
* @param options - Cleanup options
|
|
21
24
|
* @returns What was cleaned, plus warnings
|
|
@@ -25,13 +28,15 @@ export async function performSessionCleanup(options) {
|
|
|
25
28
|
const filesBefore = countSessionFiles();
|
|
26
29
|
const daemonKilled = options.force ? await killLiveDaemon(warnings) : false;
|
|
27
30
|
const session = await removeStaleDaemonFiles();
|
|
28
|
-
const
|
|
31
|
+
const orphan = killOrphanedChrome();
|
|
32
|
+
const unrecorded = readLiveDaemonPid() === null ? killSessionChromes() : [];
|
|
33
|
+
const chrome = orphan || unrecorded.length > 0;
|
|
29
34
|
const output = options.removeOutput ? removeOutputFile(warnings) : false;
|
|
30
35
|
const filesRemoved = countSessionFiles() < filesBefore;
|
|
31
|
-
clearLastSessionEnd();
|
|
36
|
+
const endedRecordRemoved = clearLastSessionEnd();
|
|
32
37
|
return {
|
|
33
38
|
cleaned: {
|
|
34
|
-
session: session || daemonKilled || filesRemoved,
|
|
39
|
+
session: session || daemonKilled || filesRemoved || endedRecordRemoved,
|
|
35
40
|
chrome,
|
|
36
41
|
daemons: daemonKilled,
|
|
37
42
|
output,
|
|
@@ -22,4 +22,14 @@ export declare function probeDaemonSocket(socketPath?: string, timeoutMs?: numbe
|
|
|
22
22
|
* @returns True if the daemon socket accepts a connection
|
|
23
23
|
*/
|
|
24
24
|
export declare function isDaemonAlive(): Promise<boolean>;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the daemon socket certainly cannot be reached: there is no socket
|
|
27
|
+
* file, or connecting is refused. A daemon that is only slow to accept (busy,
|
|
28
|
+
* a timeout) does not count, so it is never taken for a dead one.
|
|
29
|
+
*
|
|
30
|
+
* @param socketPath - Socket path (defaults to the session's daemon socket)
|
|
31
|
+
* @param timeoutMs - Connection timeout
|
|
32
|
+
* @returns True when the socket is missing or refuses connections
|
|
33
|
+
*/
|
|
34
|
+
export declare function isDaemonSocketGone(socketPath?: string, timeoutMs?: number): Promise<boolean>;
|
|
25
35
|
//# sourceMappingURL=daemonSocket.d.ts.map
|
|
@@ -37,4 +37,26 @@ export function probeDaemonSocket(socketPath = getSessionFilePath('DAEMON_SOCKET
|
|
|
37
37
|
export async function isDaemonAlive() {
|
|
38
38
|
return (await probeDaemonSocket()) === 'alive';
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Whether the daemon socket certainly cannot be reached: there is no socket
|
|
42
|
+
* file, or connecting is refused. A daemon that is only slow to accept (busy,
|
|
43
|
+
* a timeout) does not count, so it is never taken for a dead one.
|
|
44
|
+
*
|
|
45
|
+
* @param socketPath - Socket path (defaults to the session's daemon socket)
|
|
46
|
+
* @param timeoutMs - Connection timeout
|
|
47
|
+
* @returns True when the socket is missing or refuses connections
|
|
48
|
+
*/
|
|
49
|
+
export function isDaemonSocketGone(socketPath = getSessionFilePath('DAEMON_SOCKET'), timeoutMs = PROBE_TIMEOUT_MS) {
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
const socket = net.createConnection(socketPath);
|
|
52
|
+
const finish = (gone) => {
|
|
53
|
+
clearTimeout(timer);
|
|
54
|
+
socket.destroy();
|
|
55
|
+
resolve(gone);
|
|
56
|
+
};
|
|
57
|
+
const timer = setTimeout(() => finish(false), timeoutMs);
|
|
58
|
+
socket.once('connect', () => finish(false));
|
|
59
|
+
socket.once('error', (error) => finish(error.code === 'ENOENT' || error.code === 'ECONNREFUSED'));
|
|
60
|
+
});
|
|
61
|
+
}
|
|
40
62
|
//# sourceMappingURL=daemonSocket.js.map
|
|
@@ -18,12 +18,15 @@ export interface LastSessionEnd {
|
|
|
18
18
|
export declare function writeLastSessionEnd(reason: UnexpectedEndReason): void;
|
|
19
19
|
/**
|
|
20
20
|
* Forget the last session's end (a new session is starting, or `cleanup` ran).
|
|
21
|
+
*
|
|
22
|
+
* @returns True if there was a record to remove
|
|
21
23
|
*/
|
|
22
|
-
export declare function clearLastSessionEnd():
|
|
24
|
+
export declare function clearLastSessionEnd(): boolean;
|
|
23
25
|
/**
|
|
24
|
-
* How the last session ended, if it ended unexpectedly.
|
|
26
|
+
* How the last session of a directory ended, if it ended unexpectedly.
|
|
25
27
|
*
|
|
28
|
+
* @param dir - Session directory (default: the selected session's)
|
|
26
29
|
* @returns The record, or null
|
|
27
30
|
*/
|
|
28
|
-
export declare function readLastSessionEnd(): LastSessionEnd | null;
|
|
31
|
+
export declare function readLastSessionEnd(dir?: string): LastSessionEnd | null;
|
|
29
32
|
//# sourceMappingURL=lastSession.d.ts.map
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* `bdg status` can explain why there is no session any more.
|
|
4
4
|
*/
|
|
5
5
|
import * as fs from 'fs';
|
|
6
|
-
import { getSessionFilePath } from './paths.js';
|
|
6
|
+
import { getSessionDir, getSessionFilePath, sessionFilePathIn } from './paths.js';
|
|
7
7
|
import { createLogger } from '../ui/logging/index.js';
|
|
8
8
|
import { AtomicFileWriter } from '../utils/atomicFile.js';
|
|
9
9
|
import { getErrorMessage } from '../utils/errors.js';
|
|
@@ -23,18 +23,24 @@ export function writeLastSessionEnd(reason) {
|
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Forget the last session's end (a new session is starting, or `cleanup` ran).
|
|
26
|
+
*
|
|
27
|
+
* @returns True if there was a record to remove
|
|
26
28
|
*/
|
|
27
29
|
export function clearLastSessionEnd() {
|
|
28
|
-
|
|
30
|
+
const file = getSessionFilePath('LAST_SESSION');
|
|
31
|
+
const existed = fs.existsSync(file);
|
|
32
|
+
fs.rmSync(file, { force: true });
|
|
33
|
+
return existed;
|
|
29
34
|
}
|
|
30
35
|
/**
|
|
31
|
-
* How the last session ended, if it ended unexpectedly.
|
|
36
|
+
* How the last session of a directory ended, if it ended unexpectedly.
|
|
32
37
|
*
|
|
38
|
+
* @param dir - Session directory (default: the selected session's)
|
|
33
39
|
* @returns The record, or null
|
|
34
40
|
*/
|
|
35
|
-
export function readLastSessionEnd() {
|
|
41
|
+
export function readLastSessionEnd(dir = getSessionDir()) {
|
|
36
42
|
try {
|
|
37
|
-
const data = JSON.parse(fs.readFileSync(
|
|
43
|
+
const data = JSON.parse(fs.readFileSync(sessionFilePathIn(dir, 'LAST_SESSION'), 'utf8'));
|
|
38
44
|
return typeof data.endedAt === 'number' ? data : null;
|
|
39
45
|
}
|
|
40
46
|
catch (error) {
|
package/dist/session/paths.d.ts
CHANGED
|
@@ -124,8 +124,10 @@ export declare function getDaemonSocketPath(): string;
|
|
|
124
124
|
* Ensure the session directory exists.
|
|
125
125
|
*
|
|
126
126
|
* Creates ~/.bdg/ if it doesn't exist. Safe to call multiple times (idempotent).
|
|
127
|
+
* A path that cannot hold a directory is refused before `mkdir`, which
|
|
128
|
+
* would spin on a pseudo-filesystem ({@link makeDirectory}).
|
|
127
129
|
*
|
|
128
|
-
* @throws Error if directory
|
|
130
|
+
* @throws Error if the directory cannot be created
|
|
129
131
|
*/
|
|
130
132
|
export declare function ensureSessionDir(): void;
|
|
131
133
|
export {};
|
package/dist/session/paths.js
CHANGED
|
@@ -9,6 +9,7 @@ import * as fs from 'fs';
|
|
|
9
9
|
import * as os from 'os';
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import { createLogger, logDebugError } from '../ui/logging/index.js';
|
|
12
|
+
import { makeDirectory } from '../utils/directories.js';
|
|
12
13
|
const log = createLogger('session');
|
|
13
14
|
/**
|
|
14
15
|
* Session file paths relative to ~/.bdg/
|
|
@@ -166,13 +167,12 @@ export function getDaemonSocketPath() {
|
|
|
166
167
|
* Ensure the session directory exists.
|
|
167
168
|
*
|
|
168
169
|
* Creates ~/.bdg/ if it doesn't exist. Safe to call multiple times (idempotent).
|
|
170
|
+
* A path that cannot hold a directory is refused before `mkdir`, which
|
|
171
|
+
* would spin on a pseudo-filesystem ({@link makeDirectory}).
|
|
169
172
|
*
|
|
170
|
-
* @throws Error if directory
|
|
173
|
+
* @throws Error if the directory cannot be created
|
|
171
174
|
*/
|
|
172
175
|
export function ensureSessionDir() {
|
|
173
|
-
|
|
174
|
-
if (!fs.existsSync(dir)) {
|
|
175
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
176
|
-
}
|
|
176
|
+
makeDirectory(getSessionDir());
|
|
177
177
|
}
|
|
178
178
|
//# sourceMappingURL=paths.js.map
|
|
@@ -20,6 +20,7 @@ import * as path from 'path';
|
|
|
20
20
|
import { getSessionBaseDir, getSessionDir, listSessionDirs, sessionFilePathIn, } from './paths.js';
|
|
21
21
|
import { createLogger, logDebugError } from '../ui/logging/index.js';
|
|
22
22
|
import { delay } from '../utils/async.js';
|
|
23
|
+
import { makeDirectory } from '../utils/directories.js';
|
|
23
24
|
const log = createLogger('session');
|
|
24
25
|
/** Lock file guarding port selection, in the port registry (or base session) directory */
|
|
25
26
|
const PORT_LOCK_FILE = 'port.lock';
|
|
@@ -93,8 +94,8 @@ function trustedRegistryDir(create) {
|
|
|
93
94
|
const dir = getPortRegistryDir();
|
|
94
95
|
try {
|
|
95
96
|
if (create) {
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
makeDirectory(path.dirname(dir));
|
|
98
|
+
makeDirectory(path.join(dir, CLAIMS_DIR), 0o700);
|
|
98
99
|
}
|
|
99
100
|
if (!fs.existsSync(dir))
|
|
100
101
|
return null;
|
|
@@ -266,7 +267,7 @@ function releaseLock(lockPath, token) {
|
|
|
266
267
|
export async function withPortLock(select) {
|
|
267
268
|
const registryDir = trustedRegistryDir(true);
|
|
268
269
|
const lockDir = registryDir ?? getSessionBaseDir();
|
|
269
|
-
|
|
270
|
+
makeDirectory(lockDir);
|
|
270
271
|
const lockPath = path.join(lockDir, PORT_LOCK_FILE);
|
|
271
272
|
const token = `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
272
273
|
const locked = await acquireLock(lockPath, token);
|
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { StatusResponseData } from '../ipc/session/queries.js';
|
|
6
6
|
import { type SocketProbeResult } from './daemonSocket.js';
|
|
7
|
+
import { type UnexpectedEndReason } from './lastSession.js';
|
|
7
8
|
import { type SessionDirEntry } from './paths.js';
|
|
8
9
|
/**
|
|
9
10
|
* State of a session: its daemon runs (`active`, `starting`, `ending`,
|
|
10
|
-
* `unresponsive`),
|
|
11
|
-
* files (`stale`)
|
|
11
|
+
* `unresponsive`), died and left its Chrome running (`crashed`) or only
|
|
12
|
+
* files (`stale`), or exited after the session ended without `bdg stop`
|
|
13
|
+
* (`ended`: Chrome crashed or was closed, the page was closed, `--timeout`).
|
|
12
14
|
*/
|
|
13
|
-
export type RunningSessionState = 'active' | 'starting' | 'ending' | 'unresponsive' | 'crashed' | 'stale';
|
|
15
|
+
export type RunningSessionState = 'active' | 'starting' | 'ending' | 'unresponsive' | 'crashed' | 'stale' | 'ended';
|
|
14
16
|
/**
|
|
15
17
|
* One session in the list.
|
|
16
18
|
*/
|
|
@@ -27,6 +29,10 @@ export interface RunningSessionInfo {
|
|
|
27
29
|
chromePid?: number;
|
|
28
30
|
/** Command that cleans up a crashed or stale session */
|
|
29
31
|
cleanup?: string;
|
|
32
|
+
/** Why an `ended` session ended */
|
|
33
|
+
endReason?: UnexpectedEndReason;
|
|
34
|
+
/** When an `ended` session ended (epoch ms) */
|
|
35
|
+
endedAt?: number;
|
|
30
36
|
}
|
|
31
37
|
/**
|
|
32
38
|
* Summarize a daemon's status response.
|
|
@@ -38,8 +44,10 @@ export interface RunningSessionInfo {
|
|
|
38
44
|
export declare function toRunningSession(name: string | null, data: StatusResponseData): RunningSessionInfo;
|
|
39
45
|
/**
|
|
40
46
|
* A session whose daemon is gone: `crashed` while the Chrome bdg launched for
|
|
41
|
-
* it still runs, `stale` when only its files are left
|
|
42
|
-
*
|
|
47
|
+
* it still runs, `stale` when only its files are left, `ended` when it left
|
|
48
|
+
* only the record of an end without `bdg stop` ({@link describeEnded}).
|
|
49
|
+
* Nothing is changed on disk; `cleanup` names the command that removes
|
|
50
|
+
* leftover files.
|
|
43
51
|
*
|
|
44
52
|
* @param entry - Session directory
|
|
45
53
|
* @param probe - Result of probing its daemon socket
|
|
@@ -6,6 +6,7 @@ import * as fs from 'fs';
|
|
|
6
6
|
import { getStatus } from '../ipc/client.js';
|
|
7
7
|
import { isSessionChrome, readLiveDaemonPid } from './cleanup/staleSession.js';
|
|
8
8
|
import { probeDaemonSocket } from './daemonSocket.js';
|
|
9
|
+
import { readLastSessionEnd } from './lastSession.js';
|
|
9
10
|
import { SESSION_STATE_FILES, getNamedSessionDir, listSessionDirs, sessionFilePathIn, } from './paths.js';
|
|
10
11
|
import { readPidFromFile } from './pid.js';
|
|
11
12
|
import { readPortFile } from './portClaims.js';
|
|
@@ -58,9 +59,10 @@ async function describeSession(entry) {
|
|
|
58
59
|
return { name, state: 'unresponsive' };
|
|
59
60
|
}
|
|
60
61
|
/**
|
|
61
|
-
* A session whose daemon socket does not answer: `
|
|
62
|
-
* (from daemon.pid, verified by command line) runs
|
|
63
|
-
* socket
|
|
62
|
+
* A session whose daemon socket does not answer: `ending` while its daemon
|
|
63
|
+
* (from daemon.pid, verified by command line) still runs, since a daemon
|
|
64
|
+
* writes daemon.pid only after it opened its socket and removes the socket
|
|
65
|
+
* first when it shuts down; otherwise see {@link describeLeftovers}.
|
|
64
66
|
*
|
|
65
67
|
* @param entry - Session directory
|
|
66
68
|
* @param probe - Result of probing its daemon socket
|
|
@@ -69,13 +71,15 @@ async function describeSession(entry) {
|
|
|
69
71
|
function describeWithoutSocket(entry, probe) {
|
|
70
72
|
const daemonPid = readLiveDaemonPid(entry.dir);
|
|
71
73
|
if (daemonPid !== null)
|
|
72
|
-
return { name: entry.name, state: '
|
|
74
|
+
return { name: entry.name, state: 'ending', daemonPid };
|
|
73
75
|
return describeLeftovers(entry, probe);
|
|
74
76
|
}
|
|
75
77
|
/**
|
|
76
78
|
* A session whose daemon is gone: `crashed` while the Chrome bdg launched for
|
|
77
|
-
* it still runs, `stale` when only its files are left
|
|
78
|
-
*
|
|
79
|
+
* it still runs, `stale` when only its files are left, `ended` when it left
|
|
80
|
+
* only the record of an end without `bdg stop` ({@link describeEnded}).
|
|
81
|
+
* Nothing is changed on disk; `cleanup` names the command that removes
|
|
82
|
+
* leftover files.
|
|
79
83
|
*
|
|
80
84
|
* @param entry - Session directory
|
|
81
85
|
* @param probe - Result of probing its daemon socket
|
|
@@ -87,7 +91,7 @@ export function describeLeftovers({ name, dir }, probe) {
|
|
|
87
91
|
const leftover = probe === 'stale' ||
|
|
88
92
|
SESSION_STATE_FILES.some((type) => fs.existsSync(sessionFilePathIn(dir, type)));
|
|
89
93
|
if (!orphan && !leftover)
|
|
90
|
-
return
|
|
94
|
+
return describeEnded({ name, dir });
|
|
91
95
|
const port = leftoverPort(dir);
|
|
92
96
|
return {
|
|
93
97
|
name,
|
|
@@ -97,6 +101,26 @@ export function describeLeftovers({ name, dir }, probe) {
|
|
|
97
101
|
cleanup: sessionCommand('bdg cleanup', name),
|
|
98
102
|
};
|
|
99
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* A session that ended without `bdg stop` and whose daemon exited cleanly:
|
|
106
|
+
* `ended`, with why and when, until the session starts again or
|
|
107
|
+
* `bdg cleanup` clears the record.
|
|
108
|
+
*
|
|
109
|
+
* @param entry - Session directory
|
|
110
|
+
* @returns Session info, or null when the directory holds no such record
|
|
111
|
+
*/
|
|
112
|
+
function describeEnded({ name, dir }) {
|
|
113
|
+
const end = readLastSessionEnd(dir);
|
|
114
|
+
if (end === null)
|
|
115
|
+
return null;
|
|
116
|
+
return {
|
|
117
|
+
name,
|
|
118
|
+
state: 'ended',
|
|
119
|
+
endReason: end.reason,
|
|
120
|
+
endedAt: end.endedAt,
|
|
121
|
+
cleanup: sessionCommand('bdg cleanup', name),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
100
124
|
/**
|
|
101
125
|
* The port a dead session used: from its metadata, else its `port.txt`.
|
|
102
126
|
*
|
package/dist/telemetry/a11y.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CommandError } from '../errors/index.js';
|
|
2
2
|
import { unknownQueryFieldError } from '../errors/messages.js';
|
|
3
|
-
import { callCDP } from '../ipc/client.js';
|
|
3
|
+
import { callBdgScript, callCDP } from '../ipc/client.js';
|
|
4
4
|
import { MASKED_VALUE, SENSITIVE_FIELD_JS } from '../runtime/dom/elementInfo.js';
|
|
5
5
|
import { childFrameIds } from '../runtime/dom/frameLayout.js';
|
|
6
6
|
import { ConcurrencyLimiter } from '../utils/concurrency.js';
|
|
@@ -118,7 +118,7 @@ async function sensitiveNodeIds(tree) {
|
|
|
118
118
|
*/
|
|
119
119
|
async function isSensitiveField(backendNodeId) {
|
|
120
120
|
const objectGroup = 'bdg-a11y-secret';
|
|
121
|
-
const resolved = await
|
|
121
|
+
const resolved = await callBdgScript('DOM.resolveNode', { backendNodeId, objectGroup });
|
|
122
122
|
const objectId = resolved.data?.result?.object
|
|
123
123
|
?.objectId;
|
|
124
124
|
if (!objectId)
|
|
@@ -31,7 +31,8 @@ export declare function formatExceptionText(details: Protocol.Runtime.ExceptionD
|
|
|
31
31
|
* @param messages - Array to populate with console messages
|
|
32
32
|
* @param includeAll - If true, disable default pattern filtering
|
|
33
33
|
* @param getCurrentNavigationId - Function to get current navigation ID
|
|
34
|
+
* @param onDropped - Called for each message dropped at the limit (the oldest go first)
|
|
34
35
|
* @returns Cleanup function to remove event handlers
|
|
35
36
|
*/
|
|
36
|
-
export declare function startConsoleCollection(cdp: CDPConnection, messages: ConsoleMessage[], includeAll?: boolean, getCurrentNavigationId?: () => number): Promise<CleanupFunction>;
|
|
37
|
+
export declare function startConsoleCollection(cdp: CDPConnection, messages: ConsoleMessage[], includeAll?: boolean, getCurrentNavigationId?: () => number, onDropped?: () => void): Promise<CleanupFunction>;
|
|
37
38
|
//# sourceMappingURL=console.d.ts.map
|