browser-debugger-cli 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/cdp/schema.d.ts +4 -1
- package/dist/cdp/schema.js +48 -7
- package/dist/commands/cdp.js +3 -2
- package/dist/commands/cleanup.d.ts +11 -0
- package/dist/commands/cleanup.js +161 -57
- package/dist/commands/console.d.ts +20 -1
- package/dist/commands/console.js +57 -17
- package/dist/commands/details.js +3 -2
- package/dist/commands/dom/DomElementResolver.d.ts +10 -3
- package/dist/commands/dom/DomElementResolver.js +35 -17
- package/dist/commands/dom/a11y.d.ts +10 -0
- package/dist/commands/dom/a11y.js +27 -5
- package/dist/commands/dom/eval.d.ts +3 -1
- package/dist/commands/dom/eval.js +29 -4
- package/dist/commands/dom/form.js +16 -62
- package/dist/commands/dom/formInteraction.js +152 -113
- package/dist/commands/dom/formSummary.d.ts +49 -0
- package/dist/commands/dom/formSummary.js +180 -0
- package/dist/commands/dom/frames.d.ts +2 -1
- package/dist/commands/dom/frames.js +17 -2
- package/dist/commands/dom/get.d.ts +6 -5
- package/dist/commands/dom/get.js +92 -82
- package/dist/commands/dom/helpers/index.d.ts +1 -1
- package/dist/commands/dom/helpers/index.js +1 -1
- package/dist/commands/dom/helpers/query.d.ts +44 -17
- package/dist/commands/dom/helpers/query.js +244 -97
- package/dist/commands/dom/helpers/runElementCommand.d.ts +10 -2
- package/dist/commands/dom/helpers/runElementCommand.js +97 -30
- package/dist/commands/dom/helpers/screenshot.d.ts +4 -1
- package/dist/commands/dom/helpers/screenshot.js +164 -49
- package/dist/commands/dom/index.d.ts +3 -1
- package/dist/commands/dom/index.js +16 -6
- package/dist/commands/dom/layout.d.ts +14 -0
- package/dist/commands/dom/layout.js +54 -0
- package/dist/commands/dom/listeners.d.ts +5 -1
- package/dist/commands/dom/listeners.js +13 -3
- package/dist/commands/dom/query.js +2 -3
- package/dist/commands/dom/screenshot.d.ts +12 -2
- package/dist/commands/dom/screenshot.js +27 -3
- package/dist/commands/dom/semanticUtils.d.ts +6 -13
- package/dist/commands/dom/semanticUtils.js +15 -19
- package/dist/commands/dom/wait.d.ts +13 -0
- package/dist/commands/dom/wait.js +83 -0
- package/dist/commands/helpJson.js +2 -2
- package/dist/commands/network/list.js +4 -11
- package/dist/commands/optionBehaviors.js +112 -21
- package/dist/commands/page.d.ts +2 -1
- package/dist/commands/page.js +41 -5
- package/dist/commands/peek.js +4 -11
- package/dist/commands/sessions.d.ts +8 -0
- package/dist/commands/sessions.js +19 -0
- package/dist/commands/shared/CommandRunner.js +4 -4
- package/dist/commands/shared/dataFetcher.js +2 -2
- package/dist/commands/shared/followMode.d.ts +21 -1
- package/dist/commands/shared/followMode.js +29 -2
- package/dist/commands/shared/handleValidationError.d.ts +2 -2
- package/dist/commands/shared/handleValidationError.js +12 -3
- package/dist/commands/shared/optionTypes.d.ts +40 -5
- package/dist/commands/shared/startHelpers.js +12 -3
- package/dist/commands/shared/validation.d.ts +3 -2
- package/dist/commands/shared/validation.js +4 -3
- package/dist/commands/start.d.ts +63 -0
- package/dist/commands/start.js +115 -15
- package/dist/commands/status.js +29 -7
- package/dist/commands/stop.js +7 -6
- package/dist/commands/tail.js +4 -11
- package/dist/commands/types.d.ts +2 -0
- package/dist/commands.js +2 -0
- package/dist/connection/chromeIdentity.d.ts +65 -0
- package/dist/connection/chromeIdentity.js +143 -0
- package/dist/connection/launcher/profilePreferences.d.ts +47 -0
- package/dist/connection/launcher/profilePreferences.js +151 -0
- package/dist/connection/launcher.d.ts +21 -2
- package/dist/connection/launcher.js +42 -16
- package/dist/connection/portReservation.d.ts +14 -4
- package/dist/connection/portReservation.js +21 -6
- package/dist/connection/startupExit.d.ts +8 -0
- package/dist/connection/startupExit.js +15 -6
- package/dist/constants.d.ts +6 -2
- package/dist/constants.js +9 -2
- package/dist/daemon/SessionController.js +23 -7
- package/dist/daemon/errors.d.ts +1 -1
- package/dist/daemon/errors.js +1 -1
- package/dist/daemon/launcher.d.ts +2 -1
- package/dist/daemon/launcher.js +5 -6
- package/dist/daemon/server/SocketServer.js +1 -2
- package/dist/daemon/session/Session.d.ts +13 -0
- package/dist/daemon/session/Session.js +57 -8
- package/dist/daemon/session/chromeConnection.d.ts +9 -0
- package/dist/daemon/session/chromeConnection.js +45 -8
- package/dist/daemon/session/commandRegistry.js +52 -62
- package/dist/daemon/session/interactions.d.ts +35 -9
- package/dist/daemon/session/interactions.js +36 -9
- package/dist/daemon/session/triggeredRequests.d.ts +67 -0
- package/dist/daemon/session/triggeredRequests.js +157 -0
- package/dist/daemon/session/types.d.ts +5 -1
- package/dist/daemon.js +5393 -1600
- package/dist/errors/messages.d.ts +387 -24
- package/dist/errors/messages.js +761 -67
- package/dist/index.js +3976 -1558
- package/dist/ipc/client.d.ts +12 -1
- package/dist/ipc/client.js +22 -3
- package/dist/ipc/protocol/commands.d.ts +89 -4
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +258 -7
- package/dist/ipc/session/lifecycle.d.ts +8 -1
- package/dist/ipc/session/queries.d.ts +5 -1
- package/dist/ipc/session/types.d.ts +5 -0
- package/dist/ipc/transport/index.d.ts +2 -1
- package/dist/ipc/transport/index.js +2 -2
- package/dist/runtime/dom/actionEffects.d.ts +106 -0
- package/dist/runtime/dom/actionEffects.js +256 -0
- package/dist/runtime/dom/actionEffectsScripts.d.ts +52 -0
- package/dist/runtime/dom/actionEffectsScripts.js +234 -0
- package/dist/runtime/dom/elementGeometry.d.ts +170 -0
- package/dist/runtime/dom/elementGeometry.js +553 -0
- package/dist/runtime/dom/elementInfo.d.ts +77 -0
- package/dist/runtime/dom/elementInfo.js +191 -0
- package/dist/runtime/dom/evalHelpers.d.ts +51 -6
- package/dist/runtime/dom/evalHelpers.js +136 -26
- package/dist/runtime/dom/eventListeners.d.ts +2 -1
- package/dist/runtime/dom/eventListeners.js +174 -47
- package/dist/runtime/dom/formDiscovery.d.ts +1 -1
- package/dist/runtime/dom/formDiscovery.js +116 -16
- package/dist/runtime/dom/formFillHelpers/fill.d.ts +10 -0
- package/dist/runtime/dom/formFillHelpers/fill.js +125 -10
- package/dist/runtime/dom/formFillHelpers/index.d.ts +2 -2
- package/dist/runtime/dom/formFillHelpers/index.js +2 -2
- package/dist/runtime/dom/formFillHelpers/pressKey.js +14 -3
- package/dist/runtime/dom/formFillHelpers/scroll.d.ts +3 -0
- package/dist/runtime/dom/formFillHelpers/scroll.js +60 -18
- package/dist/runtime/dom/formFillHelpers/shared.d.ts +17 -0
- package/dist/runtime/dom/formFillHelpers/shared.js +25 -1
- package/dist/runtime/dom/formFillHelpers/stability.d.ts +20 -6
- package/dist/runtime/dom/formFillHelpers/stability.js +50 -19
- package/dist/runtime/dom/formSubmitHelpers.d.ts +3 -0
- package/dist/runtime/dom/formSubmitHelpers.js +89 -15
- package/dist/runtime/dom/frameLayout.d.ts +60 -0
- package/dist/runtime/dom/frameLayout.js +140 -0
- package/dist/runtime/dom/frameOrigin.d.ts +50 -0
- package/dist/runtime/dom/frameOrigin.js +62 -0
- package/dist/runtime/dom/frameScopedConnection.d.ts +92 -0
- package/dist/runtime/dom/frameScopedConnection.js +252 -0
- package/dist/runtime/dom/frameSelection.d.ts +1 -1
- package/dist/runtime/dom/frameSelection.js +2 -2
- package/dist/runtime/dom/frames.d.ts +25 -2
- package/dist/runtime/dom/frames.js +202 -63
- package/dist/runtime/dom/layout.d.ts +67 -0
- package/dist/runtime/dom/layout.js +333 -0
- package/dist/runtime/dom/listenerPageScripts.d.ts +66 -0
- package/dist/runtime/dom/listenerPageScripts.js +279 -0
- package/dist/runtime/dom/listenerSummary.d.ts +132 -11
- package/dist/runtime/dom/listenerSummary.js +344 -22
- package/dist/runtime/dom/pageActivity.d.ts +41 -0
- package/dist/runtime/dom/pageActivity.js +123 -0
- package/dist/runtime/dom/reactEventHelpers.d.ts +58 -2
- package/dist/runtime/dom/reactEventHelpers.js +212 -41
- package/dist/runtime/dom/targetNode.d.ts +80 -27
- package/dist/runtime/dom/targetNode.js +249 -33
- package/dist/runtime/dom/wait.d.ts +25 -0
- package/dist/runtime/dom/wait.js +199 -0
- package/dist/runtime/dom/waitCondition.d.ts +71 -0
- package/dist/runtime/dom/waitCondition.js +75 -0
- package/dist/runtime/page/emulation.d.ts +51 -0
- package/dist/runtime/page/emulation.js +80 -0
- package/dist/runtime/page/loadingState.d.ts +36 -0
- package/dist/runtime/page/loadingState.js +86 -0
- package/dist/runtime/page/navigation.d.ts +46 -2
- package/dist/runtime/page/navigation.js +69 -33
- package/dist/session/QueryCacheManager.d.ts +11 -1
- package/dist/session/QueryCacheManager.js +25 -3
- package/dist/session/chromeOwners.d.ts +34 -0
- package/dist/session/chromeOwners.js +51 -0
- package/dist/session/cleanup/staleSession.d.ts +11 -1
- package/dist/session/cleanup/staleSession.js +17 -6
- package/dist/session/cleanup/userCommands.js +2 -4
- package/dist/session/metadata.d.ts +5 -1
- package/dist/session/metadata.js +2 -1
- package/dist/session/paths.d.ts +77 -3
- package/dist/session/paths.js +111 -5
- package/dist/session/port.d.ts +31 -7
- package/dist/session/port.js +50 -43
- package/dist/session/portClaims.d.ts +66 -0
- package/dist/session/portClaims.js +284 -0
- package/dist/session/sessionList.d.ts +58 -0
- package/dist/session/sessionList.js +199 -0
- package/dist/session/sessionName.d.ts +46 -0
- package/dist/session/sessionName.js +97 -0
- package/dist/telemetry/a11y.d.ts +8 -3
- package/dist/telemetry/a11y.js +92 -28
- package/dist/telemetry/requestKinds.d.ts +32 -0
- package/dist/telemetry/requestKinds.js +61 -0
- package/dist/telemetry/requestState.d.ts +31 -0
- package/dist/telemetry/requestState.js +38 -0
- package/dist/types.d.ts +80 -3
- package/dist/ui/formatters/a11y.js +3 -0
- package/dist/ui/formatters/console/chronological.d.ts +8 -0
- package/dist/ui/formatters/console/chronological.js +17 -4
- package/dist/ui/formatters/console/json.js +3 -4
- package/dist/ui/formatters/console/shared.d.ts +12 -0
- package/dist/ui/formatters/console.d.ts +2 -2
- package/dist/ui/formatters/console.js +1 -1
- package/dist/ui/formatters/details.js +2 -1
- package/dist/ui/formatters/dom.d.ts +26 -14
- package/dist/ui/formatters/dom.js +65 -52
- package/dist/ui/formatters/form.js +29 -18
- package/dist/ui/formatters/layout.d.ts +31 -0
- package/dist/ui/formatters/layout.js +53 -0
- package/dist/ui/formatters/listeners.d.ts +3 -2
- package/dist/ui/formatters/listeners.js +73 -9
- package/dist/ui/formatters/networkHeaders.js +13 -0
- package/dist/ui/formatters/preview.js +2 -1
- package/dist/ui/formatters/requestStatus.d.ts +1 -17
- package/dist/ui/formatters/requestStatus.js +2 -30
- package/dist/ui/formatters/sessions.d.ts +12 -0
- package/dist/ui/formatters/sessions.js +40 -0
- package/dist/ui/formatters/status.d.ts +21 -2
- package/dist/ui/formatters/status.js +47 -10
- package/dist/ui/formatters/triggeredRequests.d.ts +36 -0
- package/dist/ui/formatters/triggeredRequests.js +65 -0
- package/dist/ui/formatting.d.ts +10 -0
- package/dist/ui/formatting.js +28 -36
- package/dist/ui/messages/chrome.d.ts +9 -0
- package/dist/ui/messages/chrome.js +17 -5
- package/dist/ui/messages/commands.d.ts +388 -14
- package/dist/ui/messages/commands.js +664 -21
- package/dist/ui/messages/consoleMessages.d.ts +10 -0
- package/dist/ui/messages/consoleMessages.js +17 -0
- package/dist/ui/messages/hints.js +2 -1
- package/dist/ui/messages/preview.js +5 -4
- package/dist/ui/messages/session.d.ts +16 -21
- package/dist/ui/messages/session.js +28 -26
- package/dist/ui/messages/sessionCommand.d.ts +43 -0
- package/dist/ui/messages/sessionCommand.js +52 -0
- package/dist/utils/async.d.ts +8 -0
- package/dist/utils/async.js +19 -0
- package/dist/utils/http.d.ts +22 -1
- package/dist/utils/http.js +28 -9
- package/dist/utils/selectorFilters.d.ts +36 -8
- package/dist/utils/selectorFilters.js +267 -53
- package/dist/utils/shellDetection.d.ts +8 -2
- package/dist/utils/shellDetection.js +120 -33
- package/dist/utils/suggestions.d.ts +26 -0
- package/dist/utils/suggestions.js +73 -0
- package/dist/utils/taskMappings.js +10 -0
- package/dist/utils/url.d.ts +12 -2
- package/dist/utils/url.js +69 -7
- package/package.json +1 -1
- package/dist/ui/formatters/sessionFormatters.d.ts +0 -58
- package/dist/ui/formatters/sessionFormatters.js +0 -121
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome profile preferences writer.
|
|
3
|
+
*
|
|
4
|
+
* Chrome reads `<user-data-dir>/Default/Preferences` as nested JSON and looks
|
|
5
|
+
* preferences up by path (`profile.password_manager_enabled` is
|
|
6
|
+
* `{"profile": {"password_manager_enabled": ...}}`). bdg's preferences use
|
|
7
|
+
* dotted names, so they are expanded into that structure and deep-merged into
|
|
8
|
+
* the existing file: persistent profiles reused across sessions get them too,
|
|
9
|
+
* and everything else Chrome stored there is kept.
|
|
10
|
+
*/
|
|
11
|
+
import type { Logger } from '../types.js';
|
|
12
|
+
/** JSON object as stored in the Preferences file */
|
|
13
|
+
type PrefsObject = Record<string, unknown>;
|
|
14
|
+
/**
|
|
15
|
+
* Merge preferences into an existing Preferences object.
|
|
16
|
+
*
|
|
17
|
+
* Dotted names become nested paths, and a literal dotted top-level key (as
|
|
18
|
+
* earlier bdg versions wrote, which Chrome ignored) is removed.
|
|
19
|
+
*
|
|
20
|
+
* @param existing - Current Preferences content
|
|
21
|
+
* @param prefs - Preferences to apply, dotted or nested
|
|
22
|
+
* @returns Merged Preferences (a new object; `existing` is not modified)
|
|
23
|
+
*/
|
|
24
|
+
export declare function mergePreferences(existing: PrefsObject, prefs: PrefsObject): PrefsObject;
|
|
25
|
+
/**
|
|
26
|
+
* Whether a running Chrome has the profile open, judged by its
|
|
27
|
+
* `SingletonLock` symlink (`<host>-<pid>`, macOS and Linux).
|
|
28
|
+
*
|
|
29
|
+
* @param userDataDir - Chrome user data directory
|
|
30
|
+
* @returns True if the lock names a live process on this host
|
|
31
|
+
*/
|
|
32
|
+
export declare function profileInUse(userDataDir: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Apply preferences to the default profile in a Chrome user data directory.
|
|
35
|
+
*
|
|
36
|
+
* Failures are logged (visible in daemon.log), not thrown: Chrome still
|
|
37
|
+
* starts, only without them. An unreadable Preferences file and a profile
|
|
38
|
+
* that a running Chrome has open are left alone, and the file is not
|
|
39
|
+
* rewritten when nothing changes.
|
|
40
|
+
*
|
|
41
|
+
* @param userDataDir - Chrome user data directory
|
|
42
|
+
* @param prefs - Preferences to apply, dotted or nested
|
|
43
|
+
* @param logger - Logger for failures
|
|
44
|
+
*/
|
|
45
|
+
export declare function writeProfilePreferences(userDataDir: string, prefs: PrefsObject, logger: Logger): void;
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=profilePreferences.d.ts.map
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome profile preferences writer.
|
|
3
|
+
*
|
|
4
|
+
* Chrome reads `<user-data-dir>/Default/Preferences` as nested JSON and looks
|
|
5
|
+
* preferences up by path (`profile.password_manager_enabled` is
|
|
6
|
+
* `{"profile": {"password_manager_enabled": ...}}`). bdg's preferences use
|
|
7
|
+
* dotted names, so they are expanded into that structure and deep-merged into
|
|
8
|
+
* the existing file: persistent profiles reused across sessions get them too,
|
|
9
|
+
* and everything else Chrome stored there is kept.
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from 'fs';
|
|
12
|
+
import * as os from 'os';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import { chromePrefsNotAppliedMessage } from '../../ui/messages/chrome.js';
|
|
15
|
+
import { getErrorMessage } from '../../utils/errors.js';
|
|
16
|
+
import { isProcessAlive } from '../../utils/process.js';
|
|
17
|
+
/**
|
|
18
|
+
* Whether a value is a plain JSON object (not an array or null).
|
|
19
|
+
*
|
|
20
|
+
* @param value - Value to check
|
|
21
|
+
* @returns True for objects that can be merged into
|
|
22
|
+
*/
|
|
23
|
+
function isPrefsObject(value) {
|
|
24
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Set a preference at a nested path, merging objects with what is there.
|
|
28
|
+
*
|
|
29
|
+
* @param target - Preferences object to modify
|
|
30
|
+
* @param keys - Path segments, e.g. `['profile', 'exit_type']`
|
|
31
|
+
* @param value - Value to set
|
|
32
|
+
*/
|
|
33
|
+
function setAtPath(target, keys, value) {
|
|
34
|
+
const [key, ...rest] = keys;
|
|
35
|
+
if (key === undefined)
|
|
36
|
+
return;
|
|
37
|
+
const current = target[key];
|
|
38
|
+
if (rest.length > 0) {
|
|
39
|
+
const child = isPrefsObject(current) ? current : {};
|
|
40
|
+
target[key] = child;
|
|
41
|
+
setAtPath(child, rest, value);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (isPrefsObject(current) && isPrefsObject(value)) {
|
|
45
|
+
for (const [childKey, childValue] of Object.entries(value)) {
|
|
46
|
+
setAtPath(current, [childKey], childValue);
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
target[key] = value;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Merge preferences into an existing Preferences object.
|
|
54
|
+
*
|
|
55
|
+
* Dotted names become nested paths, and a literal dotted top-level key (as
|
|
56
|
+
* earlier bdg versions wrote, which Chrome ignored) is removed.
|
|
57
|
+
*
|
|
58
|
+
* @param existing - Current Preferences content
|
|
59
|
+
* @param prefs - Preferences to apply, dotted or nested
|
|
60
|
+
* @returns Merged Preferences (a new object; `existing` is not modified)
|
|
61
|
+
*/
|
|
62
|
+
export function mergePreferences(existing, prefs) {
|
|
63
|
+
const merged = structuredClone(existing);
|
|
64
|
+
for (const [name, value] of Object.entries(prefs)) {
|
|
65
|
+
if (name.includes('.'))
|
|
66
|
+
delete merged[name];
|
|
67
|
+
setAtPath(merged, name.split('.'), value);
|
|
68
|
+
}
|
|
69
|
+
return merged;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Read the profile's Preferences file.
|
|
73
|
+
*
|
|
74
|
+
* @param file - Preferences file path
|
|
75
|
+
* @returns Its content, `{}` if missing
|
|
76
|
+
* @throws Error if it is not valid JSON or not a JSON object
|
|
77
|
+
*/
|
|
78
|
+
function readPreferencesFile(file) {
|
|
79
|
+
if (!fs.existsSync(file))
|
|
80
|
+
return {};
|
|
81
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
82
|
+
if (!isPrefsObject(parsed))
|
|
83
|
+
throw new Error('not a JSON object');
|
|
84
|
+
return parsed;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Whether a running Chrome has the profile open, judged by its
|
|
88
|
+
* `SingletonLock` symlink (`<host>-<pid>`, macOS and Linux).
|
|
89
|
+
*
|
|
90
|
+
* @param userDataDir - Chrome user data directory
|
|
91
|
+
* @returns True if the lock names a live process on this host
|
|
92
|
+
*/
|
|
93
|
+
export function profileInUse(userDataDir) {
|
|
94
|
+
let target;
|
|
95
|
+
try {
|
|
96
|
+
target = fs.readlinkSync(path.join(userDataDir, 'SingletonLock'));
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
const separator = target.lastIndexOf('-');
|
|
102
|
+
const pid = Number(target.slice(separator + 1));
|
|
103
|
+
return (target.slice(0, separator) === os.hostname() && Number.isInteger(pid) && isProcessAlive(pid));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Replace a file atomically: write a temporary file next to it, then rename.
|
|
107
|
+
*
|
|
108
|
+
* @param file - File to replace
|
|
109
|
+
* @param content - New content
|
|
110
|
+
*/
|
|
111
|
+
function writeFileAtomic(file, content) {
|
|
112
|
+
const temp = `${file}.bdg-${process.pid}.tmp`;
|
|
113
|
+
try {
|
|
114
|
+
fs.writeFileSync(temp, content, 'utf8');
|
|
115
|
+
fs.renameSync(temp, file);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
fs.rmSync(temp, { force: true });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Apply preferences to the default profile in a Chrome user data directory.
|
|
123
|
+
*
|
|
124
|
+
* Failures are logged (visible in daemon.log), not thrown: Chrome still
|
|
125
|
+
* starts, only without them. An unreadable Preferences file and a profile
|
|
126
|
+
* that a running Chrome has open are left alone, and the file is not
|
|
127
|
+
* rewritten when nothing changes.
|
|
128
|
+
*
|
|
129
|
+
* @param userDataDir - Chrome user data directory
|
|
130
|
+
* @param prefs - Preferences to apply, dotted or nested
|
|
131
|
+
* @param logger - Logger for failures
|
|
132
|
+
*/
|
|
133
|
+
export function writeProfilePreferences(userDataDir, prefs, logger) {
|
|
134
|
+
const file = path.join(userDataDir, 'Default', 'Preferences');
|
|
135
|
+
if (profileInUse(userDataDir)) {
|
|
136
|
+
logger.info(chromePrefsNotAppliedMessage(file, 'the profile is in use by a running Chrome'));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const existing = readPreferencesFile(file);
|
|
141
|
+
const content = JSON.stringify(mergePreferences(existing, prefs));
|
|
142
|
+
if (content === JSON.stringify(existing))
|
|
143
|
+
return;
|
|
144
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
145
|
+
writeFileAtomic(file, content);
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
logger.info(chromePrefsNotAppliedMessage(file, getErrorMessage(error)));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=profilePreferences.js.map
|
|
@@ -7,7 +7,11 @@ import type { Options as ChromeLaunchOptions } from 'chrome-launcher';
|
|
|
7
7
|
export interface LaunchOptions extends Pick<ChromeLaunchOptions, 'logLevel' | 'connectionPollInterval' | 'maxConnectionRetries' | 'portStrictMode' | 'envVars' | 'handleSIGINT' | 'ignoreDefaultFlags' | 'chromeFlags' | 'chromePath'> {
|
|
8
8
|
/** Remote debugging port (defaults to 9222 when omitted) */
|
|
9
9
|
port?: number;
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Directory for Chrome profile data, chosen by the user: bdg's default
|
|
12
|
+
* preferences are not written into it. Falls back to the bdg-managed
|
|
13
|
+
* persistent profile in the session directory (~/.bdg/chrome-profile)
|
|
14
|
+
*/
|
|
11
15
|
userDataDir?: string | undefined;
|
|
12
16
|
/** Base directory for creating user data dir (defaults to OS temp dir, injectable for testing) */
|
|
13
17
|
baseDir?: string | undefined;
|
|
@@ -23,7 +27,7 @@ export interface LaunchOptions extends Pick<ChromeLaunchOptions, 'logLevel' | 'c
|
|
|
23
27
|
prefsFile?: string | undefined;
|
|
24
28
|
/** Override Chrome binary detection with an explicit path */
|
|
25
29
|
chromePath?: string;
|
|
26
|
-
/** bdg session directory
|
|
30
|
+
/** bdg session directory: recorded as a marker flag on the Chrome command line, and holds the default profile */
|
|
27
31
|
sessionDir?: string | undefined;
|
|
28
32
|
}
|
|
29
33
|
/**
|
|
@@ -46,4 +50,19 @@ export interface LaunchOptions extends Pick<ChromeLaunchOptions, 'logLevel' | 'c
|
|
|
46
50
|
* Uses chrome-launcher for cross-platform Chrome detection and launching.
|
|
47
51
|
*/
|
|
48
52
|
export declare function launchChrome(options?: LaunchOptions): Promise<LaunchedChrome>;
|
|
53
|
+
/**
|
|
54
|
+
* Preferences for the launched profile: bdg's defaults (no translate prompt,
|
|
55
|
+
* no password manager or leak check, whose bubbles capture input) for
|
|
56
|
+
* profiles bdg manages, with the caller's preferences on top. A profile the
|
|
57
|
+
* user chose (`--user-data-dir`) gets only preferences the caller passed.
|
|
58
|
+
*
|
|
59
|
+
* File-based preferences take precedence over inline preferences because
|
|
60
|
+
* files allow for complex, reusable configurations that can be version
|
|
61
|
+
* controlled and shared across team members or CI environments.
|
|
62
|
+
*
|
|
63
|
+
* @param options - Launch options containing prefs or prefsFile
|
|
64
|
+
* @returns Merged preferences (empty when there is nothing to apply)
|
|
65
|
+
* @throws ChromeLaunchError if prefs file cannot be read or parsed, or prefs are not JSON
|
|
66
|
+
*/
|
|
67
|
+
export declare function resolveChromePrefs(options: LaunchOptions): Record<string, unknown>;
|
|
49
68
|
//# sourceMappingURL=launcher.d.ts.map
|
|
@@ -6,10 +6,12 @@ import { BDG_CHROME_PREFS, DEFAULT_CDP_PORT, CHROME_PROFILE_DIR, DEFAULT_CHROME_
|
|
|
6
6
|
import { getErrorMessage } from '../utils/errors.js';
|
|
7
7
|
import { filterDefined } from '../utils/objects.js';
|
|
8
8
|
import { isProcessAlive } from '../utils/process.js';
|
|
9
|
+
import { verifyLaunchedChrome } from './chromeIdentity.js';
|
|
9
10
|
import { ChromeLaunchError } from './errors.js';
|
|
10
11
|
import { resolveChromeBinary } from './launcher/binaryResolver.js';
|
|
11
12
|
import { buildChromeFlags } from './launcher/flagsBuilder.js';
|
|
12
13
|
import { loadChromePrefs, ensureJSONCompatiblePrefs } from './launcher/preferencesLoader.js';
|
|
14
|
+
import { writeProfilePreferences } from './launcher/profilePreferences.js';
|
|
13
15
|
import { reservePort } from './portReservation.js';
|
|
14
16
|
import { markStartupLogs, watchStartupExit } from './startupExit.js';
|
|
15
17
|
/**
|
|
@@ -57,7 +59,7 @@ export async function launchChrome(options = {}) {
|
|
|
57
59
|
}
|
|
58
60
|
const reservation = await reservePort(port);
|
|
59
61
|
reservation.release();
|
|
60
|
-
const userDataDir = options.userDataDir ?? getPersistentUserDataDir(options.baseDir);
|
|
62
|
+
const userDataDir = options.userDataDir ?? getPersistentUserDataDir(options.baseDir ?? options.sessionDir);
|
|
61
63
|
if (!fs.existsSync(userDataDir)) {
|
|
62
64
|
try {
|
|
63
65
|
fs.mkdirSync(userDataDir, { recursive: true });
|
|
@@ -74,9 +76,11 @@ export async function launchChrome(options = {}) {
|
|
|
74
76
|
}
|
|
75
77
|
logger.info(`Launching Chrome on port ${port}...`);
|
|
76
78
|
logger.debug(`User data directory: ${userDataDir}`);
|
|
77
|
-
|
|
79
|
+
applyProfilePreferences(userDataDir, options, logger);
|
|
80
|
+
const chromeOptions = buildChromeOptions({ ...options, port });
|
|
78
81
|
const launcher = new chromeLauncher.Launcher(chromeOptions);
|
|
79
|
-
const
|
|
82
|
+
const logs = markStartupLogs(userDataDir);
|
|
83
|
+
const startup = watchStartupExit(() => launcher.chromeProcess, logs, userDataDir);
|
|
80
84
|
try {
|
|
81
85
|
const launchStart = Date.now();
|
|
82
86
|
logger.info('Waiting for Chrome to be ready...');
|
|
@@ -96,10 +100,11 @@ export async function launchChrome(options = {}) {
|
|
|
96
100
|
},
|
|
97
101
|
});
|
|
98
102
|
}
|
|
103
|
+
await verifyLaunchedChrome({ logs, port, pid: chromeProcessPid });
|
|
99
104
|
logger.info(`Chrome launched successfully (PID: ${chromeProcessPid}, ${launchDurationMs}ms)`);
|
|
100
105
|
return {
|
|
101
106
|
pid: chromeProcessPid,
|
|
102
|
-
port
|
|
107
|
+
port,
|
|
103
108
|
userDataDir: launcher.userDataDir,
|
|
104
109
|
kill: async () => {
|
|
105
110
|
return Promise.resolve().then(() => {
|
|
@@ -128,7 +133,9 @@ export async function launchChrome(options = {}) {
|
|
|
128
133
|
/**
|
|
129
134
|
* Get the default persistent user-data-dir path.
|
|
130
135
|
*
|
|
131
|
-
* Uses the session directory (
|
|
136
|
+
* Uses the session directory (`~/.bdg`, `$BDG_SESSION_DIR`, or
|
|
137
|
+
* `<base>/sessions/<name>` for a named session, passed as `sessionDir`) to
|
|
138
|
+
* store Chrome profile data,
|
|
132
139
|
* ensuring each session has its own isolated Chrome profile. This prevents
|
|
133
140
|
* SingletonLock conflicts when multiple agents run concurrently.
|
|
134
141
|
*
|
|
@@ -136,7 +143,7 @@ export async function launchChrome(options = {}) {
|
|
|
136
143
|
* For shared login state across sessions, use --user-data-dir to specify a
|
|
137
144
|
* shared location explicitly.
|
|
138
145
|
*
|
|
139
|
-
* @param baseDir - Optional base directory (defaults to
|
|
146
|
+
* @param baseDir - Optional base directory (defaults to `~/.bdg` or `$BDG_SESSION_DIR`). Allows injection for testing or custom locations.
|
|
140
147
|
* @returns Absolute path to session-isolated user-data-dir
|
|
141
148
|
* @throws Error if user data directory cannot be created due to permission issues
|
|
142
149
|
*/
|
|
@@ -168,32 +175,52 @@ function getPersistentUserDataDir(baseDir) {
|
|
|
168
175
|
return userDataDir;
|
|
169
176
|
}
|
|
170
177
|
/**
|
|
171
|
-
*
|
|
178
|
+
* Preferences for the launched profile: bdg's defaults (no translate prompt,
|
|
179
|
+
* no password manager or leak check, whose bubbles capture input) for
|
|
180
|
+
* profiles bdg manages, with the caller's preferences on top. A profile the
|
|
181
|
+
* user chose (`--user-data-dir`) gets only preferences the caller passed.
|
|
172
182
|
*
|
|
173
183
|
* File-based preferences take precedence over inline preferences because
|
|
174
184
|
* files allow for complex, reusable configurations that can be version
|
|
175
185
|
* controlled and shared across team members or CI environments.
|
|
176
186
|
*
|
|
177
187
|
* @param options - Launch options containing prefs or prefsFile
|
|
178
|
-
* @returns
|
|
179
|
-
* @throws ChromeLaunchError if prefs file cannot be read
|
|
188
|
+
* @returns Merged preferences (empty when there is nothing to apply)
|
|
189
|
+
* @throws ChromeLaunchError if prefs file cannot be read or parsed, or prefs are not JSON
|
|
180
190
|
*/
|
|
191
|
+
export function resolveChromePrefs(options) {
|
|
192
|
+
const defaults = options.userDataDir === undefined ? BDG_CHROME_PREFS : {};
|
|
193
|
+
const prefs = { ...defaults, ...loadChromePrefs(options) };
|
|
194
|
+
ensureJSONCompatiblePrefs(prefs);
|
|
195
|
+
return prefs;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Write the launch preferences into the profile, if there are any.
|
|
199
|
+
*
|
|
200
|
+
* @param userDataDir - Chrome user data directory
|
|
201
|
+
* @param options - Launch options
|
|
202
|
+
* @param logger - Logger for failures
|
|
203
|
+
* @throws ChromeLaunchError if prefs file cannot be read or parsed, or prefs are not JSON
|
|
204
|
+
*/
|
|
205
|
+
function applyProfilePreferences(userDataDir, options, logger) {
|
|
206
|
+
const prefs = resolveChromePrefs(options);
|
|
207
|
+
if (Object.keys(prefs).length > 0)
|
|
208
|
+
writeProfilePreferences(userDataDir, prefs, logger);
|
|
209
|
+
}
|
|
181
210
|
/**
|
|
182
211
|
* Build chrome-launcher options from bdg launch options.
|
|
183
212
|
*
|
|
184
213
|
* Maps LaunchOptions to chrome-launcher API format using a clean utility approach
|
|
185
|
-
* that filters out undefined values automatically.
|
|
186
|
-
*
|
|
214
|
+
* that filters out undefined values automatically. Preferences are not passed:
|
|
215
|
+
* chrome-launcher would replace whole nested sections of an existing profile's
|
|
216
|
+
* Preferences, so `writeProfilePreferences` merges them instead.
|
|
187
217
|
*
|
|
188
218
|
* @param options - bdg launch options to convert
|
|
189
219
|
* @returns chrome-launcher compatible options object
|
|
190
|
-
* @throws ChromeLaunchError if preference loading fails
|
|
191
220
|
*/
|
|
192
221
|
function buildChromeOptions(options) {
|
|
193
|
-
const
|
|
194
|
-
const userDataDir = options.userDataDir ?? getPersistentUserDataDir(options.baseDir);
|
|
222
|
+
const userDataDir = options.userDataDir ?? getPersistentUserDataDir(options.baseDir ?? options.sessionDir);
|
|
195
223
|
const chromePathOverride = resolveChromeBinary(options);
|
|
196
|
-
const mergedPrefs = userPrefs ? { ...BDG_CHROME_PREFS, ...userPrefs } : BDG_CHROME_PREFS;
|
|
197
224
|
return {
|
|
198
225
|
logLevel: options.logLevel ?? DEFAULT_CHROME_LOG_LEVEL,
|
|
199
226
|
handleSIGINT: options.handleSIGINT ?? DEFAULT_CHROME_HANDLE_SIGINT,
|
|
@@ -206,7 +233,6 @@ function buildChromeOptions(options) {
|
|
|
206
233
|
connectionPollInterval: options.connectionPollInterval ?? CHROME_READY_POLL_MS,
|
|
207
234
|
maxConnectionRetries: options.maxConnectionRetries ?? CHROME_READY_POLL_ATTEMPTS,
|
|
208
235
|
portStrictMode: options.portStrictMode,
|
|
209
|
-
prefs: ensureJSONCompatiblePrefs(mergedPrefs),
|
|
210
236
|
envVars: options.envVars,
|
|
211
237
|
chromePath: chromePathOverride,
|
|
212
238
|
}),
|
|
@@ -12,14 +12,24 @@ export interface PortReservation {
|
|
|
12
12
|
release: () => void;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* Whether something already accepts connections on
|
|
16
|
-
* listener on all interfaces (`0.0.0.0`/`::`) does not stop bdg from binding
|
|
17
|
-
* 127.0.0.1 on macOS, but Chrome would then fail to listen, so it is checked
|
|
18
|
-
* by connecting.
|
|
15
|
+
* Whether something already accepts connections on host:port.
|
|
19
16
|
*
|
|
20
17
|
* @param port - Port to check
|
|
18
|
+
* @param host - Address to connect to
|
|
21
19
|
* @returns True if a connection was accepted
|
|
22
20
|
*/
|
|
21
|
+
export declare function acceptsConnections(port: number, host: string): Promise<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* Whether something already accepts connections on the port, on 127.0.0.1 or
|
|
24
|
+
* ::1. A listener on all interfaces (`0.0.0.0`/`::`) does not stop bdg from
|
|
25
|
+
* binding 127.0.0.1 on macOS, but Chrome would then fail to listen; a Chrome
|
|
26
|
+
* on [::1] (one that found 127.0.0.1 taken) does not stop it either, but a
|
|
27
|
+
* second Chrome would be reached through the first one's port. Both are found
|
|
28
|
+
* by connecting.
|
|
29
|
+
*
|
|
30
|
+
* @param port - Port to check
|
|
31
|
+
* @returns True if a connection was accepted on either address
|
|
32
|
+
*/
|
|
23
33
|
export declare function isPortAnswering(port: number): Promise<boolean>;
|
|
24
34
|
/**
|
|
25
35
|
* Atomically reserve a port to prevent race conditions during Chrome launch.
|
|
@@ -8,18 +8,18 @@ import * as net from 'net';
|
|
|
8
8
|
import { ChromeLaunchError } from './errors.js';
|
|
9
9
|
/** How long a connection attempt may take when checking a port */
|
|
10
10
|
const CONNECT_CHECK_MS = 500;
|
|
11
|
+
/** Loopback addresses Chrome may listen on (it falls back to IPv6 when IPv4 is taken) */
|
|
12
|
+
const LOOPBACK_HOSTS = ['127.0.0.1', '::1'];
|
|
11
13
|
/**
|
|
12
|
-
* Whether something already accepts connections on
|
|
13
|
-
* listener on all interfaces (`0.0.0.0`/`::`) does not stop bdg from binding
|
|
14
|
-
* 127.0.0.1 on macOS, but Chrome would then fail to listen, so it is checked
|
|
15
|
-
* by connecting.
|
|
14
|
+
* Whether something already accepts connections on host:port.
|
|
16
15
|
*
|
|
17
16
|
* @param port - Port to check
|
|
17
|
+
* @param host - Address to connect to
|
|
18
18
|
* @returns True if a connection was accepted
|
|
19
19
|
*/
|
|
20
|
-
export function
|
|
20
|
+
export function acceptsConnections(port, host) {
|
|
21
21
|
return new Promise((resolve) => {
|
|
22
|
-
const socket = net.connect({ port, host
|
|
22
|
+
const socket = net.connect({ port, host });
|
|
23
23
|
const finish = (answering) => {
|
|
24
24
|
socket.destroy();
|
|
25
25
|
resolve(answering);
|
|
@@ -29,6 +29,21 @@ export function isPortAnswering(port) {
|
|
|
29
29
|
socket.once('error', () => finish(false));
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Whether something already accepts connections on the port, on 127.0.0.1 or
|
|
34
|
+
* ::1. A listener on all interfaces (`0.0.0.0`/`::`) does not stop bdg from
|
|
35
|
+
* binding 127.0.0.1 on macOS, but Chrome would then fail to listen; a Chrome
|
|
36
|
+
* on [::1] (one that found 127.0.0.1 taken) does not stop it either, but a
|
|
37
|
+
* second Chrome would be reached through the first one's port. Both are found
|
|
38
|
+
* by connecting.
|
|
39
|
+
*
|
|
40
|
+
* @param port - Port to check
|
|
41
|
+
* @returns True if a connection was accepted on either address
|
|
42
|
+
*/
|
|
43
|
+
export async function isPortAnswering(port) {
|
|
44
|
+
const answers = await Promise.all(LOOPBACK_HOSTS.map((host) => acceptsConnections(port, host)));
|
|
45
|
+
return answers.some(Boolean);
|
|
46
|
+
}
|
|
32
47
|
/**
|
|
33
48
|
* The error for a port another process uses.
|
|
34
49
|
*
|
|
@@ -22,6 +22,14 @@ export interface StartupLogs {
|
|
|
22
22
|
* @returns Log positions
|
|
23
23
|
*/
|
|
24
24
|
export declare function markStartupLogs(userDataDir: string): StartupLogs;
|
|
25
|
+
/**
|
|
26
|
+
* Chrome's output (stderr, then stdout) since {@link markStartupLogs}. The
|
|
27
|
+
* marked offsets are byte sizes, so the files are cut before decoding.
|
|
28
|
+
*
|
|
29
|
+
* @param logs - Log positions
|
|
30
|
+
* @returns Output lines
|
|
31
|
+
*/
|
|
32
|
+
export declare function readStartupLines(logs: StartupLogs): string[];
|
|
25
33
|
/**
|
|
26
34
|
* Reject when the launched Chrome exits before it is ready.
|
|
27
35
|
*
|
|
@@ -31,18 +31,27 @@ export function markStartupLogs(userDataDir) {
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
* Chrome's output since {@link markStartupLogs}
|
|
34
|
+
* Chrome's output (stderr, then stdout) since {@link markStartupLogs}. The
|
|
35
|
+
* marked offsets are byte sizes, so the files are cut before decoding.
|
|
35
36
|
*
|
|
36
37
|
* @param logs - Log positions
|
|
37
|
-
* @returns
|
|
38
|
+
* @returns Output lines
|
|
38
39
|
*/
|
|
39
|
-
function
|
|
40
|
-
|
|
40
|
+
export function readStartupLines(logs) {
|
|
41
|
+
return logs.files.flatMap(({ file, offset }) => {
|
|
41
42
|
if (!fs.existsSync(file))
|
|
42
43
|
return [];
|
|
43
|
-
return fs.readFileSync(file
|
|
44
|
+
return fs.readFileSync(file).subarray(offset).toString('utf8').split('\n');
|
|
44
45
|
});
|
|
45
|
-
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Chrome's output since {@link markStartupLogs}, last lines first trimmed.
|
|
49
|
+
*
|
|
50
|
+
* @param logs - Log positions
|
|
51
|
+
* @returns Up to {@link OUTPUT_LINES} non-empty lines
|
|
52
|
+
*/
|
|
53
|
+
function readStartupOutput(logs) {
|
|
54
|
+
return readStartupLines(logs)
|
|
46
55
|
.map((line) => line.trim())
|
|
47
56
|
.filter(Boolean)
|
|
48
57
|
.slice(-OUTPUT_LINES);
|
package/dist/constants.d.ts
CHANGED
|
@@ -46,8 +46,12 @@ export declare const BDG_CHROME_FLAGS: string[];
|
|
|
46
46
|
export declare const DOCKER_CHROME_FLAGS: string[];
|
|
47
47
|
/**
|
|
48
48
|
* BDG-specific Chrome preferences for automation
|
|
49
|
-
*
|
|
50
|
-
* User preferences take precedence
|
|
49
|
+
* Written into the launched profile's Default/Preferences (dotted names become nested paths)
|
|
50
|
+
* before every launch, so persistent profiles get them too. User preferences take precedence.
|
|
51
|
+
*
|
|
52
|
+
* The password manager and its leak check are off: after a login their bubble
|
|
53
|
+
* (e.g. "Change your password") captures all input in headless Chrome, so
|
|
54
|
+
* later clicks silently never reach the page.
|
|
51
55
|
*
|
|
52
56
|
* Note: Crash/restore popup suppression is handled by Chrome flags (--disable-session-crashed-bubble, --disable-infobars)
|
|
53
57
|
* which are more reliable than preference-based approaches.
|
package/dist/constants.js
CHANGED
|
@@ -64,8 +64,12 @@ export const DOCKER_CHROME_FLAGS = [
|
|
|
64
64
|
];
|
|
65
65
|
/**
|
|
66
66
|
* BDG-specific Chrome preferences for automation
|
|
67
|
-
*
|
|
68
|
-
* User preferences take precedence
|
|
67
|
+
* Written into the launched profile's Default/Preferences (dotted names become nested paths)
|
|
68
|
+
* before every launch, so persistent profiles get them too. User preferences take precedence.
|
|
69
|
+
*
|
|
70
|
+
* The password manager and its leak check are off: after a login their bubble
|
|
71
|
+
* (e.g. "Change your password") captures all input in headless Chrome, so
|
|
72
|
+
* later clicks silently never reach the page.
|
|
69
73
|
*
|
|
70
74
|
* Note: Crash/restore popup suppression is handled by Chrome flags (--disable-session-crashed-bubble, --disable-infobars)
|
|
71
75
|
* which are more reliable than preference-based approaches.
|
|
@@ -75,6 +79,9 @@ export const BDG_CHROME_PREFS = {
|
|
|
75
79
|
'translate.enabled': false, // Disable Google Translate popup
|
|
76
80
|
translate_site_blacklist: ['*'], // Block translate for all sites
|
|
77
81
|
'profile.exit_type': 'Normal', // Trick Chrome into thinking it shut down correctly
|
|
82
|
+
credentials_enable_service: false, // No "Save password?" bubble
|
|
83
|
+
'profile.password_manager_enabled': false, // Password manager off
|
|
84
|
+
'profile.password_manager_leak_detection': false, // No "Change your password" breach dialog
|
|
78
85
|
};
|
|
79
86
|
// ============================================================================
|
|
80
87
|
// DATA COLLECTION LIMITS
|
|
@@ -9,20 +9,36 @@ import { CDPConnectionError, CDPProtocolError, ChromeLaunchError, ConnectionErro
|
|
|
9
9
|
import { Session, StartCancelledError } from './session/Session.js';
|
|
10
10
|
import { detectTargetMismatch } from './session/targetMismatch.js';
|
|
11
11
|
import { CommandError } from '../errors/index.js';
|
|
12
|
-
import { LAUNCHED_CHROME_DESCRIPTION, cdpRequestRejectedError, sessionEndedDuringCommandError, } from '../errors/messages.js';
|
|
12
|
+
import { LAUNCHED_CHROME_DESCRIPTION, cdpRequestRejectedError, sessionAlreadyRunningMessage, sessionEndedDuringCommandError, sessionUnavailableSuggestion, } from '../errors/messages.js';
|
|
13
13
|
import { IPCErrorCode, } from '../ipc/index.js';
|
|
14
14
|
import { clearLastSessionEnd, writeLastSessionEnd } from '../session/lastSession.js';
|
|
15
15
|
import { createLogger } from '../ui/logging/index.js';
|
|
16
16
|
import { formatChromeIssue } from '../ui/messages/chrome.js';
|
|
17
|
+
import { noActiveSessionMessage } from '../ui/messages/sessionCommand.js';
|
|
17
18
|
import { getErrorMessage } from '../utils/errors.js';
|
|
18
19
|
import { EXIT_CODES } from '../utils/exitCodes.js';
|
|
19
20
|
import { filterDefined } from '../utils/objects.js';
|
|
20
21
|
const log = createLogger('daemon');
|
|
21
|
-
const NO_SESSION_ERROR = 'No active session';
|
|
22
22
|
/** Error for requests that need the session while `bdg <url>` is still starting it */
|
|
23
23
|
const STARTING_ERROR = 'The session is still starting';
|
|
24
24
|
const COMMAND_TIMEOUT_MS = 30000;
|
|
25
25
|
const QUERY_TIMEOUT_MS = 5000;
|
|
26
|
+
/** Time a `dom wait` gets beyond its own --timeout to report the timeout itself */
|
|
27
|
+
const WAIT_COMMAND_MARGIN_MS = 5000;
|
|
28
|
+
/**
|
|
29
|
+
* How long a session command may run: `dom wait` runs for its own
|
|
30
|
+
* --timeout, everything else gets {@link COMMAND_TIMEOUT_MS}.
|
|
31
|
+
*
|
|
32
|
+
* @param name - Command name
|
|
33
|
+
* @param params - Command parameters
|
|
34
|
+
* @returns Timeout in milliseconds
|
|
35
|
+
*/
|
|
36
|
+
function commandTimeoutMs(name, params) {
|
|
37
|
+
if (name !== 'dom_wait')
|
|
38
|
+
return COMMAND_TIMEOUT_MS;
|
|
39
|
+
const { timeout } = params;
|
|
40
|
+
return Math.max(COMMAND_TIMEOUT_MS, (timeout ?? 0) + WAIT_COMMAND_MARGIN_MS);
|
|
41
|
+
}
|
|
26
42
|
/**
|
|
27
43
|
* Run a promise with a timeout, clearing the timer either way.
|
|
28
44
|
*
|
|
@@ -139,11 +155,11 @@ export class SessionController {
|
|
|
139
155
|
*/
|
|
140
156
|
noSessionError() {
|
|
141
157
|
if (!this.launching)
|
|
142
|
-
return { error:
|
|
158
|
+
return { error: noActiveSessionMessage() };
|
|
143
159
|
return {
|
|
144
160
|
error: STARTING_ERROR,
|
|
145
161
|
exitCode: EXIT_CODES.RESOURCE_BUSY,
|
|
146
|
-
suggestion:
|
|
162
|
+
suggestion: sessionUnavailableSuggestion(EXIT_CODES.RESOURCE_BUSY),
|
|
147
163
|
};
|
|
148
164
|
}
|
|
149
165
|
/**
|
|
@@ -270,7 +286,7 @@ export class SessionController {
|
|
|
270
286
|
const { sessionId: _sessionId, type: _type, ...params } = request;
|
|
271
287
|
try {
|
|
272
288
|
const execute = this.session.execute.bind(this.session);
|
|
273
|
-
const data = await withTimeout(execute(name, params),
|
|
289
|
+
const data = await withTimeout(execute(name, params), commandTimeoutMs(name, params), 'Command');
|
|
274
290
|
return { ...base, status: 'ok', data };
|
|
275
291
|
}
|
|
276
292
|
catch (error) {
|
|
@@ -367,7 +383,7 @@ export class SessionController {
|
|
|
367
383
|
return {
|
|
368
384
|
...base,
|
|
369
385
|
status: 'error',
|
|
370
|
-
message:
|
|
386
|
+
message: noActiveSessionMessage(),
|
|
371
387
|
errorCode: IPCErrorCode.NO_SESSION,
|
|
372
388
|
};
|
|
373
389
|
}
|
|
@@ -430,7 +446,7 @@ export class SessionController {
|
|
|
430
446
|
};
|
|
431
447
|
}
|
|
432
448
|
return {
|
|
433
|
-
message:
|
|
449
|
+
message: sessionAlreadyRunningMessage(process.pid),
|
|
434
450
|
errorCode: IPCErrorCode.SESSION_ALREADY_RUNNING,
|
|
435
451
|
existingSession: { ...existingSession, targetUrl },
|
|
436
452
|
};
|
package/dist/daemon/errors.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export declare class SessionDirError extends DaemonError {
|
|
|
54
54
|
/**
|
|
55
55
|
* @param message - What is wrong with the directory
|
|
56
56
|
* @param suggestion - How to fix it
|
|
57
|
-
* @param exitCode - 82 for permissions, 103 otherwise
|
|
57
|
+
* @param exitCode - 82 for permissions, 81 for a too-long path, 103 otherwise
|
|
58
58
|
*/
|
|
59
59
|
constructor(message: string, suggestion: string, exitCode?: number);
|
|
60
60
|
}
|
package/dist/daemon/errors.js
CHANGED
|
@@ -69,7 +69,7 @@ export class SessionDirError extends DaemonError {
|
|
|
69
69
|
/**
|
|
70
70
|
* @param message - What is wrong with the directory
|
|
71
71
|
* @param suggestion - How to fix it
|
|
72
|
-
* @param exitCode - 82 for permissions, 103 otherwise
|
|
72
|
+
* @param exitCode - 82 for permissions, 81 for a too-long path, 103 otherwise
|
|
73
73
|
*/
|
|
74
74
|
constructor(message, suggestion, exitCode = EXIT_CODES.SESSION_FILE_ERROR) {
|
|
75
75
|
super(message, 'SESSION_DIR_UNUSABLE', exitCode);
|
|
@@ -17,7 +17,8 @@ export declare function launchDaemon(): Promise<void>;
|
|
|
17
17
|
* Check that the session directory can hold the daemon's files before
|
|
18
18
|
* spawning it (otherwise the daemon dies and only its log says why).
|
|
19
19
|
*
|
|
20
|
-
* @throws SessionDirError (103) for a file
|
|
20
|
+
* @throws SessionDirError (103) for a file, (81) for a too-long path like a
|
|
21
|
+
* named session's, (82) when not writable
|
|
21
22
|
*/
|
|
22
23
|
export declare function assertUsableSessionDir(): void;
|
|
23
24
|
//# sourceMappingURL=launcher.d.ts.map
|