browser-debugger-cli 0.13.0 → 0.15.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 +101 -187
- package/README.md +4 -4
- package/dist/commands/cdp.js +1 -0
- package/dist/commands/cleanup.js +3 -0
- package/dist/commands/console.js +5 -1
- package/dist/commands/dom/a11y.d.ts +1 -1
- package/dist/commands/dom/a11y.js +20 -20
- package/dist/commands/dom/eval.d.ts +3 -1
- package/dist/commands/dom/eval.js +8 -5
- package/dist/commands/dom/formInteraction.js +1 -1
- package/dist/commands/dom/get.js +25 -7
- package/dist/commands/dom/helpers/evalResult.d.ts +36 -0
- package/dist/commands/dom/helpers/evalResult.js +59 -0
- package/dist/commands/dom/index.js +7 -2
- package/dist/commands/dom/query.d.ts +2 -1
- package/dist/commands/dom/query.js +5 -3
- package/dist/commands/dom/screenshot.js +1 -0
- package/dist/commands/helpJson.d.ts +1 -1
- package/dist/commands/helpJson.js +4 -4
- package/dist/commands/helpTopic.js +10 -4
- package/dist/commands/network/har.js +18 -14
- package/dist/commands/network/list.js +46 -3
- package/dist/commands/optionBehaviors.d.ts +25 -2
- package/dist/commands/optionBehaviors.js +60 -42
- package/dist/commands/peek.js +3 -0
- package/dist/commands/shared/CommandRunner.js +13 -13
- package/dist/commands/shared/daemonErrorHandler.js +2 -2
- package/dist/commands/shared/dataFetcher.d.ts +4 -2
- package/dist/commands/shared/dataFetcher.js +11 -3
- package/dist/commands/shared/handleValidationError.js +3 -3
- package/dist/commands/shared/optionTypes.d.ts +15 -3
- package/dist/commands/shared/outputFile.d.ts +2 -1
- package/dist/commands/shared/outputFile.js +7 -4
- package/dist/commands/shared/startHelpers.js +3 -3
- package/dist/commands/status.js +3 -1
- package/dist/commands/stop.js +2 -1
- package/dist/connection/chromeIdentity.d.ts +8 -2
- package/dist/connection/chromeIdentity.js +85 -13
- package/dist/connection/launcher/flagsBuilder.d.ts +46 -0
- package/dist/connection/launcher/flagsBuilder.js +107 -23
- package/dist/connection/launcher.d.ts +1 -1
- package/dist/connection/launcher.js +1 -2
- package/dist/constants.d.ts +31 -5
- package/dist/constants.js +37 -5
- package/dist/daemon/SessionController.js +2 -0
- package/dist/daemon/launcher.d.ts +17 -3
- package/dist/daemon/launcher.js +37 -7
- package/dist/daemon/session/Session.d.ts +2 -1
- package/dist/daemon/session/Session.js +10 -2
- package/dist/daemon/session/TelemetryStore.d.ts +7 -0
- package/dist/daemon/session/TelemetryStore.js +6 -0
- package/dist/daemon/session/commandRegistry.js +25 -7
- package/dist/daemon/session/matchedStylesReset.d.ts +26 -0
- package/dist/daemon/session/matchedStylesReset.js +46 -0
- package/dist/daemon/session/plugins.js +1 -0
- package/dist/daemon/session/triggeredRequests.d.ts +0 -5
- package/dist/daemon/session/triggeredRequests.js +13 -7
- package/dist/daemon.js +8742 -8315
- package/dist/errors/messages.d.ts +31 -0
- package/dist/errors/messages.js +96 -6
- package/dist/index.js +1129 -548
- package/dist/ipc/client.d.ts +6 -1
- package/dist/ipc/client.js +11 -2
- package/dist/ipc/protocol/commands.d.ts +8 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +5 -2
- package/dist/ipc/session/types.d.ts +5 -1
- package/dist/ipc/transport/index.d.ts +6 -0
- package/dist/ipc/transport/index.js +16 -1
- package/dist/program.d.ts +14 -0
- package/dist/program.js +53 -0
- package/dist/runtime/dom/elementGeometry.d.ts +23 -0
- package/dist/runtime/dom/elementGeometry.js +17 -15
- package/dist/runtime/dom/elementInfo.d.ts +13 -4
- package/dist/runtime/dom/elementInfo.js +15 -5
- package/dist/runtime/dom/evalHelpers.d.ts +24 -4
- package/dist/runtime/dom/evalHelpers.js +40 -12
- package/dist/runtime/dom/frameScopedConnection.d.ts +7 -0
- package/dist/runtime/dom/frameScopedConnection.js +2 -2
- package/dist/runtime/dom/frames.d.ts +2 -1
- package/dist/runtime/dom/frames.js +3 -1
- package/dist/runtime/dom/inspect.d.ts +17 -3
- package/dist/runtime/dom/inspect.js +40 -26
- package/dist/runtime/dom/inspectModel.d.ts +3 -3
- package/dist/runtime/dom/inspectRules.d.ts +29 -3
- package/dist/runtime/dom/inspectRules.js +205 -11
- package/dist/runtime/dom/layout.d.ts +0 -2
- package/dist/runtime/dom/layout.js +1 -2
- package/dist/runtime/dom/reactEventHelpers.d.ts +4 -1
- package/dist/runtime/dom/reactEventHelpers.js +9 -2
- package/dist/runtime/dom/targetNode.d.ts +10 -6
- package/dist/runtime/dom/targetNode.js +15 -8
- package/dist/runtime/page/emulation.js +6 -5
- package/dist/runtime/page/userAgent.d.ts +86 -2
- package/dist/runtime/page/userAgent.js +154 -33
- package/dist/session/paths.d.ts +38 -3
- package/dist/session/paths.js +154 -7
- package/dist/session/portClaims.d.ts +0 -8
- package/dist/session/portClaims.js +1 -22
- package/dist/session/sessionList.d.ts +5 -1
- package/dist/session/sessionList.js +5 -1
- package/dist/telemetry/a11y.d.ts +15 -1
- package/dist/telemetry/a11y.js +83 -0
- package/dist/telemetry/har/builder.d.ts +12 -1
- package/dist/telemetry/har/builder.js +11 -3
- package/dist/telemetry/har/sanitize.d.ts +24 -0
- package/dist/telemetry/har/sanitize.js +138 -0
- package/dist/telemetry/har/sanitizeBody.d.ts +38 -0
- package/dist/telemetry/har/sanitizeBody.js +168 -0
- package/dist/telemetry/network.d.ts +13 -16
- package/dist/telemetry/network.js +30 -52
- package/dist/telemetry/networkRetention.d.ts +83 -0
- package/dist/telemetry/networkRetention.js +117 -0
- package/dist/types.d.ts +26 -0
- package/dist/ui/OutputBuilder.d.ts +10 -0
- package/dist/ui/OutputBuilder.js +12 -0
- package/dist/ui/formatters/a11y.d.ts +5 -7
- package/dist/ui/formatters/a11y.js +7 -61
- package/dist/ui/formatters/console/chronological.js +4 -4
- package/dist/ui/formatters/console/follow.d.ts +4 -2
- package/dist/ui/formatters/console/follow.js +6 -3
- package/dist/ui/formatters/console/json.d.ts +3 -6
- package/dist/ui/formatters/console/json.js +9 -13
- package/dist/ui/formatters/console/shared.d.ts +17 -2
- package/dist/ui/formatters/console/shared.js +17 -0
- package/dist/ui/formatters/console/summarize.d.ts +2 -2
- package/dist/ui/formatters/console/summarize.js +22 -7
- package/dist/ui/formatters/console.d.ts +1 -1
- package/dist/ui/formatters/console.js +1 -5
- package/dist/ui/formatters/details.js +1 -1
- package/dist/ui/formatters/dom.d.ts +13 -4
- package/dist/ui/formatters/dom.js +25 -7
- package/dist/ui/formatters/layout.js +2 -1
- package/dist/ui/formatters/longValues.d.ts +14 -0
- package/dist/ui/formatters/longValues.js +23 -0
- package/dist/ui/formatters/networkList.d.ts +8 -2
- package/dist/ui/formatters/networkList.js +11 -2
- package/dist/ui/formatters/preview.d.ts +4 -1
- package/dist/ui/formatters/preview.js +55 -13
- package/dist/ui/formatters/sessions.d.ts +3 -2
- package/dist/ui/formatters/sessions.js +10 -3
- package/dist/ui/formatters/status.js +7 -0
- package/dist/ui/formatters/triggeredRequests.js +2 -1
- package/dist/ui/messages/chrome.d.ts +34 -7
- package/dist/ui/messages/chrome.js +81 -15
- package/dist/ui/messages/commands.d.ts +29 -8
- package/dist/ui/messages/commands.js +36 -8
- package/dist/ui/messages/networkMessages.d.ts +50 -0
- package/dist/ui/messages/networkMessages.js +66 -0
- package/dist/ui/messages/session.d.ts +8 -0
- package/dist/ui/messages/session.js +10 -0
- package/dist/utils/atomicFile.d.ts +2 -1
- package/dist/utils/atomicFile.js +5 -2
- package/dist/utils/directories.d.ts +41 -0
- package/dist/utils/directories.js +48 -0
- package/dist/utils/http.d.ts +9 -2
- package/dist/utils/http.js +4 -3
- package/dist/utils/strings.d.ts +19 -0
- package/dist/utils/strings.js +16 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1287,14 +1287,14 @@ var require_is_docker = __commonJS({
|
|
|
1287
1287
|
var require_is_wsl = __commonJS({
|
|
1288
1288
|
"node_modules/is-wsl/index.js"(exports, module) {
|
|
1289
1289
|
"use strict";
|
|
1290
|
-
var
|
|
1290
|
+
var os5 = __require("os");
|
|
1291
1291
|
var fs19 = __require("fs");
|
|
1292
1292
|
var isDocker = require_is_docker();
|
|
1293
1293
|
var isWsl3 = () => {
|
|
1294
1294
|
if (process.platform !== "linux") {
|
|
1295
1295
|
return false;
|
|
1296
1296
|
}
|
|
1297
|
-
if (
|
|
1297
|
+
if (os5.release().toLowerCase().includes("microsoft")) {
|
|
1298
1298
|
if (isDocker()) {
|
|
1299
1299
|
return false;
|
|
1300
1300
|
}
|
|
@@ -1327,16 +1327,25 @@ function resourceTypeFromName(name) {
|
|
|
1327
1327
|
([type, abbreviation]) => type.toLowerCase() === wanted || abbreviation.toLowerCase() === wanted
|
|
1328
1328
|
)?.[0];
|
|
1329
1329
|
}
|
|
1330
|
-
var HTTP_LOCALHOST, MAX_CONSOLE_MESSAGES, MAX_RESPONSE_SIZE, CHROME_NETWORK_BUFFER_TOTAL, CHROME_NETWORK_BUFFER_PER_RESOURCE, CHROME_POST_DATA_LIMIT, MAX_JSONL_BUFFER_SIZE, PORT_OPTION_DESCRIPTION, RESOURCE_TYPE_ABBREVIATIONS, MIME_TYPE_RULES;
|
|
1330
|
+
var HTTP_LOCALHOST, MAX_NETWORK_REQUESTS, MAX_CONSOLE_MESSAGES, MAX_RESPONSE_SIZE, MAX_TOTAL_BODY_BYTES, MAX_VALUE_LENGTH, MAX_CONSOLE_TEXT_LENGTH, MAX_CONSOLE_JSON_TEXT_LENGTH, CHROME_NETWORK_BUFFER_TOTAL, CHROME_NETWORK_BUFFER_PER_RESOURCE, CHROME_POST_DATA_LIMIT, QUERY_JSON_LIST_LIMIT, EVAL_JSON_ARRAY_LIMIT, LAYOUT_ELEMENT_LIMIT, MAX_TRIGGERED_REQUESTS, MAX_JSONL_BUFFER_SIZE, PORT_OPTION_DESCRIPTION, RESOURCE_TYPE_ABBREVIATIONS, MIME_TYPE_RULES;
|
|
1331
1331
|
var init_constants = __esm({
|
|
1332
1332
|
"src/constants.ts"() {
|
|
1333
1333
|
"use strict";
|
|
1334
1334
|
HTTP_LOCALHOST = "127.0.0.1";
|
|
1335
|
+
MAX_NETWORK_REQUESTS = 1e4;
|
|
1335
1336
|
MAX_CONSOLE_MESSAGES = 1e4;
|
|
1336
1337
|
MAX_RESPONSE_SIZE = 5 * 1024 * 1024;
|
|
1338
|
+
MAX_TOTAL_BODY_BYTES = 100 * 1024 * 1024;
|
|
1339
|
+
MAX_VALUE_LENGTH = 2e4;
|
|
1340
|
+
MAX_CONSOLE_TEXT_LENGTH = 200;
|
|
1341
|
+
MAX_CONSOLE_JSON_TEXT_LENGTH = 1e4;
|
|
1337
1342
|
CHROME_NETWORK_BUFFER_TOTAL = 50 * 1024 * 1024;
|
|
1338
1343
|
CHROME_NETWORK_BUFFER_PER_RESOURCE = 10 * 1024 * 1024;
|
|
1339
1344
|
CHROME_POST_DATA_LIMIT = 1 * 1024 * 1024;
|
|
1345
|
+
QUERY_JSON_LIST_LIMIT = 100;
|
|
1346
|
+
EVAL_JSON_ARRAY_LIMIT = 100;
|
|
1347
|
+
LAYOUT_ELEMENT_LIMIT = 100;
|
|
1348
|
+
MAX_TRIGGERED_REQUESTS = 50;
|
|
1340
1349
|
MAX_JSONL_BUFFER_SIZE = 256 * 1024 * 1024;
|
|
1341
1350
|
PORT_OPTION_DESCRIPTION = "Chrome debugging port";
|
|
1342
1351
|
RESOURCE_TYPE_ABBREVIATIONS = {
|
|
@@ -1481,11 +1490,30 @@ function makeDirectory(dir, mode) {
|
|
|
1481
1490
|
}
|
|
1482
1491
|
fs2.mkdirSync(dir, { recursive: true, ...mode !== void 0 && { mode } });
|
|
1483
1492
|
}
|
|
1484
|
-
|
|
1493
|
+
function dirTrustProblem(dir, options = {}) {
|
|
1494
|
+
const stat = fs2.lstatSync(dir);
|
|
1495
|
+
const problem = (reason, kind) => ({ reason, kind });
|
|
1496
|
+
if (stat.isSymbolicLink()) return problem("it is a symbolic link", "symlink");
|
|
1497
|
+
if (!stat.isDirectory()) return problem("not a directory", "not-directory");
|
|
1498
|
+
const uid = process.getuid?.();
|
|
1499
|
+
if (uid !== void 0 && stat.uid !== uid) return problem(`owned by uid ${stat.uid}`, "owner");
|
|
1500
|
+
if (process.platform === "win32") return null;
|
|
1501
|
+
const writeBits = options.allowGroupWrite ? OTHER_WRITE : GROUP_OTHER_WRITE;
|
|
1502
|
+
if ((stat.mode & writeBits) === 0) return null;
|
|
1503
|
+
const mode = (stat.mode & 4095).toString(8);
|
|
1504
|
+
if ((stat.mode & STICKY) !== 0 && (stat.mode & OTHER_WRITE) !== 0) {
|
|
1505
|
+
return problem(`a shared sticky directory (mode ${mode})`, "shared");
|
|
1506
|
+
}
|
|
1507
|
+
return problem(`writable by others (mode ${mode})`, "writable");
|
|
1508
|
+
}
|
|
1509
|
+
var PSEUDO_FILESYSTEM, GROUP_OTHER_WRITE, OTHER_WRITE, STICKY;
|
|
1485
1510
|
var init_directories = __esm({
|
|
1486
1511
|
"src/utils/directories.ts"() {
|
|
1487
1512
|
"use strict";
|
|
1488
1513
|
PSEUDO_FILESYSTEM = /^\/(proc|sys)(\/|$)/;
|
|
1514
|
+
GROUP_OTHER_WRITE = 18;
|
|
1515
|
+
OTHER_WRITE = 2;
|
|
1516
|
+
STICKY = 512;
|
|
1489
1517
|
}
|
|
1490
1518
|
});
|
|
1491
1519
|
|
|
@@ -1494,12 +1522,16 @@ import * as fs3 from "fs";
|
|
|
1494
1522
|
import * as os2 from "os";
|
|
1495
1523
|
import * as path3 from "path";
|
|
1496
1524
|
function getSessionBaseDir() {
|
|
1497
|
-
const override =
|
|
1498
|
-
if (override
|
|
1525
|
+
const override = sessionDirOverride();
|
|
1526
|
+
if (override !== null) {
|
|
1499
1527
|
return path3.isAbsolute(override) ? override : path3.resolve(override);
|
|
1500
1528
|
}
|
|
1501
1529
|
return path3.join(os2.homedir(), ".bdg");
|
|
1502
1530
|
}
|
|
1531
|
+
function sessionDirOverride() {
|
|
1532
|
+
const override = process.env[SESSION_DIR_OVERRIDE_ENV];
|
|
1533
|
+
return override && override.trim().length > 0 ? override : null;
|
|
1534
|
+
}
|
|
1503
1535
|
function getSessionName() {
|
|
1504
1536
|
const name = process.env[SESSION_NAME_ENV]?.trim();
|
|
1505
1537
|
if (!name) return null;
|
|
@@ -1535,14 +1567,81 @@ function getDaemonSocketPath() {
|
|
|
1535
1567
|
return getSessionFilePath("DAEMON_SOCKET");
|
|
1536
1568
|
}
|
|
1537
1569
|
function ensureSessionDir() {
|
|
1538
|
-
makeDirectory(getSessionDir());
|
|
1570
|
+
makeDirectory(getSessionDir(), PRIVATE_DIR_MODE);
|
|
1571
|
+
}
|
|
1572
|
+
function sessionDirChain(dir) {
|
|
1573
|
+
const base = getSessionBaseDir();
|
|
1574
|
+
const relative2 = path3.relative(base, dir);
|
|
1575
|
+
if (relative2 === ".." || relative2.startsWith(`..${path3.sep}`) || path3.isAbsolute(relative2)) {
|
|
1576
|
+
return [{ dir, bdgOwned: false, isBase: false }];
|
|
1577
|
+
}
|
|
1578
|
+
const chain = [{ dir: base, bdgOwned: sessionDirOverride() === null, isBase: true }];
|
|
1579
|
+
let current = base;
|
|
1580
|
+
for (const part of relative2.split(path3.sep).filter(Boolean)) {
|
|
1581
|
+
current = path3.join(current, part);
|
|
1582
|
+
chain.push({ dir: current, bdgOwned: true, isBase: false });
|
|
1583
|
+
}
|
|
1584
|
+
return chain;
|
|
1585
|
+
}
|
|
1586
|
+
function tightenDir(dir) {
|
|
1587
|
+
if (process.platform === "win32") return;
|
|
1588
|
+
let fd;
|
|
1589
|
+
try {
|
|
1590
|
+
const { O_RDONLY, O_DIRECTORY, O_NOFOLLOW } = fs3.constants;
|
|
1591
|
+
fd = fs3.openSync(dir, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
|
|
1592
|
+
const stat = fs3.fstatSync(fd);
|
|
1593
|
+
if (stat.uid !== process.getuid?.() || (stat.mode & GROUP_OTHER_ACCESS) === 0) return;
|
|
1594
|
+
fs3.fchmodSync(fd, PRIVATE_DIR_MODE);
|
|
1595
|
+
log.debug(`Session directory ${dir} restricted to mode 700`);
|
|
1596
|
+
} catch (error) {
|
|
1597
|
+
log.debug(`Session directory ${dir} not restricted: ${getErrorMessage(error)}`);
|
|
1598
|
+
} finally {
|
|
1599
|
+
if (fd !== void 0) fs3.closeSync(fd);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
function chainDirProblem(entry) {
|
|
1603
|
+
const problem = dirTrustProblem(entry.dir, SESSION_DIR_TRUST);
|
|
1604
|
+
if (problem?.kind !== "symlink" || !entry.isBase) return problem;
|
|
1605
|
+
const target = fs3.realpathSync(entry.dir);
|
|
1606
|
+
const targetProblem = dirTrustProblem(target, SESSION_DIR_TRUST);
|
|
1607
|
+
return targetProblem && {
|
|
1608
|
+
...targetProblem,
|
|
1609
|
+
reason: `it links to ${target}, which is ${targetProblem.reason}`
|
|
1610
|
+
};
|
|
1539
1611
|
}
|
|
1540
|
-
|
|
1612
|
+
function acceptTrustedDir(entry) {
|
|
1613
|
+
if (entry.bdgOwned) {
|
|
1614
|
+
tightenDir(entry.dir);
|
|
1615
|
+
return;
|
|
1616
|
+
}
|
|
1617
|
+
const mode = fs3.statSync(entry.dir, { throwIfNoEntry: false })?.mode ?? 0;
|
|
1618
|
+
if ((mode & GROUP_OTHER_ACCESS) === 0) return;
|
|
1619
|
+
log.debug(
|
|
1620
|
+
`Session directory ${entry.dir} is open to group or others; left as is (chosen with ${SESSION_DIR_OVERRIDE_ENV})`
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
function secureSessionDir(dir = getSessionDir()) {
|
|
1624
|
+
for (const entry of sessionDirChain(dir)) {
|
|
1625
|
+
let problem;
|
|
1626
|
+
try {
|
|
1627
|
+
problem = chainDirProblem(entry);
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
const code = error.code;
|
|
1630
|
+
if (code === "ENOENT" || code === "ENOTDIR") continue;
|
|
1631
|
+
problem = { reason: getErrorMessage(error), kind: "not-directory" };
|
|
1632
|
+
}
|
|
1633
|
+
if (problem !== null) return { dir: entry.dir, ...problem, bdgOwned: entry.bdgOwned };
|
|
1634
|
+
acceptTrustedDir(entry);
|
|
1635
|
+
}
|
|
1636
|
+
return null;
|
|
1637
|
+
}
|
|
1638
|
+
var log, SESSION_FILES, SESSION_DIR_OVERRIDE_ENV, SESSION_NAME_ENV, NAMED_SESSIONS_DIR, MAX_SOCKET_PATH_BYTES, MAX_DAEMON_SOCKET_PATH_BYTES, SESSION_STATE_FILES, PRIVATE_DIR_MODE, GROUP_OTHER_ACCESS, SESSION_DIR_TRUST;
|
|
1541
1639
|
var init_paths = __esm({
|
|
1542
1640
|
"src/session/paths.ts"() {
|
|
1543
1641
|
"use strict";
|
|
1544
1642
|
init_logging();
|
|
1545
1643
|
init_directories();
|
|
1644
|
+
init_errors();
|
|
1546
1645
|
log = createLogger("session");
|
|
1547
1646
|
SESSION_FILES = {
|
|
1548
1647
|
OUTPUT: "session.json",
|
|
@@ -1564,6 +1663,9 @@ var init_paths = __esm({
|
|
|
1564
1663
|
"CHROME_PID",
|
|
1565
1664
|
"METADATA"
|
|
1566
1665
|
];
|
|
1666
|
+
PRIVATE_DIR_MODE = 448;
|
|
1667
|
+
GROUP_OTHER_ACCESS = 63;
|
|
1668
|
+
SESSION_DIR_TRUST = { allowGroupWrite: true };
|
|
1567
1669
|
}
|
|
1568
1670
|
});
|
|
1569
1671
|
|
|
@@ -1645,14 +1747,17 @@ var init_atomicFile = __esm({
|
|
|
1645
1747
|
*
|
|
1646
1748
|
* @param filePath - Target file path
|
|
1647
1749
|
* @param data - Data to write
|
|
1648
|
-
* @param options - Write options
|
|
1750
|
+
* @param options - Write options (`mode`: permissions of the new file, less the umask)
|
|
1649
1751
|
* @returns Promise that resolves when write completes
|
|
1650
1752
|
* @throws Error if write operation fails
|
|
1651
1753
|
*/
|
|
1652
1754
|
static async writeAsync(filePath, data, options = {}) {
|
|
1653
1755
|
const tmpPath = this.getTempPath(filePath);
|
|
1654
1756
|
try {
|
|
1655
|
-
await fs4.promises.writeFile(tmpPath, data, {
|
|
1757
|
+
await fs4.promises.writeFile(tmpPath, data, {
|
|
1758
|
+
encoding: options.encoding ?? "utf-8",
|
|
1759
|
+
...options.mode !== void 0 && { mode: options.mode }
|
|
1760
|
+
});
|
|
1656
1761
|
await fs4.promises.rename(tmpPath, filePath);
|
|
1657
1762
|
} catch (error) {
|
|
1658
1763
|
try {
|
|
@@ -2163,6 +2268,12 @@ function truncateByLength(text, maxLength = DEFAULT_MAX_LENGTH) {
|
|
|
2163
2268
|
}
|
|
2164
2269
|
return text.slice(0, maxLength - 1) + "\u2026";
|
|
2165
2270
|
}
|
|
2271
|
+
function capLength(text, maxLength) {
|
|
2272
|
+
if (text.length <= maxLength) return { text };
|
|
2273
|
+
const lastKept = text.charCodeAt(maxLength - 1);
|
|
2274
|
+
const end = lastKept >= 55296 && lastKept <= 56319 ? maxLength - 1 : maxLength;
|
|
2275
|
+
return { text: text.slice(0, end), truncatedFrom: text.length };
|
|
2276
|
+
}
|
|
2166
2277
|
var DEFAULT_MAX_LENGTH;
|
|
2167
2278
|
var init_strings = __esm({
|
|
2168
2279
|
"src/utils/strings.ts"() {
|
|
@@ -2178,8 +2289,8 @@ function chromeClosedMessage(pid) {
|
|
|
2178
2289
|
function orphanedDaemonsCleanedMessage(count) {
|
|
2179
2290
|
return `Cleaned up ${count} orphaned daemon process${count === 1 ? "" : "es"}`;
|
|
2180
2291
|
}
|
|
2181
|
-
function skillBackupMessage(
|
|
2182
|
-
return `previous copy kept in ${
|
|
2292
|
+
function skillBackupMessage(path8) {
|
|
2293
|
+
return `previous copy kept in ${path8}`;
|
|
2183
2294
|
}
|
|
2184
2295
|
function fieldLabelList(labels) {
|
|
2185
2296
|
const shown = labels.slice(0, 5).map((label) => label.replace(/\s*:\s*$/, "")).join(", ");
|
|
@@ -2228,8 +2339,7 @@ function newMessageText(message) {
|
|
|
2228
2339
|
return `"${message.text}" (${message.element})`;
|
|
2229
2340
|
}
|
|
2230
2341
|
function moreMatchesNote(hidden, jsonLimit) {
|
|
2231
|
-
|
|
2232
|
-
return `... and ${hidden} more (${where})`;
|
|
2342
|
+
return `... and ${hidden} more (--json lists up to ${jsonLimit})`;
|
|
2233
2343
|
}
|
|
2234
2344
|
function queryMoreMatchesNote(omitted, indexed) {
|
|
2235
2345
|
const indices = indexed === void 0 ? "" : `; indices 0-${indexed - 1} work with other commands`;
|
|
@@ -2238,6 +2348,12 @@ function queryMoreMatchesNote(omitted, indexed) {
|
|
|
2238
2348
|
function queryViewportCheckedNote(checked) {
|
|
2239
2349
|
return `Visibility is checked for the first ${checked} matches only; ${sessionCommand("bdg dom layout <index>")} checks any of them`;
|
|
2240
2350
|
}
|
|
2351
|
+
function a11yTreeShownNote(listed) {
|
|
2352
|
+
return `Showing the first ${listed} nodes (text boxes and repeated text left out)`;
|
|
2353
|
+
}
|
|
2354
|
+
function a11yTreeMoreNote(omitted) {
|
|
2355
|
+
return `${omitted} more: --limit 0 lists all, --depth <n> limits the levels, or search with bdg dom a11y query "role:<role>"`;
|
|
2356
|
+
}
|
|
2241
2357
|
function a11yMoreMatchesNote(omitted) {
|
|
2242
2358
|
return `... and ${omitted} more (--limit 0 lists all; their indices work too)`;
|
|
2243
2359
|
}
|
|
@@ -2344,6 +2460,8 @@ function coverText(cover, transparent) {
|
|
|
2344
2460
|
return transparent ? `under transparent ${cover} (clicks land on it)` : `covered by ${cover}`;
|
|
2345
2461
|
}
|
|
2346
2462
|
function inspectCascadeNote(reason) {
|
|
2463
|
+
if (reason === "skipped")
|
|
2464
|
+
return "hints skipped: this page's stylesheets are slow to read (--rules waits 5 s)";
|
|
2347
2465
|
return reason === "timeout" ? "CSS rules not read: the page's stylesheets took too long (hints wait 1 s; --rules and --why 5 s)" : "CSS rules not read: Chrome could not report the rules matching this element";
|
|
2348
2466
|
}
|
|
2349
2467
|
function inspectPseudoOfNote(pseudo) {
|
|
@@ -2574,6 +2692,9 @@ function inspectMidTransitionNote() {
|
|
|
2574
2692
|
function helpJsonDetailsNote() {
|
|
2575
2693
|
return "Option behaviors, defaults, choices and examples: bdg <command> --help --json (e.g. bdg dom query --help --json). Everything at once: bdg --help --json --full";
|
|
2576
2694
|
}
|
|
2695
|
+
function moreCharsNote(count) {
|
|
2696
|
+
return `\u2026 ${count} more chars (use --full)`;
|
|
2697
|
+
}
|
|
2577
2698
|
var REQUIRED_FIELD_EMPTY_REASON, NO_VISIBLE_EFFECT, HOVER_OFF_DONE, HOVER_USAGE, LAYOUT_REASONS, SCROLL_LOCKED_PREFIX, VIEWPORT_POSITION_HINTS, INSPECT_OUTPUT_LEGEND, COLLAPSED_LISTENERS_HEADING, NO_LISTENERS_HINT, NOTE_TYPES_SHOWN, POINTER_ACTION_DONE, POINTER_ACTION_NOUN, PAGE_ACTION_DONE, PAGE_INFO_DESCRIPTION, PAGE_EMULATE_DESCRIPTION, PAGE_ACTION_DESCRIPTIONS, CLICK_RESULT_WAIT_HELP, WAIT_HELP_EXAMPLES, FRAME_URL_MAX_LENGTH, AUDIT_HELP_EXAMPLES, CSS_SEARCH_HELP_EXAMPLES;
|
|
2578
2699
|
var init_commands = __esm({
|
|
2579
2700
|
"src/ui/messages/commands.ts"() {
|
|
@@ -3096,6 +3217,7 @@ var init_shellDetection = __esm({
|
|
|
3096
3217
|
});
|
|
3097
3218
|
|
|
3098
3219
|
// src/errors/messages.ts
|
|
3220
|
+
import * as os3 from "os";
|
|
3099
3221
|
import * as path4 from "path";
|
|
3100
3222
|
function sessionAlreadyRunningError(pid, duration, targetUrl) {
|
|
3101
3223
|
return joinLines(
|
|
@@ -3267,8 +3389,13 @@ function unknownHelpTopicError(topic, closest) {
|
|
|
3267
3389
|
suggestion: closest ? `Did you mean: bdg help ${closest}?` : `Run "bdg ${parent ? `${parent} ` : ""}--help" for commands`
|
|
3268
3390
|
};
|
|
3269
3391
|
}
|
|
3270
|
-
function
|
|
3271
|
-
|
|
3392
|
+
function didYouMeanSuggestion(candidates) {
|
|
3393
|
+
const last = candidates.at(-1) ?? "";
|
|
3394
|
+
const rest = candidates.slice(0, -1);
|
|
3395
|
+
return `Did you mean: ${rest.length > 0 ? `${rest.join(", ")} or ${last}` : last}?`;
|
|
3396
|
+
}
|
|
3397
|
+
function usageHelpSuggestion(path8) {
|
|
3398
|
+
return `Run "${path8} --help" for usage`;
|
|
3272
3399
|
}
|
|
3273
3400
|
function missingSubcommandMessage() {
|
|
3274
3401
|
return "Missing subcommand";
|
|
@@ -3325,7 +3452,7 @@ function invalidSessionNameError(name, maxLength) {
|
|
|
3325
3452
|
function sessionNameSocketTooLongError(name, socketPath, max) {
|
|
3326
3453
|
return {
|
|
3327
3454
|
message: `Session name "${name}" makes the daemon socket path too long (${Buffer.byteLength(socketPath)} bytes, at most ${max}): ${socketPath}`,
|
|
3328
|
-
suggestion:
|
|
3455
|
+
suggestion: `Use a shorter session name, or a shorter BDG_SESSION_DIR (e.g. ${privateSessionDirExample()})`
|
|
3329
3456
|
};
|
|
3330
3457
|
}
|
|
3331
3458
|
function purgeNeedsNamedSessionError() {
|
|
@@ -3340,19 +3467,52 @@ function purgeRefusedError(dir, reason) {
|
|
|
3340
3467
|
suggestion: `End the session first (${sessionCommand("bdg cleanup --force")}), then retry: ${sessionCommand("bdg cleanup --purge")}`
|
|
3341
3468
|
};
|
|
3342
3469
|
}
|
|
3470
|
+
function privateSessionDirExample() {
|
|
3471
|
+
const runtimeDir = process.env["XDG_RUNTIME_DIR"]?.trim();
|
|
3472
|
+
if (runtimeDir) return path4.join(runtimeDir, "bdg");
|
|
3473
|
+
const uid = process.getuid?.();
|
|
3474
|
+
return path4.join(os3.tmpdir(), uid === void 0 ? "bdg" : `bdg-${uid}`);
|
|
3475
|
+
}
|
|
3476
|
+
function sessionDirSuggestion() {
|
|
3477
|
+
return `Set BDG_SESSION_DIR to a short directory only you can write to, e.g. BDG_SESSION_DIR=${privateSessionDirExample()}`;
|
|
3478
|
+
}
|
|
3343
3479
|
function sessionDirIsFileError(dir) {
|
|
3344
|
-
return { message: `Session directory ${dir} is a file`, suggestion:
|
|
3480
|
+
return { message: `Session directory ${dir} is a file`, suggestion: sessionDirSuggestion() };
|
|
3345
3481
|
}
|
|
3346
3482
|
function sessionDirNotWritableError(dir, reason) {
|
|
3347
3483
|
return {
|
|
3348
3484
|
message: `Session directory ${dir} is not writable (${reason})`,
|
|
3349
|
-
suggestion:
|
|
3485
|
+
suggestion: sessionDirSuggestion()
|
|
3486
|
+
};
|
|
3487
|
+
}
|
|
3488
|
+
function untrustedSessionDirError(untrusted) {
|
|
3489
|
+
return {
|
|
3490
|
+
message: `Session directory ${untrusted.dir} is not safe to use: ${untrusted.reason}`,
|
|
3491
|
+
suggestion: untrustedSessionDirSuggestion(untrusted)
|
|
3492
|
+
};
|
|
3493
|
+
}
|
|
3494
|
+
function untrustedSessionDirSuggestion(untrusted) {
|
|
3495
|
+
const { dir, kind, bdgOwned } = untrusted;
|
|
3496
|
+
const example = `BDG_SESSION_DIR=${privateSessionDirExample()}`;
|
|
3497
|
+
if (kind === "symlink") {
|
|
3498
|
+
return `Remove the link (rm ${dir}) or point BDG_SESSION_DIR at the real directory`;
|
|
3499
|
+
}
|
|
3500
|
+
if (kind === "shared") {
|
|
3501
|
+
return `${dir} is shared by all users; use a subdirectory only you can write to, e.g. ${example}`;
|
|
3502
|
+
}
|
|
3503
|
+
if (bdgOwned) return `Run chmod 700 ${dir} (or remove it if it is not yours), then retry`;
|
|
3504
|
+
return `Use a directory you own that others cannot write to, e.g. ${example} (or chmod 700 ${dir} if it is yours)`;
|
|
3505
|
+
}
|
|
3506
|
+
function daemonLogNotOpenedError(logPath, code) {
|
|
3507
|
+
return {
|
|
3508
|
+
message: `Cannot open the daemon log ${logPath} (${code === "ELOOP" ? "it is a symbolic link" : code})`,
|
|
3509
|
+
suggestion: `Remove ${logPath} if you did not create it, then retry`
|
|
3350
3510
|
};
|
|
3351
3511
|
}
|
|
3352
3512
|
function socketPathTooLongError(socketPath, max) {
|
|
3353
3513
|
return {
|
|
3354
3514
|
message: `Session directory path is too long for the daemon socket (${Buffer.byteLength(socketPath)} bytes, at most ${max}): ${socketPath}`,
|
|
3355
|
-
suggestion:
|
|
3515
|
+
suggestion: sessionDirSuggestion()
|
|
3356
3516
|
};
|
|
3357
3517
|
}
|
|
3358
3518
|
function externalChromeUnreachableError(endpoint, secure) {
|
|
@@ -3691,10 +3851,10 @@ function skillSourceMissingError(source) {
|
|
|
3691
3851
|
suggestion: "Reinstall bdg: npm install -g browser-debugger-cli"
|
|
3692
3852
|
};
|
|
3693
3853
|
}
|
|
3694
|
-
function skillWriteFailedError(
|
|
3854
|
+
function skillWriteFailedError(path8, reason, pathIsFile, otherTargetFlag) {
|
|
3695
3855
|
const fix = pathIsFile ? "A part of that path is a file, not a directory: move it away" : "Check the permissions of that directory";
|
|
3696
3856
|
return {
|
|
3697
|
-
message: `Could not write ${
|
|
3857
|
+
message: `Could not write ${path8}: ${reason}`,
|
|
3698
3858
|
suggestion: otherTargetFlag ? `${fix}, or install for the other agent only (${otherTargetFlag})` : fix
|
|
3699
3859
|
};
|
|
3700
3860
|
}
|
|
@@ -3707,7 +3867,7 @@ function intervalWithoutFollowError() {
|
|
|
3707
3867
|
function unknownAuditCheckMessage(value, similar, checks) {
|
|
3708
3868
|
return `Unknown check "${value}"${similar.length > 0 ? `; did you mean ${similar[0]}?` : ""} (checks: ${checks.join(", ")})`;
|
|
3709
3869
|
}
|
|
3710
|
-
var LAUNCHED_CHROME_DESCRIPTION, A11Y_NAME_QUERY_EXAMPLE, SCOPED_FILTER_EXAMPLES, PLAYWRIGHT_ONLY_SYNTAX, MALFORMED_SELECTOR_DETAILS,
|
|
3870
|
+
var LAUNCHED_CHROME_DESCRIPTION, A11Y_NAME_QUERY_EXAMPLE, SCOPED_FILTER_EXAMPLES, PLAYWRIGHT_ONLY_SYNTAX, MALFORMED_SELECTOR_DETAILS, SCROLL_PROBLEMS, VIA_LABEL_SUFFIX, NAME_QUERY_PLACEHOLDER, LABEL_WITHOUT_CONTROL, REDECLARED_PATTERN;
|
|
3711
3871
|
var init_messages = __esm({
|
|
3712
3872
|
"src/errors/messages.ts"() {
|
|
3713
3873
|
"use strict";
|
|
@@ -3728,7 +3888,6 @@ var init_messages = __esm({
|
|
|
3728
3888
|
"empty-has": () => ":has() has an empty selector",
|
|
3729
3889
|
"scope-in-has": () => ":has() with filters is already relative to the element; write :has(> a:visible) instead of :has(:scope > a:visible)"
|
|
3730
3890
|
};
|
|
3731
|
-
SESSION_DIR_SUGGESTION = "Set BDG_SESSION_DIR to a short, writable directory, e.g. BDG_SESSION_DIR=/tmp/bdg";
|
|
3732
3891
|
SCROLL_PROBLEMS = {
|
|
3733
3892
|
"index-without-selector": [
|
|
3734
3893
|
"--index requires a selector",
|
|
@@ -4051,7 +4210,17 @@ var init_validation2 = __esm({
|
|
|
4051
4210
|
});
|
|
4052
4211
|
|
|
4053
4212
|
// src/ipc/transport/index.ts
|
|
4213
|
+
import * as path5 from "path";
|
|
4054
4214
|
async function sendRequest(request, requestName, expectedType, timeoutMs = getIPCRequestTimeout(), socketPath = getDaemonSocketPath()) {
|
|
4215
|
+
const untrusted = secureSessionDir(path5.dirname(socketPath));
|
|
4216
|
+
if (untrusted) {
|
|
4217
|
+
const err = untrustedSessionDirError(untrusted);
|
|
4218
|
+
throw new CommandError(
|
|
4219
|
+
err.message,
|
|
4220
|
+
{ suggestion: err.suggestion },
|
|
4221
|
+
EXIT_CODES.SESSION_FILE_ERROR
|
|
4222
|
+
);
|
|
4223
|
+
}
|
|
4055
4224
|
return new Promise((resolve4, reject2) => {
|
|
4056
4225
|
const buffer = new JSONLBuffer();
|
|
4057
4226
|
let resolved = false;
|
|
@@ -4110,8 +4279,11 @@ var init_transport = __esm({
|
|
|
4110
4279
|
"src/ipc/transport/index.ts"() {
|
|
4111
4280
|
"use strict";
|
|
4112
4281
|
init_constants();
|
|
4282
|
+
init_errors3();
|
|
4283
|
+
init_messages();
|
|
4113
4284
|
init_paths();
|
|
4114
4285
|
init_logging();
|
|
4286
|
+
init_exitCodes();
|
|
4115
4287
|
init_errors4();
|
|
4116
4288
|
init_jsonl();
|
|
4117
4289
|
init_socket();
|
|
@@ -4287,8 +4459,12 @@ async function callCDP(method, params, options = {}) {
|
|
|
4287
4459
|
function callBdgScript(method, params) {
|
|
4288
4460
|
return callCDP(method, params, { isolated: true });
|
|
4289
4461
|
}
|
|
4290
|
-
async function domEval(script, frame) {
|
|
4291
|
-
return sendCommand("dom_eval", {
|
|
4462
|
+
async function domEval(script, frame, full) {
|
|
4463
|
+
return sendCommand("dom_eval", {
|
|
4464
|
+
script,
|
|
4465
|
+
...frame !== void 0 && { frame },
|
|
4466
|
+
...full && { full }
|
|
4467
|
+
});
|
|
4292
4468
|
}
|
|
4293
4469
|
async function domFrames() {
|
|
4294
4470
|
return sendCommand("dom_frames", {});
|
|
@@ -8085,7 +8261,7 @@ function textPreview2(text, length = PREVIEW_LENGTH) {
|
|
|
8085
8261
|
const characters = Array.from(collapsed);
|
|
8086
8262
|
return characters.length > length ? characters.slice(0, length).join("") + "..." : collapsed;
|
|
8087
8263
|
}
|
|
8088
|
-
var CLASS_FRAGMENT, LABEL_CLASSES_JS, PREVIEW_LENGTH, ELEMENT_TEXT_LENGTH, WITHOUT_DECORATIONS_JS, COMPOSED_JS, FLAT_TEXT_JS, ELEMENT_TEXT_JS, MASKED_VALUE, SENSITIVE_FIELD_JS, ELEMENT_STATE_JS, ELEMENT_DESCRIPTION_JS, SIBLING_POSITION_JS, ELEMENT_IDENTITY_JS, ELEMENT_CONTEXT_JS;
|
|
8264
|
+
var CLASS_FRAGMENT, LABEL_CLASSES_JS, PREVIEW_LENGTH, ELEMENT_TEXT_LENGTH, WITHOUT_DECORATIONS_JS, COMPOSED_JS, FLAT_TEXT_JS, ELEMENT_TEXT_JS, MASKED_VALUE, SENSITIVE_NAME_SOURCE, SENSITIVE_FIELD_JS, ELEMENT_STATE_JS, ELEMENT_DESCRIPTION_JS, SIBLING_POSITION_JS, ELEMENT_IDENTITY_JS, ELEMENT_CONTEXT_JS;
|
|
8089
8265
|
var init_elementInfo = __esm({
|
|
8090
8266
|
"src/runtime/dom/elementInfo.ts"() {
|
|
8091
8267
|
"use strict";
|
|
@@ -8126,7 +8302,7 @@ var init_elementInfo = __esm({
|
|
|
8126
8302
|
while (walker.nextNode()) if (composes(walker.currentNode)) return true;
|
|
8127
8303
|
return false;
|
|
8128
8304
|
}`;
|
|
8129
|
-
FLAT_TEXT_JS = `(el, limit) => {
|
|
8305
|
+
FLAT_TEXT_JS = `(el, limit, fields) => {
|
|
8130
8306
|
const composed = ${COMPOSED_JS};
|
|
8131
8307
|
const view = el.ownerDocument.defaultView;
|
|
8132
8308
|
let text = '';
|
|
@@ -8136,7 +8312,8 @@ var init_elementInfo = __esm({
|
|
|
8136
8312
|
if (text.length >= limit) return;
|
|
8137
8313
|
if (node.nodeType === 3 && visible) text += node.data.replace(/\\s+/g, ' ');
|
|
8138
8314
|
if (node.nodeType !== 1) return;
|
|
8139
|
-
if (/^(input|textarea
|
|
8315
|
+
if (/^(input|textarea)$/.test(node.localName)) return;
|
|
8316
|
+
if (!fields && (node.localName === 'select' || node.isContentEditable)) return;
|
|
8140
8317
|
const style = view.getComputedStyle(node);
|
|
8141
8318
|
if (node.checkVisibility && !node.checkVisibility() && style.display !== 'contents') return;
|
|
8142
8319
|
if (node.localName === 'br') text += '\\n';
|
|
@@ -8175,12 +8352,13 @@ var init_elementInfo = __esm({
|
|
|
8175
8352
|
return withoutDecorations(el, start);
|
|
8176
8353
|
}`;
|
|
8177
8354
|
MASKED_VALUE = "\u2022\u2022\u2022\u2022";
|
|
8355
|
+
SENSITIVE_NAME_SOURCE = "passw|passwd|pwd|passcode|(^|[^a-z])otp([^a-z]|$)|cvv|cvc";
|
|
8178
8356
|
SENSITIVE_FIELD_JS = `(el) => {
|
|
8179
8357
|
const autocomplete = el.getAttribute('autocomplete') || '';
|
|
8180
8358
|
if (/(^|\\s)(cc-[a-z-]+|one-time-code|current-password|new-password)(\\s|$)/i.test(autocomplete)) return true;
|
|
8181
8359
|
if (el.type === 'password' || /^password$/i.test(el.getAttribute('type') || '')) return true;
|
|
8182
8360
|
const names = [el.getAttribute('name'), el.id, autocomplete].join(' ');
|
|
8183
|
-
if (/
|
|
8361
|
+
if (/${SENSITIVE_NAME_SOURCE}/i.test(names)) return true;
|
|
8184
8362
|
try {
|
|
8185
8363
|
const security = el.ownerDocument.defaultView.getComputedStyle(el).getPropertyValue('-webkit-text-security');
|
|
8186
8364
|
return Boolean(security) && security !== 'none';
|
|
@@ -8544,7 +8722,7 @@ var init_elementGeometry = __esm({
|
|
|
8544
8722
|
/transform|filter|perspective/.test(style.willChange) || /paint|layout|strict|content/.test(style.contain)`;
|
|
8545
8723
|
FIXED_TO_VIEWPORT_JS = `(n) => {
|
|
8546
8724
|
const holdsFixed = ${HOLDS_FIXED_JS};
|
|
8547
|
-
const parentOf =
|
|
8725
|
+
const parentOf = ${FLAT_PARENT_JS};
|
|
8548
8726
|
for (let p = parentOf(n); p && p !== n.ownerDocument.documentElement; p = parentOf(p)) {
|
|
8549
8727
|
if (holdsFixed(p.ownerDocument.defaultView.getComputedStyle(p))) return false;
|
|
8550
8728
|
}
|
|
@@ -8557,7 +8735,7 @@ var init_elementGeometry = __esm({
|
|
|
8557
8735
|
const fixedToViewport = ${FIXED_TO_VIEWPORT_JS};
|
|
8558
8736
|
const reasons = ${JSON.stringify(LAYOUT_REASONS)};
|
|
8559
8737
|
const styleOf = (n) => n.ownerDocument.defaultView.getComputedStyle(n);
|
|
8560
|
-
const parentOf =
|
|
8738
|
+
const parentOf = ${FLAT_PARENT_JS};
|
|
8561
8739
|
const doc = node.ownerDocument;
|
|
8562
8740
|
const rootStyle = styleOf(doc.documentElement);
|
|
8563
8741
|
const bodyClips = rootStyle.overflowX !== 'visible' || rootStyle.overflowY !== 'visible';
|
|
@@ -8771,13 +8949,13 @@ var init_frameScopedConnection = __esm({
|
|
|
8771
8949
|
});
|
|
8772
8950
|
|
|
8773
8951
|
// src/runtime/page/bdgWorld.ts
|
|
8774
|
-
var
|
|
8952
|
+
var log21;
|
|
8775
8953
|
var init_bdgWorld = __esm({
|
|
8776
8954
|
"src/runtime/page/bdgWorld.ts"() {
|
|
8777
8955
|
"use strict";
|
|
8778
8956
|
init_logging();
|
|
8779
8957
|
init_errors();
|
|
8780
|
-
|
|
8958
|
+
log21 = createLogger("cdp");
|
|
8781
8959
|
}
|
|
8782
8960
|
});
|
|
8783
8961
|
|
|
@@ -8965,10 +9143,10 @@ function findHasFilters(text, selector) {
|
|
|
8965
9143
|
}
|
|
8966
9144
|
function parseHasArgument(argument, selector) {
|
|
8967
9145
|
if (!FILTER_NAME_HINT.test(argument)) return null;
|
|
8968
|
-
const selectors = splitSelectorList(argument).map((
|
|
8969
|
-
if (
|
|
8970
|
-
if (SCOPE_PSEUDO.test(
|
|
8971
|
-
const { leading, steps } = parseChain(
|
|
9146
|
+
const selectors = splitSelectorList(argument).map((relative2) => {
|
|
9147
|
+
if (relative2.trim() === "") reject(malformedSelectorError(selector, "empty-has"));
|
|
9148
|
+
if (SCOPE_PSEUDO.test(relative2)) reject(malformedSelectorError(selector, "scope-in-has"));
|
|
9149
|
+
const { leading, steps } = parseChain(relative2, selector);
|
|
8972
9150
|
if (leading && leading !== ">") reject(siblingInHasError(selector, leading));
|
|
8973
9151
|
const [first, ...rest] = steps;
|
|
8974
9152
|
return first ? [{ ...first, combinator: leading || " " }, ...rest] : [];
|
|
@@ -9055,12 +9233,13 @@ function selectorArgsJS(selector) {
|
|
|
9055
9233
|
}
|
|
9056
9234
|
return `${JSON.stringify(selector)}, ${JSON.stringify(parseSelectorFilters(selector))}`;
|
|
9057
9235
|
}
|
|
9058
|
-
var BOUND_TARGET_SELECTOR,
|
|
9236
|
+
var BOUND_TARGET_SELECTOR, log22, FILTER_MATCHING_JS, DEEP_QUERY_JS, FIND_ELEMENTS_JS, UNSEARCHED_CONTENT_JS, NAME_SCAN_LIMIT;
|
|
9059
9237
|
var init_targetNode = __esm({
|
|
9060
9238
|
"src/runtime/dom/targetNode.ts"() {
|
|
9061
9239
|
"use strict";
|
|
9062
9240
|
init_errors3();
|
|
9063
9241
|
init_messages();
|
|
9242
|
+
init_elementInfo();
|
|
9064
9243
|
init_shared();
|
|
9065
9244
|
init_frameScopedConnection();
|
|
9066
9245
|
init_bdgWorld();
|
|
@@ -9071,16 +9250,17 @@ var init_targetNode = __esm({
|
|
|
9071
9250
|
init_exitCodes();
|
|
9072
9251
|
init_selectorFilters();
|
|
9073
9252
|
BOUND_TARGET_SELECTOR = "__bdg_bound_target__";
|
|
9074
|
-
|
|
9253
|
+
log22 = createLogger("dom");
|
|
9075
9254
|
FILTER_MATCHING_JS = `(shadowRoots) => {
|
|
9255
|
+
const composed = ${COMPOSED_JS};
|
|
9256
|
+
const flatText = ${FLAT_TEXT_JS};
|
|
9076
9257
|
const skipped = /^(script|style|noscript|template)$/;
|
|
9077
9258
|
const hiddenText = (el) => {
|
|
9078
|
-
const
|
|
9079
|
-
acceptNode: (node) => (node.nodeType === 1 && skipped.test(node.localName) ? 2 : 1)
|
|
9080
|
-
});
|
|
9259
|
+
const nodes = el.localName === 'slot' ? el.assignedNodes({ flatten: true }) : (el.shadowRoot || el).childNodes;
|
|
9081
9260
|
let text = '';
|
|
9082
|
-
for (
|
|
9261
|
+
for (const node of nodes) {
|
|
9083
9262
|
if (node.nodeType === 3) text += node.data;
|
|
9263
|
+
else if (node.nodeType === 1 && !skipped.test(node.localName)) text += hiddenText(node);
|
|
9084
9264
|
}
|
|
9085
9265
|
return text;
|
|
9086
9266
|
};
|
|
@@ -9088,7 +9268,8 @@ var init_targetNode = __esm({
|
|
|
9088
9268
|
if (el.localName === 'input' && /^(submit|button|reset)$/i.test(el.type)) return el.value;
|
|
9089
9269
|
const rendered = typeof el.innerText === 'string' &&
|
|
9090
9270
|
(typeof el.checkVisibility !== 'function' || el.checkVisibility({ visibilityProperty: true }));
|
|
9091
|
-
|
|
9271
|
+
const text = !rendered ? hiddenText(el) : composed(el) ? flatText(el, Infinity, true) : el.innerText;
|
|
9272
|
+
return text.replace(/\\s+/g, ' ').trim();
|
|
9092
9273
|
};
|
|
9093
9274
|
const passes = (el, filter) => {
|
|
9094
9275
|
if (filter.kind === 'visible') {
|
|
@@ -9345,7 +9526,7 @@ async function noMatchContext(selector) {
|
|
|
9345
9526
|
...typeof value.shadowHost === "number" && compounds[value.shadowHost] && { shadowHost: compounds[value.shadowHost] }
|
|
9346
9527
|
};
|
|
9347
9528
|
} catch (error) {
|
|
9348
|
-
|
|
9529
|
+
log23.debug(`Could not read the page after no match: ${getErrorMessage(error)}`);
|
|
9349
9530
|
return {};
|
|
9350
9531
|
}
|
|
9351
9532
|
}
|
|
@@ -9358,7 +9539,7 @@ async function documentReadyState() {
|
|
|
9358
9539
|
const { result } = evaluated.data?.result ?? {};
|
|
9359
9540
|
return typeof result?.value === "string" ? result.value : void 0;
|
|
9360
9541
|
} catch (error) {
|
|
9361
|
-
|
|
9542
|
+
log23.debug(`Could not read document.readyState: ${getErrorMessage(error)}`);
|
|
9362
9543
|
return void 0;
|
|
9363
9544
|
}
|
|
9364
9545
|
}
|
|
@@ -9464,7 +9645,7 @@ async function queryDOMElements(selector, limit = 0) {
|
|
|
9464
9645
|
selector,
|
|
9465
9646
|
(arrayObjectId) => elementsWithDetails(arrayObjectId, described)
|
|
9466
9647
|
);
|
|
9467
|
-
if (total > 20)
|
|
9648
|
+
if (total > 20) log23.debug(`Queried ${total} elements with selector: ${selector}`);
|
|
9468
9649
|
return { selector, count: total, nodes: elements.map(queryNode) };
|
|
9469
9650
|
}
|
|
9470
9651
|
function queryNode(element, index) {
|
|
@@ -9521,7 +9702,7 @@ async function getDomContext(ref, options = {}) {
|
|
|
9521
9702
|
await callCDP("DOM.enable", {});
|
|
9522
9703
|
const desc = await describeNode(ref);
|
|
9523
9704
|
if (!desc) {
|
|
9524
|
-
|
|
9705
|
+
log23.debug(`No DOM context for ${JSON.stringify(ref)}`);
|
|
9525
9706
|
return null;
|
|
9526
9707
|
}
|
|
9527
9708
|
const full = options.full === true;
|
|
@@ -9681,11 +9862,11 @@ async function pageDocumentId() {
|
|
|
9681
9862
|
return typeof result?.value === "string" ? result.value : void 0;
|
|
9682
9863
|
} catch (error) {
|
|
9683
9864
|
if (error instanceof CommandError) throw error;
|
|
9684
|
-
|
|
9865
|
+
log23.debug(`Page document not identified: ${getErrorMessage(error)}`);
|
|
9685
9866
|
return void 0;
|
|
9686
9867
|
}
|
|
9687
9868
|
}
|
|
9688
|
-
var
|
|
9869
|
+
var log23, CDP_CONCURRENCY_LIMIT, queryCount, MAX_HOST_COMPOUNDS, VIEWPORT_HINT_LIMIT, QUERY_CACHE_LIMIT, ELEMENT_DETAILS_FUNCTION, SLICE_FUNCTION, CHILDREN_LISTED, UNSHOWN_CHILD;
|
|
9689
9870
|
var init_query = __esm({
|
|
9690
9871
|
"src/commands/dom/helpers/query.ts"() {
|
|
9691
9872
|
"use strict";
|
|
@@ -9703,7 +9884,7 @@ var init_query = __esm({
|
|
|
9703
9884
|
init_exitCodes();
|
|
9704
9885
|
init_selectorFilters();
|
|
9705
9886
|
init_suggestions();
|
|
9706
|
-
|
|
9887
|
+
log23 = createLogger("dom");
|
|
9707
9888
|
CDP_CONCURRENCY_LIMIT = 10;
|
|
9708
9889
|
queryCount = 0;
|
|
9709
9890
|
MAX_HOST_COMPOUNDS = 4;
|
|
@@ -9733,7 +9914,7 @@ var init_query = __esm({
|
|
|
9733
9914
|
|
|
9734
9915
|
// src/commands/shared/outputFile.ts
|
|
9735
9916
|
import * as fs15 from "fs";
|
|
9736
|
-
import * as
|
|
9917
|
+
import * as path6 from "path";
|
|
9737
9918
|
function outputPathError(filePath, error, extension2) {
|
|
9738
9919
|
const code = error.code ?? "";
|
|
9739
9920
|
const problem = PATH_PROBLEMS[code];
|
|
@@ -9762,13 +9943,16 @@ function assertFilePath(filePath, extension2) {
|
|
|
9762
9943
|
);
|
|
9763
9944
|
}
|
|
9764
9945
|
}
|
|
9765
|
-
async function writeOutputFile(filePath, data, extension2) {
|
|
9946
|
+
async function writeOutputFile(filePath, data, extension2, mode) {
|
|
9766
9947
|
assertFilePath(filePath, extension2);
|
|
9767
|
-
const absolutePath =
|
|
9948
|
+
const absolutePath = path6.resolve(filePath);
|
|
9768
9949
|
try {
|
|
9769
|
-
makeDirectory(
|
|
9770
|
-
if (typeof data === "string")
|
|
9771
|
-
|
|
9950
|
+
makeDirectory(path6.dirname(absolutePath));
|
|
9951
|
+
if (typeof data === "string") {
|
|
9952
|
+
await AtomicFileWriter.writeAsync(absolutePath, data, mode === void 0 ? {} : { mode });
|
|
9953
|
+
} else {
|
|
9954
|
+
await AtomicFileWriter.writeBufferAsync(absolutePath, data);
|
|
9955
|
+
}
|
|
9772
9956
|
} catch (error) {
|
|
9773
9957
|
throw outputPathError(filePath, error, extension2);
|
|
9774
9958
|
}
|
|
@@ -9802,10 +9986,13 @@ var init_outputFile = __esm({
|
|
|
9802
9986
|
});
|
|
9803
9987
|
|
|
9804
9988
|
// src/runtime/page/userAgent.ts
|
|
9989
|
+
var log24;
|
|
9805
9990
|
var init_userAgent = __esm({
|
|
9806
9991
|
"src/runtime/page/userAgent.ts"() {
|
|
9807
9992
|
"use strict";
|
|
9993
|
+
init_logging();
|
|
9808
9994
|
init_errors();
|
|
9995
|
+
log24 = createLogger("session");
|
|
9809
9996
|
}
|
|
9810
9997
|
});
|
|
9811
9998
|
|
|
@@ -9819,7 +10006,7 @@ function viewportOverride(viewport, deviceScaleFactor = 0) {
|
|
|
9819
10006
|
mobile
|
|
9820
10007
|
};
|
|
9821
10008
|
}
|
|
9822
|
-
var
|
|
10009
|
+
var log25, MOBILE_PIXEL_RATIO;
|
|
9823
10010
|
var init_emulation = __esm({
|
|
9824
10011
|
"src/runtime/page/emulation.ts"() {
|
|
9825
10012
|
"use strict";
|
|
@@ -9827,7 +10014,7 @@ var init_emulation = __esm({
|
|
|
9827
10014
|
init_userAgent();
|
|
9828
10015
|
init_logging();
|
|
9829
10016
|
init_errors();
|
|
9830
|
-
|
|
10017
|
+
log25 = createLogger("session");
|
|
9831
10018
|
MOBILE_PIXEL_RATIO = 3;
|
|
9832
10019
|
}
|
|
9833
10020
|
});
|
|
@@ -9892,12 +10079,12 @@ async function waitForPostScrollStability() {
|
|
|
9892
10079
|
const networkIdle = value?.networkIdle ?? 0;
|
|
9893
10080
|
const domIdle = value?.domIdle ?? 0;
|
|
9894
10081
|
if (networkIdle >= POST_SCROLL_NETWORK_IDLE_MS && domIdle >= POST_SCROLL_DOM_STABLE_MS) {
|
|
9895
|
-
|
|
10082
|
+
log26.debug(`Post-scroll stable: network ${networkIdle}ms, DOM ${domIdle}ms`);
|
|
9896
10083
|
return;
|
|
9897
10084
|
}
|
|
9898
10085
|
await new Promise((resolve4) => setTimeout(resolve4, STABILITY_CHECK_INTERVAL_MS));
|
|
9899
10086
|
}
|
|
9900
|
-
|
|
10087
|
+
log26.debug("Post-scroll stability timeout, proceeding anyway");
|
|
9901
10088
|
} finally {
|
|
9902
10089
|
await callBdgScript("Runtime.evaluate", {
|
|
9903
10090
|
expression: `
|
|
@@ -10196,7 +10383,7 @@ async function paintedArea(ref, bounds) {
|
|
|
10196
10383
|
height: bounds.height + top + bottom
|
|
10197
10384
|
};
|
|
10198
10385
|
} catch (error) {
|
|
10199
|
-
|
|
10386
|
+
log26.debug(`Could not measure overflowing content: ${String(error)}`);
|
|
10200
10387
|
return bounds;
|
|
10201
10388
|
} finally {
|
|
10202
10389
|
await callCDP("Runtime.releaseObjectGroup", { objectGroup }).catch(() => void 0);
|
|
@@ -10293,7 +10480,7 @@ function roundBounds(bounds) {
|
|
|
10293
10480
|
height: Math.round(bounds.height)
|
|
10294
10481
|
};
|
|
10295
10482
|
}
|
|
10296
|
-
var
|
|
10483
|
+
var log26, POST_SCROLL_NETWORK_IDLE_MS, POST_SCROLL_DOM_STABLE_MS, POST_SCROLL_MAX_WAIT_MS, STABILITY_CHECK_INTERVAL_MS, OVERFLOW_SCAN_LIMIT, CONTENT_OVERFLOW_JS, OVERFLOW_SLACK;
|
|
10297
10484
|
var init_screenshot = __esm({
|
|
10298
10485
|
"src/commands/dom/helpers/screenshot.ts"() {
|
|
10299
10486
|
"use strict";
|
|
@@ -10308,7 +10495,7 @@ var init_screenshot = __esm({
|
|
|
10308
10495
|
init_metadata();
|
|
10309
10496
|
init_logging();
|
|
10310
10497
|
init_exitCodes();
|
|
10311
|
-
|
|
10498
|
+
log26 = createLogger("dom");
|
|
10312
10499
|
POST_SCROLL_NETWORK_IDLE_MS = 150;
|
|
10313
10500
|
POST_SCROLL_DOM_STABLE_MS = 200;
|
|
10314
10501
|
POST_SCROLL_MAX_WAIT_MS = 2e3;
|
|
@@ -13509,9 +13696,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
13509
13696
|
* @param {string} [path]
|
|
13510
13697
|
* @return {(string|null|Command)}
|
|
13511
13698
|
*/
|
|
13512
|
-
executableDir(
|
|
13513
|
-
if (
|
|
13514
|
-
this._executableDir =
|
|
13699
|
+
executableDir(path8) {
|
|
13700
|
+
if (path8 === void 0) return this._executableDir;
|
|
13701
|
+
this._executableDir = path8;
|
|
13515
13702
|
return this;
|
|
13516
13703
|
}
|
|
13517
13704
|
/**
|
|
@@ -13818,8 +14005,9 @@ var OPTION_BEHAVIORS = {
|
|
|
13818
14005
|
},
|
|
13819
14006
|
"get:--full": {
|
|
13820
14007
|
default: 'Semantic output shows the element text up to 500 characters (whitespace collapsed; close buttons such as "\xD7" and aria-hidden icons left out)',
|
|
13821
|
-
whenEnabled: "Shows all of the element text;
|
|
13822
|
-
|
|
14008
|
+
whenEnabled: "Shows all of the element text; with --raw (or --node-id) prints the whole outer HTML instead of its first 20000 characters, and JSON outerHTML is whole too (else cut to 20000 with truncatedFrom, the original length)",
|
|
14009
|
+
automaticBehavior: `Without --full, --raw output cuts each element's HTML at 20000 characters and ends it with "\u2026 N more chars (use --full)"`,
|
|
14010
|
+
tokenImpact: "A page-sized container can add thousands of tokens; dom get body --raw --full on Wikipedia is about 3.4 MB. Target the element you need"
|
|
13823
14011
|
},
|
|
13824
14012
|
"get:--all": {
|
|
13825
14013
|
default: "Returns first matching element only",
|
|
@@ -13831,30 +14019,42 @@ var OPTION_BEHAVIORS = {
|
|
|
13831
14019
|
automaticBehavior: "Past the last match exits 81; a numeric index argument (a cached query index) past the indexed matches, or from an earlier page, exits 87 (re-run dom query)"
|
|
13832
14020
|
},
|
|
13833
14021
|
"query:--limit": {
|
|
13834
|
-
default: "dom query and dom a11y query list the first 50 matches and say how many more there are; --json
|
|
14022
|
+
default: "dom query and dom a11y query list the first 50 matches and say how many more there are; --json lists the first 100 with count (all matches) and omitted (the rest)",
|
|
13835
14023
|
whenEnabled: "Lists that many matches (0 = all), in human and JSON output; count is always the total, JSON omitted the rest",
|
|
13836
14024
|
automaticBehavior: "Matches are cached for index-based access (bdg dom click 55 works even when 50 are listed): all of them for dom a11y query, the first 1000 (or --limit, if higher) for dom query, which describes only those, so a page with 50000 matches answers in under a second; an element the page and frame trees both report is listed once. Indices work with click, fill, hover, pressKey, scroll, submit, layout, get and listeners, also for elements of a cross-origin iframe of the same site (a consent dialog), whose scripts then run in that frame",
|
|
13837
|
-
tokenImpact:
|
|
14025
|
+
tokenImpact: 'About one line per match (piped JSON about 160 bytes per dom query match, 230 per a11y match); a page can have thousands of links: on Wikipedia "United States" --limit 0 --json is 1.0 MB (dom query a) and 1.3 MB (dom a11y query role:link), the default 16 KB and 23 KB'
|
|
14026
|
+
},
|
|
14027
|
+
"tree:--limit": {
|
|
14028
|
+
default: "dom a11y tree lists the first 50 meaningful nodes depth-first, in human and JSON output; JSON count is the whole tree = nodes listed + omitted (cut by --limit/--depth) + skipped (never listed)",
|
|
14029
|
+
whenEnabled: "Lists that many nodes; 0 = all listed nodes (text boxes and empty wrappers are always skipped)",
|
|
14030
|
+
automaticBehavior: "Ignored nodes, text boxes, blank text, text repeating its parent name and nameless layout wrappers (generic, none, presentation, layout tables) are never listed (JSON skipped counts them); their children move up a level. For the raw tree with every node use bdg cdp Accessibility.getFullAXTree --json. JSON nodes carry depth (0 = root) instead of childIds; nodes outside the root (frame content) follow the root tree",
|
|
14031
|
+
tokenImpact: 'About 140 bytes per piped JSON node (7 KB by default); the whole tree of a long page is megabytes (Wikipedia "United States": 51k nodes, 20k listed, 2.6 MB with --limit 0 --json), so prefer --depth or dom a11y query "role:<role>"'
|
|
14032
|
+
},
|
|
14033
|
+
"tree:--depth": {
|
|
14034
|
+
default: "dom a11y tree lists every level (up to --limit nodes)",
|
|
14035
|
+
whenEnabled: "Lists nodes down to that level (0 = root only); deeper nodes are counted in omitted",
|
|
14036
|
+
tokenImpact: "An outline of a page (landmarks, headings) in a few levels"
|
|
13838
14037
|
},
|
|
13839
14038
|
"eval:--frame": {
|
|
13840
14039
|
default: "Evaluates in the page's main frame",
|
|
13841
14040
|
whenEnabled: "Evaluates in one iframe's main world (its own globals), including cross-origin (out-of-process) iframes; output gains a frame field (its URL)",
|
|
13842
14041
|
automaticBehavior: "The value is matched as: a 0-based index (bdg dom frames order: document order of the <iframe> elements, nested ones depth-first, main page not counted), else an exact name/id attribute, else a case-insensitive part of the name, id or URL. Several matches fail with 81 listing them; none fails with 83 listing all frames. Frames are looked up on every call (a reloaded iframe is found again). An index that names another frame than in the last bdg dom frames listing (iframes added, removed or moved, or the page navigated) fails with 87 STALE_CACHE: re-run bdg dom frames or pick the frame by name."
|
|
13843
14042
|
},
|
|
13844
|
-
"
|
|
13845
|
-
default: "
|
|
13846
|
-
whenEnabled: "
|
|
13847
|
-
|
|
14043
|
+
"eval:--full": {
|
|
14044
|
+
default: 'Human output prints the first 20000 characters of the value followed by "\u2026 N more chars (use --full)". In JSON a string result is cut to 20000 characters with truncatedFrom (the original length); an array result lists its first 100 elements with count (all of them) and omitted; an object or array whose JSON is still over 20000 characters becomes the first 20000 characters of its JSON text (a string; arrays keep count) with truncatedFrom (the length of the JSON text of the copy, which holds at most 1000 entries per list or object): a string result with truncatedFrom while type is object is such a JSON-text start',
|
|
14045
|
+
whenEnabled: "Prints the whole value, byte for byte; objects and arrays are copied with every entry (without --full at most 1000 per list or object)",
|
|
14046
|
+
tokenImpact: 'dom eval document.documentElement.outerHTML on Wikipedia is about 3.6 MB with --full; on a page with 20000 elements, [...document.querySelectorAll("*")].map(e => e.outerHTML) --json is 3 MB with --full and 23 KB without; select what you need in the expression instead'
|
|
14047
|
+
},
|
|
14048
|
+
"console:--full": {
|
|
14049
|
+
default: 'Message texts are cut: human output (summary, --list, --follow) at 200 characters followed by "\u2026 N more chars (use --full)"; JSON text at 10000 characters with truncatedFrom (the original length)',
|
|
14050
|
+
whenEnabled: "Message texts are printed whole, in human and JSON output",
|
|
14051
|
+
tokenImpact: "A page that logs a large payload or throws a long error can add megabytes; bdg details console <n> shows one message whole"
|
|
13848
14052
|
},
|
|
13849
14053
|
"console:--history": {
|
|
13850
14054
|
default: "Shows messages from current page load only (most recent navigation)",
|
|
13851
14055
|
whenEnabled: "Shows messages from ALL page loads during the session",
|
|
13852
14056
|
automaticBehavior: 'Page navigations create new "navigation contexts" - default filters to latest context'
|
|
13853
14057
|
},
|
|
13854
|
-
"console:-l": {
|
|
13855
|
-
default: "Smart summary with errors deduplicated and warnings grouped: the newest 50 distinct errors and warnings, with a note for the earlier ones. The session keeps the newest 10000 messages; dropped ones are counted (dropped in JSON)",
|
|
13856
|
-
whenEnabled: "Lists all messages chronologically without deduplication"
|
|
13857
|
-
},
|
|
13858
14058
|
"console:--list": {
|
|
13859
14059
|
default: "Smart summary with errors deduplicated and warnings grouped: the newest 50 distinct errors and warnings, with a note for the earlier ones. The session keeps the newest 10000 messages; dropped ones are counted (dropped in JSON)",
|
|
13860
14060
|
whenEnabled: "Lists all messages chronologically without deduplication"
|
|
@@ -14006,6 +14206,7 @@ var OPTION_BEHAVIORS = {
|
|
|
14006
14206
|
},
|
|
14007
14207
|
"inspect:--no-hints": {
|
|
14008
14208
|
default: "Hints at the element's own author declarations that have no effect (flex/grid properties without flex or grid, item properties without a flex or grid parent, offsets on static elements, sizes on inline ones, var() of an unset custom property, form controls in the browser's font), within a 1 s budget; hints none when nothing was found",
|
|
14209
|
+
automaticBehavior: "After a hint read times out, later inspects on the same page skip the hints without waiting (hints skipped: this page's stylesheets are slow to read) until a read is fast again, a stylesheet changes or it navigates; --rules and --why still wait up to 5 s. Matched rules that took over 300 ms are reused for up to 5 s, until a command that may change the page or a stylesheet or DOM change",
|
|
14009
14210
|
whenEnabled: "Skips the hints and does not read the matched rules"
|
|
14010
14211
|
},
|
|
14011
14212
|
"scroll:--down": {
|
|
@@ -14046,56 +14247,57 @@ var OPTION_BEHAVIORS = {
|
|
|
14046
14247
|
whenEnabled: "Quick scan: field names, types, and required status only",
|
|
14047
14248
|
tokenImpact: "Reduces output ~50% for initial discovery"
|
|
14048
14249
|
},
|
|
14250
|
+
"peek:--full": {
|
|
14251
|
+
default: 'Console message texts are cut: human output at 200 characters (compact output also at 2 lines) followed by "\u2026 N more chars (use --full)"; JSON text at 10000 characters with truncatedFrom (the original length)',
|
|
14252
|
+
whenEnabled: "Console message texts are printed whole, in human and JSON output",
|
|
14253
|
+
tokenImpact: "A page that logs a large payload can add megabytes per peek"
|
|
14254
|
+
},
|
|
14049
14255
|
"peek:--type": {
|
|
14050
14256
|
whenEnabled: "Filters network requests by CDP resource type. Case-insensitive, comma-separated. Valid: Document, Stylesheet, Image, Media, Font, Script, XHR, Fetch, WebSocket, etc."
|
|
14051
14257
|
},
|
|
14052
|
-
"peek:-f": {
|
|
14053
|
-
default: "Shows snapshot of current data",
|
|
14054
|
-
whenEnabled: "Continuous monitoring (like tail -f): refreshes every second, or every --interval ms (100-60000). Replaces the deprecated bdg tail",
|
|
14055
|
-
automaticBehavior: FOLLOW_BEHAVIOR
|
|
14056
|
-
},
|
|
14057
14258
|
"peek:--follow": {
|
|
14058
14259
|
default: "Shows snapshot of current data",
|
|
14059
14260
|
whenEnabled: "Continuous monitoring (like tail -f): refreshes every second, or every --interval ms (100-60000). Replaces the deprecated bdg tail",
|
|
14060
14261
|
automaticBehavior: FOLLOW_BEHAVIOR
|
|
14061
14262
|
},
|
|
14062
|
-
"console
|
|
14263
|
+
"console:--follow": {
|
|
14063
14264
|
default: "Prints the messages logged so far and exits",
|
|
14064
14265
|
whenEnabled: "Streams new messages as they come (the last --last at start)",
|
|
14065
14266
|
automaticBehavior: FOLLOW_BEHAVIOR
|
|
14066
14267
|
},
|
|
14067
|
-
"list
|
|
14268
|
+
"list:--follow": {
|
|
14068
14269
|
default: "Lists the requests captured so far and exits",
|
|
14069
14270
|
whenEnabled: "Streams requests as they finish",
|
|
14070
14271
|
automaticBehavior: FOLLOW_BEHAVIOR
|
|
14071
14272
|
},
|
|
14072
|
-
"
|
|
14273
|
+
"har:--include-sensitive": {
|
|
14274
|
+
default: 'The HAR is sanitized: values become "[redacted]" for Authorization, Proxy-Authorization, Authentication, Cookie and Set-Cookie headers, X-*key/token/secret/auth headers and headers with an api-key/apikey/token/secret/jwt/subscription-key/session(-id) segment (www-authenticate is kept); every cookie value; query and fragment parameters named like credentials (plus code, sig, key) in the request URL, queryString, redirectURL and Location/Referer headers ("%5Bredacted%5D" in URLs); and password/token/secret/key/session/signature fields of JSON (primitives at any depth under such a name), form-urlencoded (also sniffed when the Content-Type says otherwise) and multipart request bodies. A JSON body too deep to walk becomes "[redacted]" whole. Header, cookie and parameter names, cookie attributes, headersSize and bodySize stay; log.comment and JSON sanitized: true say so',
|
|
14275
|
+
whenEnabled: "Writes every captured value (JSON sanitized: false); human output warns that the file holds credentials",
|
|
14276
|
+
automaticBehavior: "Matching is by name, so it over-redacts: harmless values under credential-looking names (tokenCount: 5, sessionLength) are replaced too, and credentials under other names are kept. Unlike Chrome DevTools, which drops these headers and empties cookies, names are kept so the HAR still shows a request was authenticated. Response bodies and WebSocket messages are not redacted. HAR files are written readable by their owner only (0600). network headers and network getCookies always show real values"
|
|
14277
|
+
},
|
|
14278
|
+
"peek:--verbose": {
|
|
14073
14279
|
default: "Compact output (truncated URLs, no resource types)",
|
|
14074
14280
|
whenEnabled: "Verbose output with full URLs and resource types"
|
|
14075
14281
|
},
|
|
14076
|
-
"
|
|
14282
|
+
"bdg:--headless": {
|
|
14077
14283
|
default: "A window when there is a display: on macOS unless over SSH (SSH_CONNECTION, SSH_TTY) or CI is set; on Linux when DISPLAY or WAYLAND_DISPLAY is set. Servers, containers and CI run headless",
|
|
14078
14284
|
whenEnabled: "Chrome runs without a window (pass it when running unattended on a Mac)"
|
|
14079
14285
|
},
|
|
14080
|
-
"
|
|
14286
|
+
"bdg:--no-headless": {
|
|
14081
14287
|
default: "A window when there is a display: on macOS unless over SSH (SSH_CONNECTION, SSH_TTY) or CI is set; on Linux when DISPLAY or WAYLAND_DISPLAY is set",
|
|
14082
14288
|
whenEnabled: "Chrome shows its window even without a detected display (it fails without one)"
|
|
14083
14289
|
},
|
|
14084
|
-
"
|
|
14290
|
+
"bdg:--all": {
|
|
14085
14291
|
default: "Tracking/analytics requests and console noise are filtered; bodies of binary responses (images, fonts) are not captured",
|
|
14086
14292
|
whenEnabled: "Everything is captured, including binary response bodies (base64, flagged by responseBodyBase64, within --max-body-size)",
|
|
14087
14293
|
tokenImpact: "details network --json and HAR exports can grow considerably on media-heavy pages"
|
|
14088
14294
|
},
|
|
14089
|
-
"peek:--verbose": {
|
|
14090
|
-
default: "Compact output (truncated URLs, no resource types)",
|
|
14091
|
-
whenEnabled: "Verbose output with full URLs and resource types"
|
|
14092
|
-
},
|
|
14093
14295
|
"bdg:--session": {
|
|
14094
14296
|
default: "The default session in ~/.bdg (or $BDG_SESSION_DIR); BDG_SESSION=<name> selects a named session like the flag",
|
|
14095
14297
|
whenEnabled: "Uses the named session in ~/.bdg/sessions/<name>/ (or $BDG_SESSION_DIR/sessions/<name>/) with its own daemon, Chrome, profile and port; every command (status, stop, cleanup, ...) acts on that session only",
|
|
14096
14298
|
automaticBehavior: 'Accepted before or after any subcommand; --session wins over BDG_SESSION. Names are case-insensitive (lower-cased: ALPHA is alpha). Without --port a named session takes the first free port above 9222 not claimed by another running session, and keeps it in port.txt. Names: 1-40 letters, digits, "-" or "_", starting with a letter or digit (exit 81 otherwise, also when the socket path would be too long). Hints and suggestions in its output carry --session <name>'
|
|
14097
14299
|
},
|
|
14098
|
-
"cleanup
|
|
14300
|
+
"cleanup:--force": {
|
|
14099
14301
|
default: "Refuses to run while a session is active; removes files left by a crashed session",
|
|
14100
14302
|
whenEnabled: "Kills the running daemon and its Chrome first (use when a session is stuck)"
|
|
14101
14303
|
},
|
|
@@ -14130,23 +14332,16 @@ var OPTION_BEHAVIORS = {
|
|
|
14130
14332
|
default: "Chrome launched by bdg is always closed on stop; an attached Chrome (--chrome-ws-url) is left running",
|
|
14131
14333
|
whenEnabled: "No additional effect; kept for compatibility"
|
|
14132
14334
|
},
|
|
14133
|
-
"status:-v": {
|
|
14134
|
-
default: "Basic session status (daemon running, session active, URL)",
|
|
14135
|
-
whenEnabled: "Includes Chrome diagnostics and CDP connection details"
|
|
14136
|
-
},
|
|
14137
14335
|
"status:--verbose": {
|
|
14138
14336
|
default: "Basic session status (daemon running, session active, URL)",
|
|
14139
14337
|
whenEnabled: "Includes Chrome diagnostics and CDP connection details"
|
|
14140
14338
|
}
|
|
14141
14339
|
};
|
|
14142
|
-
function
|
|
14143
|
-
|
|
14144
|
-
const flagName = firstFlag.trim().split(" ")[0] ?? firstFlag.trim();
|
|
14145
|
-
return `${commandName}:${flagName}`;
|
|
14340
|
+
function behaviorKey(commandName, option) {
|
|
14341
|
+
return `${commandName}:${option.long ?? option.short ?? option.flags}`;
|
|
14146
14342
|
}
|
|
14147
|
-
function getOptionBehavior(commandName,
|
|
14148
|
-
|
|
14149
|
-
return OPTION_BEHAVIORS[key];
|
|
14343
|
+
function getOptionBehavior(commandName, option) {
|
|
14344
|
+
return OPTION_BEHAVIORS[behaviorKey(commandName, option)];
|
|
14150
14345
|
}
|
|
14151
14346
|
|
|
14152
14347
|
// node_modules/chrome-launcher/dist/chrome-finder.js
|
|
@@ -14815,9 +15010,9 @@ async function waitUntil(condition, timeoutMs, pollMs = 25) {
|
|
|
14815
15010
|
// src/utils/file.ts
|
|
14816
15011
|
init_logging();
|
|
14817
15012
|
import * as fs9 from "fs";
|
|
14818
|
-
function safeRemoveFile(
|
|
15013
|
+
function safeRemoveFile(path8, context, log36) {
|
|
14819
15014
|
try {
|
|
14820
|
-
fs9.rmSync(
|
|
15015
|
+
fs9.rmSync(path8, { force: true });
|
|
14821
15016
|
return true;
|
|
14822
15017
|
} catch (error) {
|
|
14823
15018
|
logDebugError(log36, `remove ${context}`, error);
|
|
@@ -15258,7 +15453,7 @@ function convertOption(option, commandName) {
|
|
|
15258
15453
|
if (option.argChoices) {
|
|
15259
15454
|
metadata.choices = option.argChoices;
|
|
15260
15455
|
}
|
|
15261
|
-
const behavior = getOptionBehavior(commandName, option
|
|
15456
|
+
const behavior = getOptionBehavior(commandName, option);
|
|
15262
15457
|
if (behavior) {
|
|
15263
15458
|
metadata.behavior = behavior;
|
|
15264
15459
|
}
|
|
@@ -15311,7 +15506,7 @@ function convertCompactCommand(command) {
|
|
|
15311
15506
|
return {
|
|
15312
15507
|
name: command.name(),
|
|
15313
15508
|
...aliases.length > 0 && { aliases },
|
|
15314
|
-
description: command.description().split("\n")[0] ?? "",
|
|
15509
|
+
description: command.summary() || (command.description().split("\n")[0] ?? ""),
|
|
15315
15510
|
...args && { arguments: args },
|
|
15316
15511
|
...options.length > 0 && {
|
|
15317
15512
|
options: Object.fromEntries(options.map((option) => [option.flags, option.description]))
|
|
@@ -15408,14 +15603,14 @@ init_errors3();
|
|
|
15408
15603
|
init_messages();
|
|
15409
15604
|
init_exitCodes();
|
|
15410
15605
|
init_suggestions();
|
|
15411
|
-
var COMMANDER_HINT = /\n?\(Did you mean (.+)\?\)\s*$/;
|
|
15606
|
+
var COMMANDER_HINT = /\n?\(Did you mean (?:one of )?(.+)\?\)\s*$/;
|
|
15412
15607
|
function helpTopicPath(args) {
|
|
15413
15608
|
const firstOption = args.findIndex((arg) => arg.startsWith("-"));
|
|
15414
15609
|
return firstOption === -1 ? args : args.slice(0, firstOption);
|
|
15415
15610
|
}
|
|
15416
15611
|
function assertKnownHelpTopic(program2, topic) {
|
|
15417
15612
|
let command = program2;
|
|
15418
|
-
const
|
|
15613
|
+
const path8 = [];
|
|
15419
15614
|
for (const word of topic) {
|
|
15420
15615
|
if (command.commands.length === 0 || word === "help") return;
|
|
15421
15616
|
const sub = command.commands.find((c) => c.name() === word || c.aliases().includes(word));
|
|
@@ -15425,8 +15620,8 @@ function assertKnownHelpTopic(program2, topic) {
|
|
|
15425
15620
|
command.commands.map((c) => c.name())
|
|
15426
15621
|
);
|
|
15427
15622
|
const err = unknownHelpTopicError(
|
|
15428
|
-
[...
|
|
15429
|
-
closest && [...
|
|
15623
|
+
[...path8, word].join(" "),
|
|
15624
|
+
closest && [...path8, closest].join(" ")
|
|
15430
15625
|
);
|
|
15431
15626
|
throw new CommandError(
|
|
15432
15627
|
err.message,
|
|
@@ -15434,7 +15629,7 @@ function assertKnownHelpTopic(program2, topic) {
|
|
|
15434
15629
|
EXIT_CODES.INVALID_ARGUMENTS
|
|
15435
15630
|
);
|
|
15436
15631
|
}
|
|
15437
|
-
|
|
15632
|
+
path8.push(sub.name());
|
|
15438
15633
|
command = sub;
|
|
15439
15634
|
}
|
|
15440
15635
|
}
|
|
@@ -15442,7 +15637,10 @@ function splitCommanderHint(text) {
|
|
|
15442
15637
|
const message = text.replace(/^error:\s*/i, "");
|
|
15443
15638
|
const hint = COMMANDER_HINT.exec(message);
|
|
15444
15639
|
if (!hint) return { message };
|
|
15445
|
-
return {
|
|
15640
|
+
return {
|
|
15641
|
+
message: message.slice(0, hint.index).trim(),
|
|
15642
|
+
suggestion: didYouMeanSuggestion((hint[1] ?? "").split(", "))
|
|
15643
|
+
};
|
|
15446
15644
|
}
|
|
15447
15645
|
var UNKNOWN_OPTION = /unknown option '([^'=]+)/;
|
|
15448
15646
|
function closestOption(flag, command) {
|
|
@@ -15473,7 +15671,7 @@ function usageErrorDetails(error, command, argv = process.argv) {
|
|
|
15473
15671
|
|
|
15474
15672
|
// src/commands/start.ts
|
|
15475
15673
|
import fs11 from "node:fs";
|
|
15476
|
-
import
|
|
15674
|
+
import os4 from "node:os";
|
|
15477
15675
|
|
|
15478
15676
|
// src/commands/shared/commonOptions.ts
|
|
15479
15677
|
function jsonOption() {
|
|
@@ -15498,14 +15696,14 @@ init_messages();
|
|
|
15498
15696
|
|
|
15499
15697
|
// src/utils/version.ts
|
|
15500
15698
|
import { readFileSync as readFileSync6 } from "fs";
|
|
15501
|
-
import { join as
|
|
15699
|
+
import { join as join6 } from "path";
|
|
15502
15700
|
var cachedVersion = "";
|
|
15503
15701
|
function getVersion() {
|
|
15504
15702
|
if (cachedVersion) {
|
|
15505
15703
|
return cachedVersion;
|
|
15506
15704
|
}
|
|
15507
15705
|
try {
|
|
15508
|
-
const pkgPath =
|
|
15706
|
+
const pkgPath = join6(PACKAGE_ROOT, "package.json");
|
|
15509
15707
|
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
15510
15708
|
cachedVersion = pkg.version ?? "0.0.0";
|
|
15511
15709
|
} catch {
|
|
@@ -15519,6 +15717,9 @@ var VERSION = getVersion();
|
|
|
15519
15717
|
function buildSuccessResponse(data) {
|
|
15520
15718
|
return { version: VERSION, success: true, data };
|
|
15521
15719
|
}
|
|
15720
|
+
function stringifyEnvelope(envelope) {
|
|
15721
|
+
return process.stdout.isTTY ? JSON.stringify(envelope, null, 2) : JSON.stringify(envelope);
|
|
15722
|
+
}
|
|
15522
15723
|
var OutputBuilder = class {
|
|
15523
15724
|
/**
|
|
15524
15725
|
* Build an error response envelope.
|
|
@@ -15557,9 +15758,7 @@ function handleValidationError(error, json) {
|
|
|
15557
15758
|
if (error.metadata.suggestion) {
|
|
15558
15759
|
errorOptions.suggestion = error.metadata.suggestion;
|
|
15559
15760
|
}
|
|
15560
|
-
console.log(
|
|
15561
|
-
JSON.stringify(OutputBuilder.buildJsonError(error.message, errorOptions), null, 2)
|
|
15562
|
-
);
|
|
15761
|
+
console.log(stringifyEnvelope(OutputBuilder.buildJsonError(error.message, errorOptions)));
|
|
15563
15762
|
} else {
|
|
15564
15763
|
console.error(genericError(error.message));
|
|
15565
15764
|
if (error.metadata.suggestion) console.error(escapeControlChars(error.metadata.suggestion));
|
|
@@ -15571,7 +15770,7 @@ function handleValidationError(error, json) {
|
|
|
15571
15770
|
const envelope = OutputBuilder.buildJsonError(message, {
|
|
15572
15771
|
exitCode: EXIT_CODES.INVALID_ARGUMENTS
|
|
15573
15772
|
});
|
|
15574
|
-
console.log(
|
|
15773
|
+
console.log(stringifyEnvelope(envelope));
|
|
15575
15774
|
} else {
|
|
15576
15775
|
console.error(genericError(message));
|
|
15577
15776
|
}
|
|
@@ -15597,20 +15796,18 @@ function noActiveSessionError() {
|
|
|
15597
15796
|
async function runJsonCommand(fn) {
|
|
15598
15797
|
try {
|
|
15599
15798
|
const data = await fn();
|
|
15600
|
-
console.log(
|
|
15799
|
+
console.log(stringifyEnvelope(buildSuccessResponse(data)));
|
|
15601
15800
|
process.exit(EXIT_CODES.SUCCESS);
|
|
15602
15801
|
} catch (caught) {
|
|
15603
15802
|
const error = isDaemonConnectionError(caught) ? noActiveSessionError() : caught instanceof IPCTimeoutError ? timeoutError(caught) : caught;
|
|
15604
15803
|
const exitCode = getErrorExitCode(error, EXIT_CODES.UNHANDLED_EXCEPTION);
|
|
15605
15804
|
const suggestion = error instanceof CommandError && typeof error.metadata["suggestion"] === "string" ? error.metadata["suggestion"] : void 0;
|
|
15606
15805
|
console.log(
|
|
15607
|
-
|
|
15806
|
+
stringifyEnvelope(
|
|
15608
15807
|
OutputBuilder.buildJsonError(getErrorMessage(error), {
|
|
15609
15808
|
exitCode,
|
|
15610
15809
|
...suggestion && { suggestion }
|
|
15611
|
-
})
|
|
15612
|
-
null,
|
|
15613
|
-
2
|
|
15810
|
+
})
|
|
15614
15811
|
)
|
|
15615
15812
|
);
|
|
15616
15813
|
process.exit(exitCode);
|
|
@@ -15636,13 +15833,11 @@ async function runCommand(handler, options, formatter) {
|
|
|
15636
15833
|
const exitCode = result.exitCode ?? EXIT_CODES.UNHANDLED_EXCEPTION;
|
|
15637
15834
|
if (options.json) {
|
|
15638
15835
|
console.log(
|
|
15639
|
-
|
|
15836
|
+
stringifyEnvelope(
|
|
15640
15837
|
OutputBuilder.buildJsonError(result.error ?? "Unknown error", {
|
|
15641
15838
|
...result.errorContext,
|
|
15642
15839
|
exitCode
|
|
15643
|
-
})
|
|
15644
|
-
null,
|
|
15645
|
-
2
|
|
15840
|
+
})
|
|
15646
15841
|
)
|
|
15647
15842
|
);
|
|
15648
15843
|
} else {
|
|
@@ -15663,12 +15858,12 @@ async function runCommand(handler, options, formatter) {
|
|
|
15663
15858
|
console.error(escapeControlChars(result.hint));
|
|
15664
15859
|
}
|
|
15665
15860
|
if (options.json) {
|
|
15666
|
-
console.log(
|
|
15861
|
+
console.log(stringifyEnvelope(buildSuccessResponse(result.data)));
|
|
15667
15862
|
} else if (formatter) {
|
|
15668
15863
|
const formattedOutput = formatter(result.data);
|
|
15669
15864
|
console.log(escapeControlChars(formattedOutput));
|
|
15670
15865
|
} else {
|
|
15671
|
-
console.log(
|
|
15866
|
+
console.log(stringifyEnvelope(buildSuccessResponse(result.data)));
|
|
15672
15867
|
}
|
|
15673
15868
|
process.exit(EXIT_CODES.SUCCESS);
|
|
15674
15869
|
} catch (caught) {
|
|
@@ -15676,13 +15871,11 @@ async function runCommand(handler, options, formatter) {
|
|
|
15676
15871
|
if (error instanceof CommandError) {
|
|
15677
15872
|
if (options.json) {
|
|
15678
15873
|
console.log(
|
|
15679
|
-
|
|
15874
|
+
stringifyEnvelope(
|
|
15680
15875
|
OutputBuilder.buildJsonError(error.message, {
|
|
15681
15876
|
...error.metadata,
|
|
15682
15877
|
exitCode: error.exitCode
|
|
15683
|
-
})
|
|
15684
|
-
null,
|
|
15685
|
-
2
|
|
15878
|
+
})
|
|
15686
15879
|
)
|
|
15687
15880
|
);
|
|
15688
15881
|
} else {
|
|
@@ -15697,13 +15890,11 @@ async function runCommand(handler, options, formatter) {
|
|
|
15697
15890
|
if (isDaemonConnectionError(error)) {
|
|
15698
15891
|
if (options.json) {
|
|
15699
15892
|
console.log(
|
|
15700
|
-
|
|
15893
|
+
stringifyEnvelope(
|
|
15701
15894
|
OutputBuilder.buildJsonError(noActiveSessionMessage(), {
|
|
15702
15895
|
suggestion: startSessionSuggestion(),
|
|
15703
15896
|
exitCode: EXIT_CODES.RESOURCE_NOT_FOUND
|
|
15704
|
-
})
|
|
15705
|
-
null,
|
|
15706
|
-
2
|
|
15897
|
+
})
|
|
15707
15898
|
)
|
|
15708
15899
|
);
|
|
15709
15900
|
} else {
|
|
@@ -15713,9 +15904,7 @@ async function runCommand(handler, options, formatter) {
|
|
|
15713
15904
|
}
|
|
15714
15905
|
const exitCode = getErrorExitCode(error, EXIT_CODES.UNHANDLED_EXCEPTION);
|
|
15715
15906
|
if (options.json) {
|
|
15716
|
-
console.log(
|
|
15717
|
-
JSON.stringify(OutputBuilder.buildJsonError(errorMessage, { exitCode }), null, 2)
|
|
15718
|
-
);
|
|
15907
|
+
console.log(stringifyEnvelope(OutputBuilder.buildJsonError(errorMessage, { exitCode })));
|
|
15719
15908
|
} else {
|
|
15720
15909
|
console.error(genericError(errorMessage));
|
|
15721
15910
|
}
|
|
@@ -15770,6 +15959,9 @@ function lastSessionEndText(end) {
|
|
|
15770
15959
|
function endedSessionText(label, end) {
|
|
15771
15960
|
return `${label} ended ${sessionEndText(end)}`;
|
|
15772
15961
|
}
|
|
15962
|
+
function untrustedSessionText(label, why) {
|
|
15963
|
+
return `${label}: ${why}`;
|
|
15964
|
+
}
|
|
15773
15965
|
function sessionEndText(end) {
|
|
15774
15966
|
const why = {
|
|
15775
15967
|
crash: "Chrome crashed or was closed",
|
|
@@ -15827,7 +16019,7 @@ var SessionDirError = class extends DaemonError {
|
|
|
15827
16019
|
// src/daemon/launcher.ts
|
|
15828
16020
|
import { spawn } from "child_process";
|
|
15829
16021
|
import fs10 from "fs";
|
|
15830
|
-
import { join as
|
|
16022
|
+
import { join as join7 } from "path";
|
|
15831
16023
|
init_messages();
|
|
15832
16024
|
init_paths();
|
|
15833
16025
|
init_logging();
|
|
@@ -15838,6 +16030,7 @@ var log8 = createLogger("launcher");
|
|
|
15838
16030
|
var DAEMON_READY_TIMEOUT_MS = 5e3;
|
|
15839
16031
|
var DAEMON_READY_POLL_MS = 20;
|
|
15840
16032
|
async function launchDaemon() {
|
|
16033
|
+
assertUsableSessionDir();
|
|
15841
16034
|
if (await isDaemonAlive()) {
|
|
15842
16035
|
log8.debug("Daemon already running");
|
|
15843
16036
|
return void 0;
|
|
@@ -15848,11 +16041,10 @@ async function launchDaemon() {
|
|
|
15848
16041
|
"DAEMON_SCRIPT_NOT_FOUND"
|
|
15849
16042
|
);
|
|
15850
16043
|
}
|
|
15851
|
-
assertUsableSessionDir();
|
|
15852
16044
|
await stopUnreachableDaemon();
|
|
15853
|
-
const logPath =
|
|
16045
|
+
const logPath = join7(getSessionDir(), "daemon.log");
|
|
15854
16046
|
rotateLog(logPath);
|
|
15855
|
-
const logFd =
|
|
16047
|
+
const logFd = openDaemonLog(logPath);
|
|
15856
16048
|
log8.debug(`Starting daemon: ${DAEMON_SCRIPT_PATH}`);
|
|
15857
16049
|
const daemon = spawn(process.execPath, [DAEMON_SCRIPT_PATH], {
|
|
15858
16050
|
detached: true,
|
|
@@ -15908,6 +16100,8 @@ function assertUsableSessionDir() {
|
|
|
15908
16100
|
problem.denied ? EXIT_CODES.PERMISSION_DENIED : EXIT_CODES.SESSION_FILE_ERROR
|
|
15909
16101
|
);
|
|
15910
16102
|
}
|
|
16103
|
+
const untrusted = secureSessionDir(dir);
|
|
16104
|
+
if (untrusted) fail(untrustedSessionDirError(untrusted));
|
|
15911
16105
|
try {
|
|
15912
16106
|
ensureSessionDir();
|
|
15913
16107
|
fs10.accessSync(dir, fs10.constants.W_OK);
|
|
@@ -15920,6 +16114,18 @@ function assertUsableSessionDir() {
|
|
|
15920
16114
|
);
|
|
15921
16115
|
}
|
|
15922
16116
|
}
|
|
16117
|
+
var DAEMON_LOG_FLAGS = fs10.constants.O_WRONLY | fs10.constants.O_APPEND | fs10.constants.O_CREAT | (fs10.constants.O_NOFOLLOW ?? 0);
|
|
16118
|
+
function openDaemonLog(logPath) {
|
|
16119
|
+
try {
|
|
16120
|
+
return fs10.openSync(logPath, DAEMON_LOG_FLAGS, 384);
|
|
16121
|
+
} catch (error) {
|
|
16122
|
+
const err = daemonLogNotOpenedError(
|
|
16123
|
+
logPath,
|
|
16124
|
+
error.code ?? getErrorMessage(error)
|
|
16125
|
+
);
|
|
16126
|
+
throw new SessionDirError(err.message, err.suggestion);
|
|
16127
|
+
}
|
|
16128
|
+
}
|
|
15923
16129
|
var MAX_LOG_BYTES = 5 * 1024 * 1024;
|
|
15924
16130
|
function rotateLog(logPath) {
|
|
15925
16131
|
try {
|
|
@@ -15939,7 +16145,7 @@ async function waitForDaemonReady(hasExited) {
|
|
|
15939
16145
|
}
|
|
15940
16146
|
if (hasExited() && !await isDaemonAlive()) {
|
|
15941
16147
|
throw new DaemonStartupError(
|
|
15942
|
-
`Daemon exited during startup. See ${
|
|
16148
|
+
`Daemon exited during startup. See ${join7(getSessionDir(), "daemon.log")}`,
|
|
15943
16149
|
"DAEMON_EXITED"
|
|
15944
16150
|
);
|
|
15945
16151
|
}
|
|
@@ -16262,7 +16468,7 @@ function reportStartOutcome(outcome, options) {
|
|
|
16262
16468
|
exitCode: outcome.exitCode,
|
|
16263
16469
|
...outcome.details
|
|
16264
16470
|
});
|
|
16265
|
-
console.log(
|
|
16471
|
+
console.log(stringifyEnvelope(envelope));
|
|
16266
16472
|
} else {
|
|
16267
16473
|
console.error(escapeControlChars(outcome.human));
|
|
16268
16474
|
}
|
|
@@ -16283,7 +16489,7 @@ function reportStartOutcome(outcome, options) {
|
|
|
16283
16489
|
daemonPid: data.daemonPid,
|
|
16284
16490
|
...autoStopAt && { autoStopAt: autoStopAt.toISOString() }
|
|
16285
16491
|
};
|
|
16286
|
-
console.log(
|
|
16492
|
+
console.log(stringifyEnvelope(buildSuccessResponse(result)));
|
|
16287
16493
|
} else {
|
|
16288
16494
|
const page = {
|
|
16289
16495
|
url: data.targetUrl,
|
|
@@ -16524,9 +16730,10 @@ init_errors();
|
|
|
16524
16730
|
var CDP_HTTP_TIMEOUT_MS = 5e3;
|
|
16525
16731
|
async function probeDevToolsEndpoint(port, logger, options) {
|
|
16526
16732
|
const url = `${options?.secure ? "https" : "http"}://${options?.host ?? HTTP_LOCALHOST}:${port}/json/version`;
|
|
16733
|
+
const timeoutMs = options?.timeoutMs ?? CDP_HTTP_TIMEOUT_MS;
|
|
16527
16734
|
let response;
|
|
16528
16735
|
try {
|
|
16529
|
-
response = await fetch(url, { signal: AbortSignal.timeout(
|
|
16736
|
+
response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
16530
16737
|
} catch (error) {
|
|
16531
16738
|
logger?.debug(`Chrome version request failed: ${getErrorMessage(error)} (${url})`);
|
|
16532
16739
|
return { kind: "unreachable" };
|
|
@@ -16545,7 +16752,7 @@ async function probeDevToolsEndpoint(port, logger, options) {
|
|
|
16545
16752
|
init_suggestions();
|
|
16546
16753
|
init_url();
|
|
16547
16754
|
function expandHome(value) {
|
|
16548
|
-
return value.startsWith("~/") ? value.replace(/^~/,
|
|
16755
|
+
return value.startsWith("~/") ? value.replace(/^~/, os4.homedir()) : value;
|
|
16549
16756
|
}
|
|
16550
16757
|
function extractUserDataDirFromFlags(flags) {
|
|
16551
16758
|
const rest = [];
|
|
@@ -16837,6 +17044,10 @@ function assertValidChromeWsUrl(url) {
|
|
|
16837
17044
|
);
|
|
16838
17045
|
}
|
|
16839
17046
|
|
|
17047
|
+
// src/index.ts
|
|
17048
|
+
init_errors3();
|
|
17049
|
+
init_messages();
|
|
17050
|
+
|
|
16840
17051
|
// src/commands/cdp.ts
|
|
16841
17052
|
init_protocol();
|
|
16842
17053
|
init_schema();
|
|
@@ -17217,7 +17428,7 @@ function getMethodHint(methodName, result) {
|
|
|
17217
17428
|
return void 0;
|
|
17218
17429
|
}
|
|
17219
17430
|
function registerCdpCommand(program2) {
|
|
17220
|
-
program2.command("cdp").description(
|
|
17431
|
+
program2.command("cdp").summary("CDP protocol introspection and execution").description(
|
|
17221
17432
|
"CDP protocol introspection and execution\n Discovery: --list, --search, --describe\n Execution: case-insensitive (network.getcookies works)"
|
|
17222
17433
|
).argument("[method]", "CDP method name (e.g., Network.getCookies, network.getcookies)").addOption(new Option("--params <json>", "Method parameters as JSON")).addOption(
|
|
17223
17434
|
new Option("--list", "List all domains or methods in a domain").conflicts([
|
|
@@ -17855,7 +18066,7 @@ function handleDaemonConnectionError(error, options) {
|
|
|
17855
18066
|
exitCode,
|
|
17856
18067
|
...suggestion && { suggestion }
|
|
17857
18068
|
});
|
|
17858
|
-
console.log(follow ? JSON.stringify(envelope) :
|
|
18069
|
+
console.log(follow ? JSON.stringify(envelope) : stringifyEnvelope(envelope));
|
|
17859
18070
|
} else {
|
|
17860
18071
|
console.error(genericError(message));
|
|
17861
18072
|
}
|
|
@@ -17941,9 +18152,17 @@ async function fetchPreviewData(query = {}) {
|
|
|
17941
18152
|
async function fetchNetworkRequests(withHeaders = false) {
|
|
17942
18153
|
const result = await fetchPreviewData({ lastN: 0, only: "network", withHeaders });
|
|
17943
18154
|
if (!result.success) return result;
|
|
18155
|
+
const { totals, pageCrashedAt } = result.data.output;
|
|
17944
18156
|
return {
|
|
17945
18157
|
success: true,
|
|
17946
|
-
data: {
|
|
18158
|
+
data: {
|
|
18159
|
+
requests: result.data.network,
|
|
18160
|
+
pageCrashedAt,
|
|
18161
|
+
evictions: {
|
|
18162
|
+
requestsDropped: totals?.networkDropped ?? 0,
|
|
18163
|
+
bodiesEvicted: totals?.networkBodiesEvicted ?? 0
|
|
18164
|
+
}
|
|
18165
|
+
}
|
|
17947
18166
|
};
|
|
17948
18167
|
}
|
|
17949
18168
|
async function fetchConsoleMessages() {
|
|
@@ -18010,9 +18229,24 @@ async function setupFollowMode(refreshFn, options) {
|
|
|
18010
18229
|
process.on("SIGTERM", () => stop2(EXIT_CODES.TERMINATED));
|
|
18011
18230
|
}
|
|
18012
18231
|
|
|
18232
|
+
// src/commands/console.ts
|
|
18233
|
+
init_constants();
|
|
18234
|
+
|
|
18013
18235
|
// src/ui/formatters/console.ts
|
|
18014
18236
|
init_commands();
|
|
18015
18237
|
|
|
18238
|
+
// src/ui/formatters/console/chronological.ts
|
|
18239
|
+
init_constants();
|
|
18240
|
+
|
|
18241
|
+
// src/ui/formatters/longValues.ts
|
|
18242
|
+
init_commands();
|
|
18243
|
+
init_strings();
|
|
18244
|
+
function capForDisplay(text, maxLength, full) {
|
|
18245
|
+
if (full) return text;
|
|
18246
|
+
const capped = capLength(text, maxLength);
|
|
18247
|
+
return capped.truncatedFrom === void 0 ? text : `${capped.text}${moreCharsNote(capped.truncatedFrom - capped.text.length)}`;
|
|
18248
|
+
}
|
|
18249
|
+
|
|
18016
18250
|
// src/ui/formatters/console/chronological.ts
|
|
18017
18251
|
init_formatting();
|
|
18018
18252
|
|
|
@@ -18040,10 +18274,9 @@ function consoleMoreGroupsNote(more, level) {
|
|
|
18040
18274
|
return `(+${more} earlier distinct ${more === 1 ? level : `${level}s`}; ${sessionCommand(`bdg console --level ${level} --last 0`)} lists every one)`;
|
|
18041
18275
|
}
|
|
18042
18276
|
|
|
18043
|
-
// src/ui/formatters/console/chronological.ts
|
|
18044
|
-
init_strings();
|
|
18045
|
-
|
|
18046
18277
|
// src/ui/formatters/console/shared.ts
|
|
18278
|
+
init_constants();
|
|
18279
|
+
init_strings();
|
|
18047
18280
|
var DEFAULT_GROUP_LIMIT = 50;
|
|
18048
18281
|
function newestGroups(groups, limit) {
|
|
18049
18282
|
const keep = limit ?? DEFAULT_GROUP_LIMIT;
|
|
@@ -18170,9 +18403,13 @@ function formatSectionHeader(label, unique, total) {
|
|
|
18170
18403
|
function formatFramePosition(frame) {
|
|
18171
18404
|
return frame.lineNumber < 0 ? frame.url : `${frame.url}:${frame.lineNumber + 1}:${frame.columnNumber + 1}`;
|
|
18172
18405
|
}
|
|
18406
|
+
function capMessageText(message, full) {
|
|
18407
|
+
if (full) return message;
|
|
18408
|
+
const { text, truncatedFrom } = capLength(message.text, MAX_CONSOLE_JSON_TEXT_LENGTH);
|
|
18409
|
+
return truncatedFrom === void 0 ? message : { ...message, text, truncatedFrom };
|
|
18410
|
+
}
|
|
18173
18411
|
|
|
18174
18412
|
// src/ui/formatters/console/chronological.ts
|
|
18175
|
-
var MAX_LIST_TEXT_LENGTH = 200;
|
|
18176
18413
|
function formatConsoleChronological(messages, options) {
|
|
18177
18414
|
const fmt = new OutputFormatter();
|
|
18178
18415
|
const displayMessages = lastMessages(messages, options.last);
|
|
@@ -18203,8 +18440,8 @@ function formatConsoleChronological(messages, options) {
|
|
|
18203
18440
|
}
|
|
18204
18441
|
lastNavigationId = msg.navigationId;
|
|
18205
18442
|
}
|
|
18206
|
-
const
|
|
18207
|
-
fmt.text(`${index} ${level} ${time} ${
|
|
18443
|
+
const text = capForDisplay(msg.text, MAX_CONSOLE_TEXT_LENGTH, options.full);
|
|
18444
|
+
fmt.text(`${index} ${level} ${time} ${text}`);
|
|
18208
18445
|
const source = formatSourceLocation(msg.stackTrace);
|
|
18209
18446
|
if (source) {
|
|
18210
18447
|
fmt.text(`${sourceIndent}\u2192 ${source}`);
|
|
@@ -18221,56 +18458,20 @@ function lastMessages(messages, last) {
|
|
|
18221
18458
|
return last && last > 0 ? messages.slice(-last) : messages;
|
|
18222
18459
|
}
|
|
18223
18460
|
|
|
18224
|
-
// src/ui/formatters/console/json.ts
|
|
18225
|
-
function toJsonError(dedup, includeStackTrace) {
|
|
18226
|
-
const source = dedup.message.stackTrace?.[0];
|
|
18227
|
-
return {
|
|
18228
|
-
count: dedup.count,
|
|
18229
|
-
level: dedup.message.type,
|
|
18230
|
-
text: dedup.message.text,
|
|
18231
|
-
...dedup.message.index !== void 0 && { index: dedup.message.index },
|
|
18232
|
-
...source && {
|
|
18233
|
-
source: {
|
|
18234
|
-
url: source.url,
|
|
18235
|
-
...source.lineNumber >= 0 && {
|
|
18236
|
-
line: source.lineNumber + 1,
|
|
18237
|
-
column: source.columnNumber + 1
|
|
18238
|
-
}
|
|
18239
|
-
}
|
|
18240
|
-
},
|
|
18241
|
-
...includeStackTrace && dedup.message.stackTrace && { stackTrace: dedup.message.stackTrace }
|
|
18242
|
-
};
|
|
18243
|
-
}
|
|
18244
|
-
function buildConsoleJsonOutput(messages, options) {
|
|
18245
|
-
const { grouped, summary } = analyzeMessages(messages);
|
|
18246
|
-
const errors = newestGroups(grouped.errors, options.groupLimit);
|
|
18247
|
-
const warnings = newestGroups(grouped.warnings, options.groupLimit);
|
|
18248
|
-
const output = {
|
|
18249
|
-
summary,
|
|
18250
|
-
errors: errors.shown.map((d) => toJsonError(d, true)),
|
|
18251
|
-
warnings: warnings.shown.map((d) => toJsonError(d, false)),
|
|
18252
|
-
...errors.more > 0 && { moreErrors: errors.more },
|
|
18253
|
-
...warnings.more > 0 && { moreWarnings: warnings.more },
|
|
18254
|
-
...options.dropped && { dropped: options.dropped },
|
|
18255
|
-
...options.pageCrashedAt !== void 0 && { pageCrashedAt: options.pageCrashedAt }
|
|
18256
|
-
};
|
|
18257
|
-
if (options.list) output.messages = lastMessages(messages, options.last);
|
|
18258
|
-
return output;
|
|
18259
|
-
}
|
|
18260
|
-
function formatConsoleJson(messages, options) {
|
|
18261
|
-
return JSON.stringify(buildConsoleJsonOutput(messages, options), null, 2);
|
|
18262
|
-
}
|
|
18263
|
-
|
|
18264
18461
|
// src/ui/formatters/console/summarize.ts
|
|
18462
|
+
init_constants();
|
|
18265
18463
|
init_formatting();
|
|
18266
|
-
function
|
|
18464
|
+
function messageText(message, full) {
|
|
18465
|
+
return capForDisplay(message.text, MAX_CONSOLE_TEXT_LENGTH, full);
|
|
18466
|
+
}
|
|
18467
|
+
function renderErrorSection(fmt, errors, total, limit, full) {
|
|
18267
18468
|
if (errors.length === 0) return;
|
|
18268
18469
|
const { shown, more } = newestGroups(errors, limit);
|
|
18269
18470
|
fmt.text(formatSectionHeader("Errors", errors.length, total));
|
|
18270
18471
|
fmt.separator("\u2500", 30);
|
|
18271
18472
|
if (more > 0) fmt.text(consoleMoreGroupsNote(more, "error")).blank();
|
|
18272
18473
|
for (const { message, count } of shown) {
|
|
18273
|
-
fmt.text(`${formatCountPrefix(count)}${message
|
|
18474
|
+
fmt.text(`${formatCountPrefix(count)}${messageText(message, full)}`);
|
|
18274
18475
|
const source = formatSourceLocation(message.stackTrace);
|
|
18275
18476
|
if (source) {
|
|
18276
18477
|
fmt.text(` \u2192 ${source}`);
|
|
@@ -18278,14 +18479,14 @@ function renderErrorSection(fmt, errors, total, limit) {
|
|
|
18278
18479
|
fmt.blank();
|
|
18279
18480
|
}
|
|
18280
18481
|
}
|
|
18281
|
-
function renderWarningSection(fmt, warnings, total, limit) {
|
|
18482
|
+
function renderWarningSection(fmt, warnings, total, limit, full) {
|
|
18282
18483
|
if (warnings.length === 0) return;
|
|
18283
18484
|
const { shown, more } = newestGroups(warnings, limit);
|
|
18284
18485
|
fmt.text(formatSectionHeader("Warnings", warnings.length, total));
|
|
18285
18486
|
fmt.separator("\u2500", 30);
|
|
18286
18487
|
if (more > 0) fmt.text(consoleMoreGroupsNote(more, "warning"));
|
|
18287
18488
|
for (const { message, count } of shown) {
|
|
18288
|
-
fmt.text(`\u2022 ${formatCountPrefix(count)}${message
|
|
18489
|
+
fmt.text(`\u2022 ${formatCountPrefix(count)}${messageText(message, full)}`);
|
|
18289
18490
|
const source = formatSourceLocation(message.stackTrace);
|
|
18290
18491
|
if (source) fmt.text(` \u2192 ${source}`);
|
|
18291
18492
|
}
|
|
@@ -18309,8 +18510,9 @@ function formatConsoleSummary(messages, options = {}) {
|
|
|
18309
18510
|
fmt.separator("\u2501", 60);
|
|
18310
18511
|
if (options.dropped) fmt.text(consoleDroppedNote(options.dropped));
|
|
18311
18512
|
fmt.blank();
|
|
18312
|
-
|
|
18313
|
-
|
|
18513
|
+
const { groupLimit, full } = options;
|
|
18514
|
+
renderErrorSection(fmt, grouped.errors, summary.errors.total, groupLimit, full);
|
|
18515
|
+
renderWarningSection(fmt, grouped.warnings, summary.warnings.total, groupLimit, full);
|
|
18314
18516
|
if (grouped.errors.length === 0 && grouped.warnings.length === 0) {
|
|
18315
18517
|
fmt.text("No errors or warnings found");
|
|
18316
18518
|
fmt.blank();
|
|
@@ -18320,6 +18522,7 @@ function formatConsoleSummary(messages, options = {}) {
|
|
|
18320
18522
|
}
|
|
18321
18523
|
|
|
18322
18524
|
// src/ui/formatters/console/follow.ts
|
|
18525
|
+
init_constants();
|
|
18323
18526
|
init_formatting();
|
|
18324
18527
|
function formatConsoleFollowLines(messages, options = {}) {
|
|
18325
18528
|
const fmt = new OutputFormatter();
|
|
@@ -18333,18 +18536,56 @@ function formatConsoleFollowLines(messages, options = {}) {
|
|
|
18333
18536
|
for (const msg of messages) {
|
|
18334
18537
|
const time = formatTimestamp(msg.timestamp);
|
|
18335
18538
|
const level = msg.type.padEnd(7);
|
|
18336
|
-
fmt.text(`${time} ${level} ${msg.text}`);
|
|
18539
|
+
fmt.text(`${time} ${level} ${capForDisplay(msg.text, MAX_CONSOLE_TEXT_LENGTH, options.full)}`);
|
|
18337
18540
|
const source = formatSourceLocation(msg.stackTrace);
|
|
18338
18541
|
if (source) fmt.text(` \u2192 ${source}`);
|
|
18339
18542
|
}
|
|
18340
18543
|
return fmt.build();
|
|
18341
18544
|
}
|
|
18342
18545
|
|
|
18546
|
+
// src/ui/formatters/console/json.ts
|
|
18547
|
+
function toJsonError(dedup, includeStackTrace) {
|
|
18548
|
+
const source = dedup.message.stackTrace?.[0];
|
|
18549
|
+
return {
|
|
18550
|
+
count: dedup.count,
|
|
18551
|
+
level: dedup.message.type,
|
|
18552
|
+
text: dedup.message.text,
|
|
18553
|
+
...dedup.message.index !== void 0 && { index: dedup.message.index },
|
|
18554
|
+
...source && {
|
|
18555
|
+
source: {
|
|
18556
|
+
url: source.url,
|
|
18557
|
+
...source.lineNumber >= 0 && {
|
|
18558
|
+
line: source.lineNumber + 1,
|
|
18559
|
+
column: source.columnNumber + 1
|
|
18560
|
+
}
|
|
18561
|
+
}
|
|
18562
|
+
},
|
|
18563
|
+
...includeStackTrace && dedup.message.stackTrace && { stackTrace: dedup.message.stackTrace }
|
|
18564
|
+
};
|
|
18565
|
+
}
|
|
18566
|
+
function buildConsoleJsonOutput(messages, options) {
|
|
18567
|
+
const { grouped, summary } = analyzeMessages(messages);
|
|
18568
|
+
const errors = newestGroups(grouped.errors, options.groupLimit);
|
|
18569
|
+
const warnings = newestGroups(grouped.warnings, options.groupLimit);
|
|
18570
|
+
const output = {
|
|
18571
|
+
summary,
|
|
18572
|
+
errors: errors.shown.map((d) => capMessageText(toJsonError(d, true), options.full)),
|
|
18573
|
+
warnings: warnings.shown.map((d) => capMessageText(toJsonError(d, false), options.full)),
|
|
18574
|
+
...errors.more > 0 && { moreErrors: errors.more },
|
|
18575
|
+
...warnings.more > 0 && { moreWarnings: warnings.more },
|
|
18576
|
+
...options.dropped && { dropped: options.dropped },
|
|
18577
|
+
...options.pageCrashedAt !== void 0 && { pageCrashedAt: options.pageCrashedAt }
|
|
18578
|
+
};
|
|
18579
|
+
if (options.list) {
|
|
18580
|
+
output.messages = lastMessages(messages, options.last).map(
|
|
18581
|
+
(message) => capMessageText(message, options.full)
|
|
18582
|
+
);
|
|
18583
|
+
}
|
|
18584
|
+
return output;
|
|
18585
|
+
}
|
|
18586
|
+
|
|
18343
18587
|
// src/ui/formatters/console.ts
|
|
18344
18588
|
function formatConsole(messages, options) {
|
|
18345
|
-
if (options.json) {
|
|
18346
|
-
return formatConsoleJson(messages, options);
|
|
18347
|
-
}
|
|
18348
18589
|
const body = options.list || options.level ? formatConsoleChronological(messages, options) : formatConsoleSummary(messages, options);
|
|
18349
18590
|
return withPageCrashedNote(body, options.pageCrashedAt);
|
|
18350
18591
|
}
|
|
@@ -18402,12 +18643,12 @@ function buildFormatOptions(options, lastN, skipped, dropped, pageCrashedAt) {
|
|
|
18402
18643
|
...options.last !== void 0 && { groupLimit: lastN },
|
|
18403
18644
|
...dropped && { dropped },
|
|
18404
18645
|
...pageCrashedAt !== void 0 && { pageCrashedAt },
|
|
18405
|
-
json: options.json,
|
|
18406
18646
|
list: listsMessages(options),
|
|
18407
18647
|
follow: options.follow,
|
|
18408
18648
|
last: lastN,
|
|
18409
18649
|
history: options.history,
|
|
18410
18650
|
level: options.level,
|
|
18651
|
+
full: options.full,
|
|
18411
18652
|
skipped
|
|
18412
18653
|
};
|
|
18413
18654
|
}
|
|
@@ -18437,13 +18678,15 @@ async function runFollowMode(options, lastN) {
|
|
|
18437
18678
|
const data = buildConsoleJsonOutput(backlog, {
|
|
18438
18679
|
list: true,
|
|
18439
18680
|
last: 0,
|
|
18440
|
-
pageCrashedAt: crashedAt
|
|
18681
|
+
pageCrashedAt: crashedAt,
|
|
18682
|
+
full: options.full
|
|
18441
18683
|
});
|
|
18442
18684
|
console.log(JSON.stringify(buildSuccessResponse(data)));
|
|
18443
18685
|
}
|
|
18444
18686
|
} else {
|
|
18445
18687
|
const text = formatConsoleFollowLines(backlog, {
|
|
18446
18688
|
header: !started,
|
|
18689
|
+
full: options.full,
|
|
18447
18690
|
...navigated && currentNavigationId !== void 0 && { navigationId: currentNavigationId }
|
|
18448
18691
|
});
|
|
18449
18692
|
if (text) console.log(text);
|
|
@@ -18478,7 +18721,12 @@ function registerConsoleCommand(program2) {
|
|
|
18478
18721
|
"--level <level>",
|
|
18479
18722
|
"Filter by message level: error, warning, info (includes log), debug; any case"
|
|
18480
18723
|
).argParser(consoleLevelOption)
|
|
18481
|
-
).addOption(consoleLastOption).addOption(
|
|
18724
|
+
).addOption(consoleLastOption).addOption(
|
|
18725
|
+
new Option(
|
|
18726
|
+
"--full",
|
|
18727
|
+
`Print message texts whole (default: the first ${MAX_CONSOLE_TEXT_LENGTH} characters, ${MAX_CONSOLE_JSON_TEXT_LENGTH} in JSON)`
|
|
18728
|
+
).default(false)
|
|
18729
|
+
).addOption(jsonOption()).action(async (options) => {
|
|
18482
18730
|
let lastN;
|
|
18483
18731
|
try {
|
|
18484
18732
|
lastN = positiveIntRule({
|
|
@@ -18644,144 +18892,66 @@ async function search(text, options) {
|
|
|
18644
18892
|
// src/commands/details.ts
|
|
18645
18893
|
init_client();
|
|
18646
18894
|
|
|
18647
|
-
// src/
|
|
18895
|
+
// src/ui/messages/networkMessages.ts
|
|
18648
18896
|
init_constants();
|
|
18897
|
+
init_formatting();
|
|
18898
|
+
function megabytes(bytes) {
|
|
18899
|
+
return `${Math.round(bytes / (1024 * 1024))} MB`;
|
|
18900
|
+
}
|
|
18901
|
+
function networkEvictedNote(counts) {
|
|
18902
|
+
const { requestsDropped, bodiesEvicted } = counts;
|
|
18903
|
+
const requests = pluralize(requestsDropped, "older network request");
|
|
18904
|
+
const bodies = pluralize(bodiesEvicted, "older response body", "older response bodies");
|
|
18905
|
+
const budget = megabytes(MAX_TOTAL_BODY_BYTES);
|
|
18906
|
+
if (requestsDropped > 0 && bodiesEvicted > 0) {
|
|
18907
|
+
return `\u26A0 ${requests} dropped, ${bodies} evicted: bdg keeps the newest ${MAX_NETWORK_REQUESTS} requests and ${budget} of bodies`;
|
|
18908
|
+
}
|
|
18909
|
+
if (requestsDropped > 0) {
|
|
18910
|
+
return `\u26A0 ${requests} ${requestsDropped === 1 ? "was" : "were"} dropped: bdg keeps the newest ${MAX_NETWORK_REQUESTS}`;
|
|
18911
|
+
}
|
|
18912
|
+
if (bodiesEvicted > 0) {
|
|
18913
|
+
return `\u26A0 ${bodies} ${bodiesEvicted === 1 ? "was" : "were"} evicted: bdg keeps the newest ${budget} of bodies`;
|
|
18914
|
+
}
|
|
18915
|
+
return void 0;
|
|
18916
|
+
}
|
|
18917
|
+
function followingNetworkMessage() {
|
|
18918
|
+
return "Following network requests... (Ctrl+C to stop)";
|
|
18919
|
+
}
|
|
18920
|
+
function stoppedFollowingNetworkMessage() {
|
|
18921
|
+
return "\nStopped following network requests.";
|
|
18922
|
+
}
|
|
18923
|
+
function headerRepeatedNote(count) {
|
|
18924
|
+
return `(sent ${count} times)`;
|
|
18925
|
+
}
|
|
18926
|
+
function localProxyNote() {
|
|
18927
|
+
return "(loopback; likely a local proxy)";
|
|
18928
|
+
}
|
|
18929
|
+
function harSanitizedComment() {
|
|
18930
|
+
return "Sanitized by bdg: values of auth, cookie, API key, token and session headers, cookies, credential query parameters in URLs, and password/token fields of request bodies are [redacted] (by name, so some harmless values are too); response bodies and WebSocket messages are not sanitized. Export with --include-sensitive to keep everything";
|
|
18931
|
+
}
|
|
18932
|
+
function harExportedMessage(result) {
|
|
18933
|
+
const filterNote = result.filtered ? " (filtered)" : "";
|
|
18934
|
+
const note = result.sanitized ? "Credentials sanitized (auth/cookie/API key/token headers, cookies, URL tokens, password and token body fields are [redacted]); --include-sensitive keeps them" : "\u26A0 Includes credentials (--include-sensitive): share this file with care";
|
|
18935
|
+
return `\u2713 Exported ${result.entries} requests${filterNote} to ${result.file}
|
|
18936
|
+
${note}`;
|
|
18937
|
+
}
|
|
18649
18938
|
|
|
18650
|
-
// src/telemetry/
|
|
18651
|
-
|
|
18652
|
-
|
|
18653
|
-
|
|
18939
|
+
// src/telemetry/networkRetention.ts
|
|
18940
|
+
var SKIPPED_BODY_PATTERN = /^\[SKIPPED: (.*)\]$/s;
|
|
18941
|
+
function skippedBodyReason(body) {
|
|
18942
|
+
return body === void 0 ? void 0 : SKIPPED_BODY_PATTERN.exec(body)?.[1];
|
|
18943
|
+
}
|
|
18654
18944
|
|
|
18655
|
-
// src/telemetry/
|
|
18656
|
-
|
|
18657
|
-
|
|
18658
|
-
|
|
18659
|
-
|
|
18660
|
-
|
|
18661
|
-
|
|
18662
|
-
"
|
|
18663
|
-
|
|
18664
|
-
|
|
18665
|
-
"doubleclick.net",
|
|
18666
|
-
"google-analytics.com",
|
|
18667
|
-
"clarity.ms",
|
|
18668
|
-
"bat.bing.com",
|
|
18669
|
-
"facebook.com",
|
|
18670
|
-
"connect.facebook.net",
|
|
18671
|
-
"tiktok.com",
|
|
18672
|
-
"linkedin.com",
|
|
18673
|
-
"twitter.com",
|
|
18674
|
-
"snapchat.com",
|
|
18675
|
-
"mixpanel.com",
|
|
18676
|
-
"segment.com",
|
|
18677
|
-
"segment.io",
|
|
18678
|
-
"amplitude.com",
|
|
18679
|
-
"heap.io",
|
|
18680
|
-
"fullstory.com",
|
|
18681
|
-
"hotjar.com",
|
|
18682
|
-
"logrocket.com",
|
|
18683
|
-
"smartlook.com",
|
|
18684
|
-
"exactag.com",
|
|
18685
|
-
"criteo.com",
|
|
18686
|
-
"adroll.com",
|
|
18687
|
-
"outbrain.com",
|
|
18688
|
-
"taboola.com",
|
|
18689
|
-
"confirmit.com",
|
|
18690
|
-
"newrelic.com",
|
|
18691
|
-
"datadoghq.com",
|
|
18692
|
-
"sentry.io",
|
|
18693
|
-
"googlesyndication.com",
|
|
18694
|
-
"googletagservices.com",
|
|
18695
|
-
"adservice.google.com",
|
|
18696
|
-
"amazon-adsystem.com",
|
|
18697
|
-
"adnxs.com",
|
|
18698
|
-
"rubiconproject.com",
|
|
18699
|
-
"pubmatic.com",
|
|
18700
|
-
"casalemedia.com",
|
|
18701
|
-
"indexww.com",
|
|
18702
|
-
"openx.net",
|
|
18703
|
-
"3lift.com",
|
|
18704
|
-
"teads.tv",
|
|
18705
|
-
"bidswitch.net",
|
|
18706
|
-
"sharethrough.com",
|
|
18707
|
-
"adsafeprotected.com",
|
|
18708
|
-
"moatads.com",
|
|
18709
|
-
"doubleverify.com",
|
|
18710
|
-
"scorecardresearch.com",
|
|
18711
|
-
"imrworldwide.com",
|
|
18712
|
-
"chartbeat.com",
|
|
18713
|
-
"chartbeat.net",
|
|
18714
|
-
"permutive.com",
|
|
18715
|
-
"permutive.app",
|
|
18716
|
-
"optimizely.com",
|
|
18717
|
-
"quantserve.com",
|
|
18718
|
-
"quantcount.com",
|
|
18719
|
-
"krxd.net",
|
|
18720
|
-
"demdex.net",
|
|
18721
|
-
"omtrdc.net",
|
|
18722
|
-
"everesttech.net",
|
|
18723
|
-
"xiti.com",
|
|
18724
|
-
"ati-host.net"
|
|
18725
|
-
];
|
|
18726
|
-
var EXCLUDED_DOMAINS_SET = new Set(DEFAULT_EXCLUDED_DOMAINS.map((d) => d.toLowerCase()));
|
|
18727
|
-
var DEFAULT_SKIP_BODY_MIME_TYPES = [
|
|
18728
|
-
"text/css",
|
|
18729
|
-
"image/svg+xml",
|
|
18730
|
-
"image/png",
|
|
18731
|
-
"image/jpeg",
|
|
18732
|
-
"image/gif",
|
|
18733
|
-
"image/webp",
|
|
18734
|
-
"image/avif",
|
|
18735
|
-
"image/bmp",
|
|
18736
|
-
"image/tiff",
|
|
18737
|
-
"font/woff",
|
|
18738
|
-
"font/woff2",
|
|
18739
|
-
"font/ttf",
|
|
18740
|
-
"font/eot",
|
|
18741
|
-
"font/otf",
|
|
18742
|
-
"application/font-woff",
|
|
18743
|
-
"application/font-woff2",
|
|
18744
|
-
"video/mp4",
|
|
18745
|
-
"video/webm",
|
|
18746
|
-
"audio/mpeg",
|
|
18747
|
-
"audio/wav"
|
|
18748
|
-
];
|
|
18749
|
-
var SKIP_BODY_MIME_TYPES_SET = new Set(DEFAULT_SKIP_BODY_MIME_TYPES.map((m) => m.toLowerCase()));
|
|
18750
|
-
function matchesWildcard(str, pattern) {
|
|
18751
|
-
const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
18752
|
-
const regex = new RegExp(`^${regexPattern}$`, "i");
|
|
18753
|
-
return regex.test(str);
|
|
18754
|
-
}
|
|
18755
|
-
function filterByResourceType(requests, types) {
|
|
18756
|
-
if (types.length === 0) {
|
|
18757
|
-
return requests;
|
|
18758
|
-
}
|
|
18759
|
-
return requests.filter((req) => {
|
|
18760
|
-
if (!req.resourceType) {
|
|
18761
|
-
return false;
|
|
18762
|
-
}
|
|
18763
|
-
return types.includes(req.resourceType);
|
|
18764
|
-
});
|
|
18765
|
-
}
|
|
18766
|
-
|
|
18767
|
-
// src/telemetry/network.ts
|
|
18768
|
-
var log22 = createLogger("network");
|
|
18769
|
-
var SKIPPED_BODY_PATTERN = /^\[SKIPPED: (.*)\]$/s;
|
|
18770
|
-
function skippedBodyReason(body) {
|
|
18771
|
-
return body === void 0 ? void 0 : SKIPPED_BODY_PATTERN.exec(body)?.[1];
|
|
18772
|
-
}
|
|
18773
|
-
var MAX_FRAME_PAYLOAD_SIZE = 100 * 1024;
|
|
18774
|
-
|
|
18775
|
-
// src/telemetry/requestState.ts
|
|
18776
|
-
function getRequestState(request) {
|
|
18777
|
-
if (request.status === void 0) return "pending";
|
|
18778
|
-
return request.status === 0 ? "failed" : "complete";
|
|
18779
|
-
}
|
|
18780
|
-
function failureReason(request) {
|
|
18781
|
-
if (!request.errorText) return void 0;
|
|
18782
|
-
return getRequestState(request) === "complete" && request.errorText === BODY_ABORTED ? void 0 : request.errorText;
|
|
18783
|
-
}
|
|
18784
|
-
var BODY_ABORTED = "net::ERR_ABORTED";
|
|
18945
|
+
// src/telemetry/requestState.ts
|
|
18946
|
+
function getRequestState(request) {
|
|
18947
|
+
if (request.status === void 0) return "pending";
|
|
18948
|
+
return request.status === 0 ? "failed" : "complete";
|
|
18949
|
+
}
|
|
18950
|
+
function failureReason(request) {
|
|
18951
|
+
if (!request.errorText) return void 0;
|
|
18952
|
+
return getRequestState(request) === "complete" && request.errorText === BODY_ABORTED ? void 0 : request.errorText;
|
|
18953
|
+
}
|
|
18954
|
+
var BODY_ABORTED = "net::ERR_ABORTED";
|
|
18785
18955
|
|
|
18786
18956
|
// src/ui/formatters/requestStatus.ts
|
|
18787
18957
|
function formatRequestStatus(request) {
|
|
@@ -18793,22 +18963,6 @@ function formatRequestStatus(request) {
|
|
|
18793
18963
|
|
|
18794
18964
|
// src/ui/formatters/networkHeaders.ts
|
|
18795
18965
|
init_formatting();
|
|
18796
|
-
|
|
18797
|
-
// src/ui/messages/networkMessages.ts
|
|
18798
|
-
function followingNetworkMessage() {
|
|
18799
|
-
return "Following network requests... (Ctrl+C to stop)";
|
|
18800
|
-
}
|
|
18801
|
-
function stoppedFollowingNetworkMessage() {
|
|
18802
|
-
return "\nStopped following network requests.";
|
|
18803
|
-
}
|
|
18804
|
-
function headerRepeatedNote(count) {
|
|
18805
|
-
return `(sent ${count} times)`;
|
|
18806
|
-
}
|
|
18807
|
-
function localProxyNote() {
|
|
18808
|
-
return "(loopback; likely a local proxy)";
|
|
18809
|
-
}
|
|
18810
|
-
|
|
18811
|
-
// src/ui/formatters/networkHeaders.ts
|
|
18812
18966
|
function formatNetworkHeaders(data) {
|
|
18813
18967
|
const fmt = new OutputFormatter();
|
|
18814
18968
|
fmt.text("Network Request Headers").separator("\u2501", 60).blank();
|
|
@@ -19435,6 +19589,9 @@ function findUnknownModifiers(modifiers) {
|
|
|
19435
19589
|
return modifierNames(modifiers).filter((name) => !(name in MODIFIER_NAMES));
|
|
19436
19590
|
}
|
|
19437
19591
|
|
|
19592
|
+
// src/ui/formatters/triggeredRequests.ts
|
|
19593
|
+
init_constants();
|
|
19594
|
+
|
|
19438
19595
|
// src/telemetry/requestKinds.ts
|
|
19439
19596
|
var ACTIVITY_RESOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
19440
19597
|
"Document",
|
|
@@ -19489,7 +19646,10 @@ function formatTriggeredRequestLines(requests, omitted = 0) {
|
|
|
19489
19646
|
const assets = requests.filter((request) => !isNotableRequest(request));
|
|
19490
19647
|
const lines = notable.slice(0, MAX_TRIGGERED_REQUESTS_SHOWN).map(formatTriggeredRequest);
|
|
19491
19648
|
const hidden = notable.length - lines.length + omitted;
|
|
19492
|
-
if (hidden > 0)
|
|
19649
|
+
if (hidden > 0)
|
|
19650
|
+
lines.push(
|
|
19651
|
+
omitted > 0 ? moreRequestsNote(hidden) : moreMatchesNote(hidden, MAX_TRIGGERED_REQUESTS)
|
|
19652
|
+
);
|
|
19493
19653
|
if (assets.length > 0) lines.push(assetRequestsNote(assets.length, assetTypeNames(assets)));
|
|
19494
19654
|
return lines;
|
|
19495
19655
|
}
|
|
@@ -19513,7 +19673,10 @@ function registerFormInteractionCommands(program2) {
|
|
|
19513
19673
|
const err = internalError("DOM command group not found");
|
|
19514
19674
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.SOFTWARE_ERROR);
|
|
19515
19675
|
}
|
|
19516
|
-
domCommand.command("fill").description("Fill a form field with a value (React-compatible, waits for stability)").argument("<selectorOrIndex>", SELECTOR_OR_INDEX_ARGUMENT).argument(
|
|
19676
|
+
domCommand.command("fill").description("Fill a form field with a value (React-compatible, waits for stability)").argument("<selectorOrIndex>", SELECTOR_OR_INDEX_ARGUMENT).argument(
|
|
19677
|
+
"<value>",
|
|
19678
|
+
'Value to fill (file inputs: local file paths, separated by commas, that are uploaded to the page; "" clears)'
|
|
19679
|
+
).option("--index <n>", "Element index if selector matches multiple (0-based)", integerOption(0)).option("--no-blur", "Do not blur after filling (keeps focus on element)").option("--no-wait", "Skip waiting for network stability after fill").addOption(jsonOption()).action(async (selectorOrIndex, value, options) => {
|
|
19517
19680
|
await runCommand(
|
|
19518
19681
|
() => runElementCommand({
|
|
19519
19682
|
selectorOrIndex,
|
|
@@ -19939,12 +20102,12 @@ function synthesizeA11yNode(domContext, nodeId) {
|
|
|
19939
20102
|
// src/ui/formatters/keyAttributes.ts
|
|
19940
20103
|
init_formatting();
|
|
19941
20104
|
init_url();
|
|
19942
|
-
var
|
|
20105
|
+
var MAX_VALUE_LENGTH2 = 40;
|
|
19943
20106
|
function fileName(src) {
|
|
19944
20107
|
if (/^(data|blob):/i.test(src)) return src;
|
|
19945
|
-
const
|
|
19946
|
-
const name =
|
|
19947
|
-
return name && name !==
|
|
20108
|
+
const path8 = src.split(/[?#]/)[0] ?? src;
|
|
20109
|
+
const name = path8.substring(path8.lastIndexOf("/") + 1);
|
|
20110
|
+
return name && name !== path8 ? `\u2026/${name}` : src;
|
|
19948
20111
|
}
|
|
19949
20112
|
function hostOf(src) {
|
|
19950
20113
|
const parsed = /^https?:/i.test(src) ? safeParseUrl(src) : null;
|
|
@@ -19953,13 +20116,13 @@ function hostOf(src) {
|
|
|
19953
20116
|
return `//${parsed.host}${more}`;
|
|
19954
20117
|
}
|
|
19955
20118
|
function shortUrl(url) {
|
|
19956
|
-
return /^https?:/i.test(url) ? `//${truncateUrl(url,
|
|
20119
|
+
return /^https?:/i.test(url) ? `//${truncateUrl(url, MAX_VALUE_LENGTH2 - 2)}` : url;
|
|
19957
20120
|
}
|
|
19958
20121
|
function shortValue(tag, name, value) {
|
|
19959
20122
|
let short = value;
|
|
19960
20123
|
if (name === "src") short = tag === "iframe" ? hostOf(value) : fileName(value);
|
|
19961
20124
|
else if (name === "href" || name === "action") short = shortUrl(value);
|
|
19962
|
-
return cutMiddle(escapeControlChars(short.replace(/\s+/g, " ").trim()),
|
|
20125
|
+
return cutMiddle(escapeControlChars(short.replace(/\s+/g, " ").trim()), MAX_VALUE_LENGTH2);
|
|
19963
20126
|
}
|
|
19964
20127
|
function keyAttributeItems(tag, attributes, leaveOut = /* @__PURE__ */ new Set()) {
|
|
19965
20128
|
return Object.entries(attributes ?? {}).flatMap(([name, value]) => {
|
|
@@ -20059,6 +20222,7 @@ function withSecretMasked(node, domContext) {
|
|
|
20059
20222
|
}
|
|
20060
20223
|
|
|
20061
20224
|
// src/commands/dom/a11y.ts
|
|
20225
|
+
init_constants();
|
|
20062
20226
|
init_errors3();
|
|
20063
20227
|
init_messages();
|
|
20064
20228
|
|
|
@@ -20072,7 +20236,7 @@ init_elementInfo();
|
|
|
20072
20236
|
init_frameScopedConnection();
|
|
20073
20237
|
init_logging();
|
|
20074
20238
|
init_errors();
|
|
20075
|
-
var
|
|
20239
|
+
var log27 = createLogger("dom");
|
|
20076
20240
|
function childFrameIds(tree) {
|
|
20077
20241
|
return (tree?.childFrames ?? []).flatMap((child) => [child.frame.id, ...childFrameIds(child)]);
|
|
20078
20242
|
}
|
|
@@ -20109,6 +20273,51 @@ function buildTreeFromRawNodes(rawNodes) {
|
|
|
20109
20273
|
const root = nodes.get(rootRaw.nodeId);
|
|
20110
20274
|
return { root, nodes, count: nodes.size };
|
|
20111
20275
|
}
|
|
20276
|
+
var LAYOUT_ROLES = /* @__PURE__ */ new Set([
|
|
20277
|
+
"generic",
|
|
20278
|
+
"none",
|
|
20279
|
+
"presentation",
|
|
20280
|
+
"LayoutTable",
|
|
20281
|
+
"LayoutTableRow",
|
|
20282
|
+
"LayoutTableCell"
|
|
20283
|
+
]);
|
|
20284
|
+
function isTreeNoise(node, parentName) {
|
|
20285
|
+
return node.role === "InlineTextBox" || node.role === "StaticText" && (node.name === parentName || !node.name?.trim()) || LAYOUT_ROLES.has(node.role) && !node.name;
|
|
20286
|
+
}
|
|
20287
|
+
function listA11yTree(tree, limit, maxDepth) {
|
|
20288
|
+
const nodes = [];
|
|
20289
|
+
const visited = /* @__PURE__ */ new Set();
|
|
20290
|
+
let omitted = 0;
|
|
20291
|
+
let skipped = 0;
|
|
20292
|
+
const visit = (node, depth, parentName) => {
|
|
20293
|
+
if (visited.has(node.nodeId)) return;
|
|
20294
|
+
visited.add(node.nodeId);
|
|
20295
|
+
const listable = !isTreeNoise(node, parentName);
|
|
20296
|
+
if (listable) {
|
|
20297
|
+
const fits = (limit === 0 || nodes.length < limit) && (maxDepth ?? depth) >= depth;
|
|
20298
|
+
if (fits) nodes.push(listedNode(node, depth));
|
|
20299
|
+
else omitted++;
|
|
20300
|
+
} else {
|
|
20301
|
+
skipped++;
|
|
20302
|
+
}
|
|
20303
|
+
for (const childId of node.childIds ?? []) {
|
|
20304
|
+
const child = tree.nodes.get(childId);
|
|
20305
|
+
if (child) visit(child, listable ? depth + 1 : depth, node.name ?? parentName);
|
|
20306
|
+
}
|
|
20307
|
+
};
|
|
20308
|
+
visit(tree.root, 0, void 0);
|
|
20309
|
+
for (const node of tree.nodes.values()) visit(node, 0, void 0);
|
|
20310
|
+
return {
|
|
20311
|
+
nodes,
|
|
20312
|
+
count: tree.count,
|
|
20313
|
+
...omitted > 0 && { omitted },
|
|
20314
|
+
...skipped > 0 && { skipped }
|
|
20315
|
+
};
|
|
20316
|
+
}
|
|
20317
|
+
function listedNode(node, depth) {
|
|
20318
|
+
const { childIds: _childIds, ...rest } = node;
|
|
20319
|
+
return { ...rest, depth };
|
|
20320
|
+
}
|
|
20112
20321
|
function visibleChildIds(rawNode, rawById, seen = /* @__PURE__ */ new Set()) {
|
|
20113
20322
|
return (rawNode.childIds ?? []).flatMap((childId) => {
|
|
20114
20323
|
const child = rawById.get(childId);
|
|
@@ -20398,49 +20607,16 @@ var IGNORED_REASONS = {
|
|
|
20398
20607
|
// src/ui/formatters/a11y.ts
|
|
20399
20608
|
init_formatting();
|
|
20400
20609
|
init_commands();
|
|
20401
|
-
var MAX_TREE_NODES_DISPLAY = 50;
|
|
20402
20610
|
var SEPARATOR_WIDTH = 50;
|
|
20403
20611
|
function formatA11yTree(tree) {
|
|
20404
20612
|
const fmt = new OutputFormatter();
|
|
20405
20613
|
fmt.text(`Accessibility Tree (${tree.count} nodes)`).separator("\u2500", SEPARATOR_WIDTH).blank();
|
|
20406
|
-
|
|
20407
|
-
|
|
20408
|
-
|
|
20409
|
-
fmt.blank().text(
|
|
20410
|
-
`Showing the first ${MAX_TREE_NODES_DISPLAY} nodes (text boxes and repeated text left out)`
|
|
20411
|
-
).text('Use --json flag for complete output, or bdg dom a11y query "role:<role>" to search');
|
|
20614
|
+
tree.nodes.forEach((node) => fmt.text(" ".repeat(node.depth) + formatA11yNodeOneLine(node)));
|
|
20615
|
+
if (tree.omitted) {
|
|
20616
|
+
fmt.blank().text(a11yTreeShownNote(tree.nodes.length)).text(a11yTreeMoreNote(tree.omitted));
|
|
20412
20617
|
}
|
|
20413
20618
|
return fmt.build();
|
|
20414
20619
|
}
|
|
20415
|
-
var LAYOUT_ROLES = /* @__PURE__ */ new Set([
|
|
20416
|
-
"generic",
|
|
20417
|
-
"none",
|
|
20418
|
-
"presentation",
|
|
20419
|
-
"LayoutTable",
|
|
20420
|
-
"LayoutTableRow",
|
|
20421
|
-
"LayoutTableCell"
|
|
20422
|
-
]);
|
|
20423
|
-
function treeLines(tree) {
|
|
20424
|
-
const lines = [];
|
|
20425
|
-
const visited = /* @__PURE__ */ new Set();
|
|
20426
|
-
let truncated = false;
|
|
20427
|
-
const visit = (node, depth, parentName) => {
|
|
20428
|
-
if (visited.has(node.nodeId)) return;
|
|
20429
|
-
visited.add(node.nodeId);
|
|
20430
|
-
if (lines.length >= MAX_TREE_NODES_DISPLAY) {
|
|
20431
|
-
truncated = true;
|
|
20432
|
-
return;
|
|
20433
|
-
}
|
|
20434
|
-
const skip = node.role === "InlineTextBox" || node.role === "StaticText" && (node.name === parentName || !node.name?.trim()) || LAYOUT_ROLES.has(node.role) && !node.name;
|
|
20435
|
-
if (!skip) lines.push(" ".repeat(depth) + formatA11yNodeOneLine(node));
|
|
20436
|
-
for (const childId of node.childIds ?? []) {
|
|
20437
|
-
const child = tree.nodes.get(childId);
|
|
20438
|
-
if (child) visit(child, skip ? depth : depth + 1, node.name ?? parentName);
|
|
20439
|
-
}
|
|
20440
|
-
};
|
|
20441
|
-
visit(tree.root, 0, void 0);
|
|
20442
|
-
return { lines, truncated };
|
|
20443
|
-
}
|
|
20444
20620
|
var FILLABLE_ROLES = /* @__PURE__ */ new Set([
|
|
20445
20621
|
"textbox",
|
|
20446
20622
|
"searchbox",
|
|
@@ -20571,19 +20747,14 @@ function truncate(str, maxLen) {
|
|
|
20571
20747
|
// src/commands/dom/a11y.ts
|
|
20572
20748
|
init_exitCodes();
|
|
20573
20749
|
var A11Y_QUERY_LIMIT = 50;
|
|
20750
|
+
var A11Y_TREE_LIMIT = 50;
|
|
20574
20751
|
async function handleA11yTree(options) {
|
|
20752
|
+
const listTree = async () => listA11yTree(await collectA11yTree(), options.limit ?? A11Y_TREE_LIMIT, options.depth);
|
|
20575
20753
|
if (options.json) {
|
|
20576
|
-
await runJsonCommand(
|
|
20577
|
-
const tree = await collectA11yTree();
|
|
20578
|
-
return {
|
|
20579
|
-
root: tree.root,
|
|
20580
|
-
nodes: Array.from(tree.nodes.values()),
|
|
20581
|
-
count: tree.count
|
|
20582
|
-
};
|
|
20583
|
-
});
|
|
20754
|
+
await runJsonCommand(listTree);
|
|
20584
20755
|
}
|
|
20585
20756
|
await runCommand(
|
|
20586
|
-
async () => ({ success: true, data: await
|
|
20757
|
+
async () => ({ success: true, data: await listTree() }),
|
|
20587
20758
|
options,
|
|
20588
20759
|
formatA11yTree
|
|
20589
20760
|
);
|
|
@@ -20632,7 +20803,10 @@ async function handleA11yQuery(pattern, options) {
|
|
|
20632
20803
|
);
|
|
20633
20804
|
return {
|
|
20634
20805
|
success: true,
|
|
20635
|
-
data: limitMatches(
|
|
20806
|
+
data: limitMatches(
|
|
20807
|
+
indexed,
|
|
20808
|
+
options.limit ?? (options.json ? QUERY_JSON_LIST_LIMIT : A11Y_QUERY_LIMIT)
|
|
20809
|
+
)
|
|
20636
20810
|
};
|
|
20637
20811
|
},
|
|
20638
20812
|
options,
|
|
@@ -20704,7 +20878,17 @@ function registerA11yCommands(domCmd) {
|
|
|
20704
20878
|
await handleA11yQuery(`name:${quoteQueryValue(search2)}`, options);
|
|
20705
20879
|
}
|
|
20706
20880
|
});
|
|
20707
|
-
a11y.command("tree").description(
|
|
20881
|
+
a11y.command("tree").description(
|
|
20882
|
+
"List the accessibility tree depth-first (ignored nodes, text boxes, repeated text and nameless wrappers left out)"
|
|
20883
|
+
).option(
|
|
20884
|
+
"--limit <n>",
|
|
20885
|
+
`Nodes to list (default: ${A11Y_TREE_LIMIT}, also with --json; 0 = all listed nodes (text boxes and empty wrappers are always skipped))`,
|
|
20886
|
+
integerOption(0)
|
|
20887
|
+
).option(
|
|
20888
|
+
"--depth <n>",
|
|
20889
|
+
"Levels below the root to list (0 = root only; default: all)",
|
|
20890
|
+
integerOption(0)
|
|
20891
|
+
).addOption(jsonOption()).action(async (options) => {
|
|
20708
20892
|
await handleA11yTree(options);
|
|
20709
20893
|
});
|
|
20710
20894
|
a11y.command("query").description('Query elements by accessibility properties (e.g., "role:button", "name:Submit")').argument(
|
|
@@ -20712,7 +20896,7 @@ function registerA11yCommands(domCmd) {
|
|
|
20712
20896
|
"Fields role, name, description as key:value or key=value, separated by spaces or commas; * is a wildcard. A name or description runs to the next field or the end, so it may contain spaces and colons; quote the whole pattern (e.g. 'role=button name=Sign in', 'name=E-mail address:')"
|
|
20713
20897
|
).option(
|
|
20714
20898
|
"--limit <n>",
|
|
20715
|
-
`Matches to list (default: ${A11Y_QUERY_LIMIT},
|
|
20899
|
+
`Matches to list (default: ${A11Y_QUERY_LIMIT}, ${QUERY_JSON_LIST_LIMIT} with --json; 0 = all); all are indexed`,
|
|
20716
20900
|
integerOption(0)
|
|
20717
20901
|
).addOption(jsonOption()).action(async (pattern, options) => {
|
|
20718
20902
|
await handleA11yQuery(pattern, options);
|
|
@@ -20774,12 +20958,39 @@ async function audit(checks, options) {
|
|
|
20774
20958
|
return { success: true, data: response.data };
|
|
20775
20959
|
}
|
|
20776
20960
|
|
|
20961
|
+
// src/commands/dom/helpers/evalResult.ts
|
|
20962
|
+
init_constants();
|
|
20963
|
+
init_strings();
|
|
20964
|
+
function boundEvalResult(value, length) {
|
|
20965
|
+
if (typeof value === "string") {
|
|
20966
|
+
const { text, truncatedFrom } = capLength(value, MAX_VALUE_LENGTH);
|
|
20967
|
+
return { result: text, ...truncatedFrom !== void 0 && { truncatedFrom } };
|
|
20968
|
+
}
|
|
20969
|
+
if (Array.isArray(value)) return boundArray(value, length ?? value.length);
|
|
20970
|
+
if (value === null || typeof value !== "object") return { result: value };
|
|
20971
|
+
const json = JSON.stringify(value);
|
|
20972
|
+
return json.length > MAX_VALUE_LENGTH ? jsonStart(json) : { result: value };
|
|
20973
|
+
}
|
|
20974
|
+
function boundArray(value, count) {
|
|
20975
|
+
const listed = value.slice(0, EVAL_JSON_ARRAY_LIMIT);
|
|
20976
|
+
const listedJson = JSON.stringify(listed);
|
|
20977
|
+
if (listedJson.length > MAX_VALUE_LENGTH) {
|
|
20978
|
+
const json = listed.length === value.length ? listedJson : JSON.stringify(value);
|
|
20979
|
+
return { ...jsonStart(json), count };
|
|
20980
|
+
}
|
|
20981
|
+
return listed.length < count ? { result: listed, count, omitted: count - listed.length } : { result: value };
|
|
20982
|
+
}
|
|
20983
|
+
function jsonStart(json) {
|
|
20984
|
+
return { result: capLength(json, MAX_VALUE_LENGTH).text, truncatedFrom: json.length };
|
|
20985
|
+
}
|
|
20986
|
+
|
|
20777
20987
|
// src/commands/dom/eval.ts
|
|
20778
20988
|
init_query();
|
|
20779
20989
|
init_messages();
|
|
20780
20990
|
init_client();
|
|
20781
20991
|
|
|
20782
20992
|
// src/ui/formatters/dom.ts
|
|
20993
|
+
init_constants();
|
|
20783
20994
|
init_formatting();
|
|
20784
20995
|
init_commands();
|
|
20785
20996
|
function formatDomQuery(data) {
|
|
@@ -20808,18 +21019,22 @@ function queryTagAttributes(node) {
|
|
|
20808
21019
|
...node.classes?.length ? [`class="${node.classes.join(" ")}"`] : []
|
|
20809
21020
|
];
|
|
20810
21021
|
}
|
|
20811
|
-
function formatDomGet(data) {
|
|
21022
|
+
function formatDomGet(data, options = {}) {
|
|
20812
21023
|
const { nodes } = data;
|
|
21024
|
+
const html = (node) => capForDisplay(node.outerHTML ?? "", MAX_VALUE_LENGTH, options.full);
|
|
20813
21025
|
if (nodes.length === 1) {
|
|
20814
|
-
return nodes[0]
|
|
21026
|
+
return html(nodes[0]);
|
|
20815
21027
|
}
|
|
20816
21028
|
const fmt = new OutputFormatter();
|
|
20817
21029
|
nodes.forEach((node, i) => {
|
|
20818
|
-
fmt.text(`[${i}] ${node
|
|
21030
|
+
fmt.text(`[${i}] ${html(node)}`);
|
|
20819
21031
|
});
|
|
20820
21032
|
return fmt.build();
|
|
20821
21033
|
}
|
|
20822
|
-
function formatDomEval(data) {
|
|
21034
|
+
function formatDomEval(data, options = {}) {
|
|
21035
|
+
return capForDisplay(evalResultText(data), MAX_VALUE_LENGTH, options.full);
|
|
21036
|
+
}
|
|
21037
|
+
function evalResultText(data) {
|
|
20823
21038
|
if (data.type === "undefined") return "undefined";
|
|
20824
21039
|
const { result } = data;
|
|
20825
21040
|
if (typeof result === "string" && (data.type !== "string" || !looksLikeOtherValue(result))) {
|
|
@@ -20876,7 +21091,7 @@ async function handleDomEval(script, options) {
|
|
|
20876
21091
|
errorContext: { suggestion: err.suggestion }
|
|
20877
21092
|
};
|
|
20878
21093
|
}
|
|
20879
|
-
const response = await domEval(script, options.frame);
|
|
21094
|
+
const response = await domEval(script, options.frame, options.full);
|
|
20880
21095
|
if (response.status === "error" || !response.data) {
|
|
20881
21096
|
const suggestion = await frameErrorSuggestion(response, options.frame);
|
|
20882
21097
|
return {
|
|
@@ -20886,7 +21101,7 @@ async function handleDomEval(script, options) {
|
|
|
20886
21101
|
...suggestion && { errorContext: { suggestion } }
|
|
20887
21102
|
};
|
|
20888
21103
|
}
|
|
20889
|
-
const { value, type, subtype, frame, warning } = response.data;
|
|
21104
|
+
const { value, type, subtype, length, frame, warning } = response.data;
|
|
20890
21105
|
const hint = [
|
|
20891
21106
|
...frame !== void 0 ? [evalFrameLine(frame)] : [],
|
|
20892
21107
|
...warning ? [warningMessage(warning)] : []
|
|
@@ -20894,7 +21109,7 @@ async function handleDomEval(script, options) {
|
|
|
20894
21109
|
return {
|
|
20895
21110
|
success: true,
|
|
20896
21111
|
data: {
|
|
20897
|
-
result: value,
|
|
21112
|
+
...options.json && !options.full ? boundEvalResult(value, length) : { result: value },
|
|
20898
21113
|
type,
|
|
20899
21114
|
...subtype && { subtype },
|
|
20900
21115
|
...frame !== void 0 && { frame },
|
|
@@ -20904,7 +21119,7 @@ async function handleDomEval(script, options) {
|
|
|
20904
21119
|
};
|
|
20905
21120
|
},
|
|
20906
21121
|
options,
|
|
20907
|
-
formatDomEval
|
|
21122
|
+
(data) => formatDomEval(data, { full: options.full })
|
|
20908
21123
|
);
|
|
20909
21124
|
}
|
|
20910
21125
|
async function frameErrorSuggestion(response, frame) {
|
|
@@ -21224,7 +21439,7 @@ function formatFormDiscovery(result) {
|
|
|
21224
21439
|
init_logging();
|
|
21225
21440
|
init_errors();
|
|
21226
21441
|
init_exitCodes();
|
|
21227
|
-
var
|
|
21442
|
+
var log28 = createLogger("dom");
|
|
21228
21443
|
function buildValidation(raw) {
|
|
21229
21444
|
const hasNativeError = !raw.isValid && !raw.valueMissing && raw.validationMessage;
|
|
21230
21445
|
const hasAriaError = raw.ariaInvalid;
|
|
@@ -21381,7 +21596,7 @@ async function cacheFormElements(forms, document2) {
|
|
|
21381
21596
|
const elements = forms.flatMap((form) => [...form.fields, ...form.buttons]);
|
|
21382
21597
|
const backendNodeIds = await resolveBackendNodeIds(elements.map((el) => el.selector)).catch(
|
|
21383
21598
|
(error) => {
|
|
21384
|
-
|
|
21599
|
+
log28.debug(`Form fields not cached by node: ${getErrorMessage(error)}`);
|
|
21385
21600
|
return [];
|
|
21386
21601
|
}
|
|
21387
21602
|
);
|
|
@@ -21397,7 +21612,7 @@ async function cacheFormElements(forms, document2) {
|
|
|
21397
21612
|
},
|
|
21398
21613
|
document2
|
|
21399
21614
|
);
|
|
21400
|
-
|
|
21615
|
+
log28.debug(`Cached ${elements.length} form elements`);
|
|
21401
21616
|
}
|
|
21402
21617
|
async function handleFormCommand(options) {
|
|
21403
21618
|
await runCommand(
|
|
@@ -21504,9 +21719,11 @@ async function withReadyState(frames) {
|
|
|
21504
21719
|
|
|
21505
21720
|
// src/commands/dom/get.ts
|
|
21506
21721
|
init_helpers();
|
|
21722
|
+
init_constants();
|
|
21507
21723
|
init_errors3();
|
|
21508
21724
|
init_messages();
|
|
21509
21725
|
init_exitCodes();
|
|
21726
|
+
init_strings();
|
|
21510
21727
|
var DOM_GET_DEFAULT_SELECTOR = "body";
|
|
21511
21728
|
async function targetNodeId(selectorOrIndex, index) {
|
|
21512
21729
|
const resolver = DomElementResolver.getInstance();
|
|
@@ -21541,12 +21758,21 @@ async function handleRawGet(selectorOrIndex, options) {
|
|
|
21541
21758
|
all: options.all,
|
|
21542
21759
|
nth: matchIndex(options)
|
|
21543
21760
|
});
|
|
21544
|
-
return { success: true, data: await getDOMElements(getOptions) };
|
|
21761
|
+
return { success: true, data: rawGetData(await getDOMElements(getOptions), options) };
|
|
21545
21762
|
},
|
|
21546
21763
|
options,
|
|
21547
|
-
formatDomGet
|
|
21764
|
+
(data) => formatDomGet(data, { full: options.full })
|
|
21548
21765
|
);
|
|
21549
21766
|
}
|
|
21767
|
+
function rawGetData(result, options) {
|
|
21768
|
+
if (!options.json || options.full) return result;
|
|
21769
|
+
return {
|
|
21770
|
+
nodes: result.nodes.map((node) => {
|
|
21771
|
+
const { text, truncatedFrom } = capLength(node.outerHTML ?? "", MAX_VALUE_LENGTH);
|
|
21772
|
+
return truncatedFrom === void 0 ? node : { ...node, outerHTML: text, truncatedFrom };
|
|
21773
|
+
})
|
|
21774
|
+
};
|
|
21775
|
+
}
|
|
21550
21776
|
async function handleSemanticGet(selectorOrIndex, options) {
|
|
21551
21777
|
await runCommand(
|
|
21552
21778
|
async () => {
|
|
@@ -21564,9 +21790,6 @@ function getOptionsConflict(selectorOrIndex, options) {
|
|
|
21564
21790
|
if (options.nodeId !== void 0 && selectorOrIndex !== void 0) {
|
|
21565
21791
|
return conflictingOptionsError("--node-id", "a selector or index");
|
|
21566
21792
|
}
|
|
21567
|
-
if (options.full && (options.raw || options.nodeId !== void 0)) {
|
|
21568
|
-
return conflictingOptionsError("--full", options.raw ? "--raw" : "--node-id");
|
|
21569
|
-
}
|
|
21570
21793
|
if (options.index !== void 0 && options.nth !== void 0) {
|
|
21571
21794
|
return conflictingOptionsError("--index", "--nth");
|
|
21572
21795
|
}
|
|
@@ -21588,9 +21811,9 @@ async function handleDomGet(selectorOrIndex, options) {
|
|
|
21588
21811
|
const { nodeId } = options;
|
|
21589
21812
|
if (nodeId !== void 0) {
|
|
21590
21813
|
await runCommand(
|
|
21591
|
-
async () => ({ success: true, data: await getDOMElements({ nodeId }) }),
|
|
21814
|
+
async () => ({ success: true, data: rawGetData(await getDOMElements({ nodeId }), options) }),
|
|
21592
21815
|
options,
|
|
21593
|
-
formatDomGet
|
|
21816
|
+
(data) => formatDomGet(data, { full: options.full })
|
|
21594
21817
|
);
|
|
21595
21818
|
return;
|
|
21596
21819
|
}
|
|
@@ -21876,11 +22099,11 @@ function treeRow(node) {
|
|
|
21876
22099
|
node.childCount !== void 0 && `(${node.childCount})`
|
|
21877
22100
|
].filter(Boolean).join(" ");
|
|
21878
22101
|
}
|
|
21879
|
-
function
|
|
22102
|
+
function treeLines(nodes, hidden, depth) {
|
|
21880
22103
|
const indent = " ".repeat(depth);
|
|
21881
22104
|
const lines = nodes.flatMap((node) => [
|
|
21882
22105
|
`${indent}${treeRow(node)}`,
|
|
21883
|
-
...
|
|
22106
|
+
...treeLines(node.children ?? [], node.hiddenChildren, depth + 1)
|
|
21884
22107
|
]);
|
|
21885
22108
|
return hidden ? [...lines, `${indent}(+${hidden} not rendered)`] : lines;
|
|
21886
22109
|
}
|
|
@@ -21956,7 +22179,7 @@ function treeBlock(data) {
|
|
|
21956
22179
|
if (!data.children && !data.hiddenChildren) return [];
|
|
21957
22180
|
return [
|
|
21958
22181
|
"tree",
|
|
21959
|
-
...
|
|
22182
|
+
...treeLines(data.children ?? [], data.hiddenChildren, 1),
|
|
21960
22183
|
...data.moreRows ? [` \u2026 +${data.moreRows} more`] : []
|
|
21961
22184
|
];
|
|
21962
22185
|
}
|
|
@@ -22077,6 +22300,7 @@ async function inspectTarget(selectorOrIndex, options) {
|
|
|
22077
22300
|
init_client();
|
|
22078
22301
|
|
|
22079
22302
|
// src/ui/formatters/layout.ts
|
|
22303
|
+
init_constants();
|
|
22080
22304
|
init_messages();
|
|
22081
22305
|
init_formatting();
|
|
22082
22306
|
init_commands();
|
|
@@ -22099,7 +22323,7 @@ function formatLayout(data) {
|
|
|
22099
22323
|
const more = data.elements.length - shown.length + (data.omitted ?? 0);
|
|
22100
22324
|
return new OutputFormatter().text(pageLayoutLine(data.page)).text(
|
|
22101
22325
|
data.indexSource ? indexLayoutHeadline(indexSourceText(data.indexSource)) : layoutHeadline(data.count, data.elements.length + (data.omitted ?? 0), data.selector)
|
|
22102
|
-
).list(shown.map((element) => layoutLine2(element, data.page.viewport))).list(more > 0 ? [moreMatchesNote(more,
|
|
22326
|
+
).list(shown.map((element) => layoutLine2(element, data.page.viewport))).list(more > 0 ? [moreMatchesNote(more, LAYOUT_ELEMENT_LIMIT)] : []).build();
|
|
22103
22327
|
}
|
|
22104
22328
|
|
|
22105
22329
|
// src/commands/dom/layout.ts
|
|
@@ -22326,10 +22550,11 @@ async function listElementListeners(selectorOrIndex, options) {
|
|
|
22326
22550
|
// src/commands/dom/query.ts
|
|
22327
22551
|
init_helpers();
|
|
22328
22552
|
init_query();
|
|
22553
|
+
init_constants();
|
|
22329
22554
|
init_exitCodes();
|
|
22330
22555
|
var QUERY_LIST_LIMIT = 50;
|
|
22331
22556
|
async function handleDomQuery(selector, options) {
|
|
22332
|
-
const limit = options.limit ?? (options.json ?
|
|
22557
|
+
const limit = options.limit ?? (options.json ? QUERY_JSON_LIST_LIMIT : QUERY_LIST_LIMIT);
|
|
22333
22558
|
await runCommand(
|
|
22334
22559
|
async () => {
|
|
22335
22560
|
const document2 = await pageDocumentId();
|
|
@@ -22373,7 +22598,7 @@ init_logging();
|
|
|
22373
22598
|
init_directories();
|
|
22374
22599
|
init_errors();
|
|
22375
22600
|
init_exitCodes();
|
|
22376
|
-
var
|
|
22601
|
+
var log29 = createLogger("dom");
|
|
22377
22602
|
var EXTENSION_FORMATS = {
|
|
22378
22603
|
".png": "png",
|
|
22379
22604
|
".jpg": "jpeg",
|
|
@@ -22499,8 +22724,8 @@ async function captureSequenceFrame(outputPath, options) {
|
|
|
22499
22724
|
}
|
|
22500
22725
|
async function handleSequenceCapture(outputDir, options) {
|
|
22501
22726
|
const fs19 = await import("fs");
|
|
22502
|
-
const
|
|
22503
|
-
const absoluteDir =
|
|
22727
|
+
const path8 = await import("path");
|
|
22728
|
+
const absoluteDir = path8.resolve(outputDir);
|
|
22504
22729
|
ensureDirectory(absoluteDir, fs19);
|
|
22505
22730
|
const intervalRule = positiveIntRule({ name: "--interval", min: 100, max: 6e4, default: 1e3 });
|
|
22506
22731
|
const limitRule = positiveIntRule({ name: "--limit", min: 1, max: 1e4, required: false });
|
|
@@ -22516,7 +22741,7 @@ async function handleSequenceCapture(outputDir, options) {
|
|
|
22516
22741
|
while (!stopping) {
|
|
22517
22742
|
frameCount++;
|
|
22518
22743
|
const filename = formatFrameFilename(frameCount, format);
|
|
22519
|
-
const outputPath =
|
|
22744
|
+
const outputPath = path8.join(absoluteDir, filename);
|
|
22520
22745
|
try {
|
|
22521
22746
|
await captureSequenceFrame(outputPath, options);
|
|
22522
22747
|
} catch (error) {
|
|
@@ -22525,7 +22750,7 @@ async function handleSequenceCapture(outputDir, options) {
|
|
|
22525
22750
|
if (options.json) {
|
|
22526
22751
|
console.log(JSON.stringify(buildSuccessResponse({ frame: frameCount, path: outputPath })));
|
|
22527
22752
|
} else {
|
|
22528
|
-
|
|
22753
|
+
log29.info(`Frame ${frameCount}: ${filename}`);
|
|
22529
22754
|
}
|
|
22530
22755
|
if (limit > 0 && frameCount >= limit) break;
|
|
22531
22756
|
await delay2(interval);
|
|
@@ -22647,6 +22872,8 @@ function formatWait(data) {
|
|
|
22647
22872
|
}
|
|
22648
22873
|
|
|
22649
22874
|
// src/commands/dom/index.ts
|
|
22875
|
+
init_constants();
|
|
22876
|
+
var EVAL_FULL_HELP = `Print the whole value (default: the first ${MAX_VALUE_LENGTH} characters; a string result in JSON too)`;
|
|
22650
22877
|
function registerDomCommands(program2) {
|
|
22651
22878
|
const dom = program2.command("dom").description("DOM inspection and manipulation").enablePositionalOptions();
|
|
22652
22879
|
registerA11yCommands(dom);
|
|
@@ -22658,7 +22885,7 @@ function registerDomCommands(program2) {
|
|
|
22658
22885
|
registerWaitCommand(dom);
|
|
22659
22886
|
dom.command("query").description("Find elements by CSS selector").argument("<selector>", 'CSS selector (e.g., ".error", "#app", "button")').option(
|
|
22660
22887
|
"--limit <n>",
|
|
22661
|
-
`Matches to list (default: ${QUERY_LIST_LIMIT}, ${
|
|
22888
|
+
`Matches to list (default: ${QUERY_LIST_LIMIT}, ${QUERY_JSON_LIST_LIMIT} with --json; 0 = all); the first ${QUERY_CACHE_LIMIT} (or more with a higher limit) are indexed`,
|
|
22662
22889
|
integerOption(0)
|
|
22663
22890
|
).option("-j, --json", "Output as JSON").addHelpText("after", SELECTOR_SCOPE_HELP).action(async (selector, options) => {
|
|
22664
22891
|
await handleDomQuery(selector, options);
|
|
@@ -22666,10 +22893,10 @@ function registerDomCommands(program2) {
|
|
|
22666
22893
|
dom.command("eval").description("Evaluate JavaScript expression in the page context").argument("<script>", 'JavaScript to execute (e.g., "document.title", "window.location.href")').option(
|
|
22667
22894
|
"--frame <frame>",
|
|
22668
22895
|
"Evaluate in an iframe, cross-origin ones included: index (from dom frames; 87 when stale), name/id attribute, or part of the name, id or URL"
|
|
22669
|
-
).option("-j, --json", "Output as JSON").action(async (script, options) => {
|
|
22896
|
+
).option("--full", EVAL_FULL_HELP).option("-j, --json", "Output as JSON").action(async (script, options) => {
|
|
22670
22897
|
await handleDomEval(script, options);
|
|
22671
22898
|
});
|
|
22672
|
-
program2.command("eval", { hidden: true }).description("Shortcut for: bdg dom eval").argument("<script>", "JavaScript to execute").option("--frame <frame>", "Evaluate in an iframe (see dom frames)").option("-j, --json", "Output as JSON").action(async (script, options) => {
|
|
22899
|
+
program2.command("eval", { hidden: true }).description("Shortcut for: bdg dom eval").argument("<script>", "JavaScript to execute").option("--frame <frame>", "Evaluate in an iframe (see dom frames)").option("--full", EVAL_FULL_HELP).option("-j, --json", "Output as JSON").action(async (script, options) => {
|
|
22673
22900
|
await handleDomEval(script, options);
|
|
22674
22901
|
});
|
|
22675
22902
|
dom.command("frames").description(
|
|
@@ -22680,7 +22907,10 @@ function registerDomCommands(program2) {
|
|
|
22680
22907
|
dom.command("get").description("Get semantic accessibility structure (default) or raw HTML (--raw)").argument(
|
|
22681
22908
|
"[selectorOrIndex]",
|
|
22682
22909
|
`${SELECTOR_OR_INDEX_ARGUMENT} (e.g. ".error", "#app", 0); default: ${DOM_GET_DEFAULT_SELECTOR}`
|
|
22683
|
-
).option("--raw", "Output raw HTML with all filtering options").option(
|
|
22910
|
+
).option("--raw", "Output raw HTML with all filtering options").option(
|
|
22911
|
+
"--full",
|
|
22912
|
+
`Show all of the element text (default: the first 500 characters); with --raw, all of the HTML (default: the first ${MAX_VALUE_LENGTH} characters)`
|
|
22913
|
+
).option("--all", "Get all matches (only with --raw)").option("--index <n>", "Element index if selector matches multiple (0-based)", integerOption(0)).addOption(new Option("--nth <n>", "Alias of --index").argParser(integerOption(0)).hideHelp()).option(
|
|
22684
22914
|
"--node-id <id>",
|
|
22685
22915
|
"Get the element with this node id (from dom query/get --raw or a11y describe; implies --raw)",
|
|
22686
22916
|
integerOption(1)
|
|
@@ -22703,8 +22933,8 @@ function registerDomCommands(program2) {
|
|
|
22703
22933
|
"Image format: png or jpeg/jpg (default: from the file extension, else png)",
|
|
22704
22934
|
screenshotFormatOption
|
|
22705
22935
|
).option("--quality <number>", "JPEG quality 0-100 (default: 90)", integerOption(0, 100)).option("--no-full-page", "Capture viewport only (default: full page)").option("--no-resize", "Disable auto-resize (full resolution)").option("--scroll <selector>", "Scroll element into view before capture").option("-f, --follow", "Continuous capture mode to directory").option("--interval <ms>", "Capture interval for --follow (default: 1000)").option("--limit <count>", "Max frames for --follow").option("-j, --json", "Output as JSON").action(
|
|
22706
|
-
async (
|
|
22707
|
-
await handleDomScreenshot(
|
|
22936
|
+
async (path8, target, options) => {
|
|
22937
|
+
await handleDomScreenshot(path8, target, options);
|
|
22708
22938
|
}
|
|
22709
22939
|
);
|
|
22710
22940
|
}
|
|
@@ -22712,7 +22942,7 @@ function registerDomCommands(program2) {
|
|
|
22712
22942
|
// src/commands/installSkill.ts
|
|
22713
22943
|
import { existsSync as existsSync11, mkdirSync as mkdirSync2, readFileSync as readFileSync8, renameSync as renameSync2, rmSync as rmSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
22714
22944
|
import { homedir as homedir3 } from "os";
|
|
22715
|
-
import { dirname as
|
|
22945
|
+
import { dirname as dirname7, join as join8 } from "path";
|
|
22716
22946
|
init_errors3();
|
|
22717
22947
|
init_messages();
|
|
22718
22948
|
|
|
@@ -22721,9 +22951,9 @@ init_formatting();
|
|
|
22721
22951
|
init_commands();
|
|
22722
22952
|
import { homedir as homedir2 } from "os";
|
|
22723
22953
|
var PATH_INDENT = " ".repeat(2 + 6 + 2 + 9 + 2);
|
|
22724
|
-
function tildePath(
|
|
22954
|
+
function tildePath(path8) {
|
|
22725
22955
|
const home = homedir2();
|
|
22726
|
-
return
|
|
22956
|
+
return path8.startsWith(`${home}/`) ? `~${path8.slice(home.length)}` : path8;
|
|
22727
22957
|
}
|
|
22728
22958
|
function addSkillTargets(fmt, skills) {
|
|
22729
22959
|
fmt.text("bdg skill:");
|
|
@@ -22747,11 +22977,11 @@ function formatInstalledSkills(data) {
|
|
|
22747
22977
|
init_logging();
|
|
22748
22978
|
init_errors();
|
|
22749
22979
|
init_exitCodes();
|
|
22750
|
-
var
|
|
22751
|
-
var SKILL_SOURCE_PATH =
|
|
22980
|
+
var log30 = createLogger("bdg");
|
|
22981
|
+
var SKILL_SOURCE_PATH = join8(PACKAGE_ROOT, ".claude", "skills", "bdg", "SKILL.md");
|
|
22752
22982
|
var SKILL_ROOTS = {
|
|
22753
|
-
claude:
|
|
22754
|
-
agents:
|
|
22983
|
+
claude: join8(".claude", "skills"),
|
|
22984
|
+
agents: join8(".agents", "skills")
|
|
22755
22985
|
};
|
|
22756
22986
|
function installSkill(targets, home = homedir3(), source = SKILL_SOURCE_PATH) {
|
|
22757
22987
|
if (!existsSync11(source)) {
|
|
@@ -22766,7 +22996,7 @@ function installSkill(targets, home = homedir3(), source = SKILL_SOURCE_PATH) {
|
|
|
22766
22996
|
const skills = [];
|
|
22767
22997
|
const failures = [];
|
|
22768
22998
|
for (const target of targets) {
|
|
22769
|
-
const result = writeSkill(target,
|
|
22999
|
+
const result = writeSkill(target, join8(home, SKILL_ROOTS[target], "bdg", "SKILL.md"), content);
|
|
22770
23000
|
if ("error" in result) failures.push(result);
|
|
22771
23001
|
else skills.push(result);
|
|
22772
23002
|
}
|
|
@@ -22775,10 +23005,10 @@ function installSkill(targets, home = homedir3(), source = SKILL_SOURCE_PATH) {
|
|
|
22775
23005
|
}
|
|
22776
23006
|
function skillFailureError(failures, targets) {
|
|
22777
23007
|
const other = failures.length === 1 ? targets.find((t) => t !== failures[0]?.target) : void 0;
|
|
22778
|
-
const errors = failures.map(({ path:
|
|
23008
|
+
const errors = failures.map(({ path: path8, error }) => {
|
|
22779
23009
|
const code = error.code;
|
|
22780
23010
|
return skillWriteFailedError(
|
|
22781
|
-
|
|
23011
|
+
path8,
|
|
22782
23012
|
getErrorMessage(error),
|
|
22783
23013
|
code === "ENOTDIR" || code === "EEXIST",
|
|
22784
23014
|
other && `--${other}`
|
|
@@ -22790,30 +23020,30 @@ function skillFailureError(failures, targets) {
|
|
|
22790
23020
|
EXIT_CODES.PERMISSION_DENIED
|
|
22791
23021
|
);
|
|
22792
23022
|
}
|
|
22793
|
-
function writeSkill(target,
|
|
22794
|
-
const existing = existsSync11(
|
|
23023
|
+
function writeSkill(target, path8, content) {
|
|
23024
|
+
const existing = existsSync11(path8) ? readFileSync8(path8, "utf-8") : void 0;
|
|
22795
23025
|
if (existing === content) {
|
|
22796
|
-
return { target, path:
|
|
23026
|
+
return { target, path: path8, status: "unchanged" };
|
|
22797
23027
|
}
|
|
22798
|
-
const backup = existing === void 0 ? void 0 : `${
|
|
22799
|
-
const temporary = `${
|
|
22800
|
-
let writing =
|
|
23028
|
+
const backup = existing === void 0 ? void 0 : `${path8}.bak`;
|
|
23029
|
+
const temporary = `${path8}.tmp`;
|
|
23030
|
+
let writing = path8;
|
|
22801
23031
|
try {
|
|
22802
|
-
mkdirSync2(
|
|
23032
|
+
mkdirSync2(dirname7(path8), { recursive: true });
|
|
22803
23033
|
writeFileSync2(temporary, content);
|
|
22804
23034
|
if (backup !== void 0) {
|
|
22805
23035
|
writing = backup;
|
|
22806
23036
|
rmSync6(backup, { force: true });
|
|
22807
23037
|
writeFileSync2(backup, existing ?? "");
|
|
22808
23038
|
}
|
|
22809
|
-
writing =
|
|
22810
|
-
renameSync2(temporary,
|
|
23039
|
+
writing = path8;
|
|
23040
|
+
renameSync2(temporary, path8);
|
|
22811
23041
|
} catch (error) {
|
|
22812
|
-
safeRemoveFile(temporary, "temporary skill copy",
|
|
23042
|
+
safeRemoveFile(temporary, "temporary skill copy", log30);
|
|
22813
23043
|
return { target, path: writing, error };
|
|
22814
23044
|
}
|
|
22815
|
-
if (backup === void 0) return { target, path:
|
|
22816
|
-
return { target, path:
|
|
23045
|
+
if (backup === void 0) return { target, path: path8, status: "installed" };
|
|
23046
|
+
return { target, path: path8, status: "updated", backup };
|
|
22817
23047
|
}
|
|
22818
23048
|
function selectedTargets(options) {
|
|
22819
23049
|
const picked = Object.keys(SKILL_ROOTS).filter((target) => options[target]);
|
|
@@ -22844,17 +23074,127 @@ function registerInstallSkillCommand(program2) {
|
|
|
22844
23074
|
|
|
22845
23075
|
// src/commands/network/har.ts
|
|
22846
23076
|
import * as fs16 from "fs";
|
|
22847
|
-
import * as
|
|
23077
|
+
import * as path7 from "path";
|
|
22848
23078
|
init_outputFile();
|
|
22849
23079
|
init_client();
|
|
22850
23080
|
init_paths();
|
|
22851
23081
|
|
|
22852
23082
|
// src/telemetry/filterDsl.ts
|
|
22853
23083
|
init_constants();
|
|
23084
|
+
|
|
23085
|
+
// src/telemetry/filters.ts
|
|
23086
|
+
init_url();
|
|
23087
|
+
var DEFAULT_EXCLUDED_DOMAINS = [
|
|
23088
|
+
"analytics.google.com",
|
|
23089
|
+
"googletagmanager.com",
|
|
23090
|
+
"googleadservices.com",
|
|
23091
|
+
"doubleclick.net",
|
|
23092
|
+
"google-analytics.com",
|
|
23093
|
+
"clarity.ms",
|
|
23094
|
+
"bat.bing.com",
|
|
23095
|
+
"facebook.com",
|
|
23096
|
+
"connect.facebook.net",
|
|
23097
|
+
"tiktok.com",
|
|
23098
|
+
"linkedin.com",
|
|
23099
|
+
"twitter.com",
|
|
23100
|
+
"snapchat.com",
|
|
23101
|
+
"mixpanel.com",
|
|
23102
|
+
"segment.com",
|
|
23103
|
+
"segment.io",
|
|
23104
|
+
"amplitude.com",
|
|
23105
|
+
"heap.io",
|
|
23106
|
+
"fullstory.com",
|
|
23107
|
+
"hotjar.com",
|
|
23108
|
+
"logrocket.com",
|
|
23109
|
+
"smartlook.com",
|
|
23110
|
+
"exactag.com",
|
|
23111
|
+
"criteo.com",
|
|
23112
|
+
"adroll.com",
|
|
23113
|
+
"outbrain.com",
|
|
23114
|
+
"taboola.com",
|
|
23115
|
+
"confirmit.com",
|
|
23116
|
+
"newrelic.com",
|
|
23117
|
+
"datadoghq.com",
|
|
23118
|
+
"sentry.io",
|
|
23119
|
+
"googlesyndication.com",
|
|
23120
|
+
"googletagservices.com",
|
|
23121
|
+
"adservice.google.com",
|
|
23122
|
+
"amazon-adsystem.com",
|
|
23123
|
+
"adnxs.com",
|
|
23124
|
+
"rubiconproject.com",
|
|
23125
|
+
"pubmatic.com",
|
|
23126
|
+
"casalemedia.com",
|
|
23127
|
+
"indexww.com",
|
|
23128
|
+
"openx.net",
|
|
23129
|
+
"3lift.com",
|
|
23130
|
+
"teads.tv",
|
|
23131
|
+
"bidswitch.net",
|
|
23132
|
+
"sharethrough.com",
|
|
23133
|
+
"adsafeprotected.com",
|
|
23134
|
+
"moatads.com",
|
|
23135
|
+
"doubleverify.com",
|
|
23136
|
+
"scorecardresearch.com",
|
|
23137
|
+
"imrworldwide.com",
|
|
23138
|
+
"chartbeat.com",
|
|
23139
|
+
"chartbeat.net",
|
|
23140
|
+
"permutive.com",
|
|
23141
|
+
"permutive.app",
|
|
23142
|
+
"optimizely.com",
|
|
23143
|
+
"quantserve.com",
|
|
23144
|
+
"quantcount.com",
|
|
23145
|
+
"krxd.net",
|
|
23146
|
+
"demdex.net",
|
|
23147
|
+
"omtrdc.net",
|
|
23148
|
+
"everesttech.net",
|
|
23149
|
+
"xiti.com",
|
|
23150
|
+
"ati-host.net"
|
|
23151
|
+
];
|
|
23152
|
+
var EXCLUDED_DOMAINS_SET = new Set(DEFAULT_EXCLUDED_DOMAINS.map((d) => d.toLowerCase()));
|
|
23153
|
+
var DEFAULT_SKIP_BODY_MIME_TYPES = [
|
|
23154
|
+
"text/css",
|
|
23155
|
+
"image/svg+xml",
|
|
23156
|
+
"image/png",
|
|
23157
|
+
"image/jpeg",
|
|
23158
|
+
"image/gif",
|
|
23159
|
+
"image/webp",
|
|
23160
|
+
"image/avif",
|
|
23161
|
+
"image/bmp",
|
|
23162
|
+
"image/tiff",
|
|
23163
|
+
"font/woff",
|
|
23164
|
+
"font/woff2",
|
|
23165
|
+
"font/ttf",
|
|
23166
|
+
"font/eot",
|
|
23167
|
+
"font/otf",
|
|
23168
|
+
"application/font-woff",
|
|
23169
|
+
"application/font-woff2",
|
|
23170
|
+
"video/mp4",
|
|
23171
|
+
"video/webm",
|
|
23172
|
+
"audio/mpeg",
|
|
23173
|
+
"audio/wav"
|
|
23174
|
+
];
|
|
23175
|
+
var SKIP_BODY_MIME_TYPES_SET = new Set(DEFAULT_SKIP_BODY_MIME_TYPES.map((m) => m.toLowerCase()));
|
|
23176
|
+
function matchesWildcard(str, pattern) {
|
|
23177
|
+
const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
23178
|
+
const regex = new RegExp(`^${regexPattern}$`, "i");
|
|
23179
|
+
return regex.test(str);
|
|
23180
|
+
}
|
|
23181
|
+
function filterByResourceType(requests, types) {
|
|
23182
|
+
if (types.length === 0) {
|
|
23183
|
+
return requests;
|
|
23184
|
+
}
|
|
23185
|
+
return requests.filter((req) => {
|
|
23186
|
+
if (!req.resourceType) {
|
|
23187
|
+
return false;
|
|
23188
|
+
}
|
|
23189
|
+
return types.includes(req.resourceType);
|
|
23190
|
+
});
|
|
23191
|
+
}
|
|
23192
|
+
|
|
23193
|
+
// src/telemetry/filterDsl.ts
|
|
22854
23194
|
init_logging();
|
|
22855
23195
|
init_suggestions();
|
|
22856
23196
|
init_url();
|
|
22857
|
-
var
|
|
23197
|
+
var log31 = createLogger("network");
|
|
22858
23198
|
var SIZE_UNITS = {
|
|
22859
23199
|
b: 1,
|
|
22860
23200
|
kb: 1024,
|
|
@@ -22964,7 +23304,7 @@ function validateSizeValue(value) {
|
|
|
22964
23304
|
parseSize(value);
|
|
22965
23305
|
return null;
|
|
22966
23306
|
} catch (error) {
|
|
22967
|
-
|
|
23307
|
+
log31.debug(
|
|
22968
23308
|
`Size parse failed for "${value}": ${error instanceof Error ? error.message : String(error)}`
|
|
22969
23309
|
);
|
|
22970
23310
|
return createError(
|
|
@@ -23150,11 +23490,162 @@ Multiple filters:
|
|
|
23150
23490
|
|
|
23151
23491
|
// src/telemetry/har/builder.ts
|
|
23152
23492
|
import { createRequire as createRequire2 } from "node:module";
|
|
23493
|
+
|
|
23494
|
+
// src/telemetry/har/sanitizeBody.ts
|
|
23495
|
+
init_elementInfo();
|
|
23496
|
+
init_logging();
|
|
23497
|
+
init_errors();
|
|
23498
|
+
var log32 = createLogger("network");
|
|
23499
|
+
var REDACTED = "[redacted]";
|
|
23500
|
+
var SENSITIVE_FIELD = new RegExp(
|
|
23501
|
+
`${SENSITIVE_NAME_SOURCE}|token|secret|api[-_]?key|authoriz|credential|jwt|private[-_]?key|access[-_]?key|session|signature`,
|
|
23502
|
+
"i"
|
|
23503
|
+
);
|
|
23504
|
+
var FORM_BODY = /^[^\s=&{["]+=[^\s&]*(?:&[^\s=&]+=[^\s&]*)*$/;
|
|
23505
|
+
function isSensitiveField2(name) {
|
|
23506
|
+
return SENSITIVE_FIELD.test(name);
|
|
23507
|
+
}
|
|
23508
|
+
function redactPairs(text, isSensitive, replacement) {
|
|
23509
|
+
return text.split("&").map((pair) => {
|
|
23510
|
+
const eq = pair.indexOf("=");
|
|
23511
|
+
if (eq === -1 || !isSensitive(decodeName(pair.slice(0, eq)))) return pair;
|
|
23512
|
+
return `${pair.slice(0, eq)}=${replacement}`;
|
|
23513
|
+
}).join("&");
|
|
23514
|
+
}
|
|
23515
|
+
function redactRequestBody(text, mimeType) {
|
|
23516
|
+
if (/multipart\/form-data/i.test(mimeType)) return redactMultipart(text, mimeType);
|
|
23517
|
+
if (/x-www-form-urlencoded/i.test(mimeType) || FORM_BODY.test(text)) {
|
|
23518
|
+
return redactPairs(text, isSensitiveField2, REDACTED);
|
|
23519
|
+
}
|
|
23520
|
+
return redactJsonBody(text);
|
|
23521
|
+
}
|
|
23522
|
+
function decodeName(name) {
|
|
23523
|
+
try {
|
|
23524
|
+
return decodeURIComponent(name.replace(/\+/g, " "));
|
|
23525
|
+
} catch (error) {
|
|
23526
|
+
log32.debug(`Field name not decodable: ${getErrorMessage(error)}`);
|
|
23527
|
+
return name;
|
|
23528
|
+
}
|
|
23529
|
+
}
|
|
23530
|
+
function redactMultipart(text, mimeType) {
|
|
23531
|
+
const match = /boundary=(?:"([^"]+)"|([^;\s]+))/i.exec(mimeType);
|
|
23532
|
+
const boundary = match?.[1] ?? match?.[2];
|
|
23533
|
+
if (!boundary) return text;
|
|
23534
|
+
const delimiter = `--${boundary}`;
|
|
23535
|
+
return text.split(delimiter).map(redactPart).join(delimiter);
|
|
23536
|
+
}
|
|
23537
|
+
function redactPart(part) {
|
|
23538
|
+
const headerEnd = part.indexOf("\r\n\r\n");
|
|
23539
|
+
if (headerEnd === -1) return part;
|
|
23540
|
+
const name = /;\s*name="([^"]*)"/i.exec(part.slice(0, headerEnd))?.[1];
|
|
23541
|
+
if (name === void 0 || !isSensitiveField2(name)) return part;
|
|
23542
|
+
const valueEnd = part.endsWith("\r\n") ? part.length - 2 : part.length;
|
|
23543
|
+
return `${part.slice(0, headerEnd + 4)}${REDACTED}${part.slice(valueEnd)}`;
|
|
23544
|
+
}
|
|
23545
|
+
function redactJsonBody(text) {
|
|
23546
|
+
if (!/^\s*[[{]/.test(text)) return text;
|
|
23547
|
+
let parsed;
|
|
23548
|
+
try {
|
|
23549
|
+
parsed = JSON.parse(text);
|
|
23550
|
+
} catch (error) {
|
|
23551
|
+
log32.debug(`Request body not JSON: ${getErrorMessage(error)}`);
|
|
23552
|
+
return error instanceof SyntaxError ? text : REDACTED;
|
|
23553
|
+
}
|
|
23554
|
+
try {
|
|
23555
|
+
const hits = { count: 0 };
|
|
23556
|
+
const redacted = redactJson(parsed, false, hits);
|
|
23557
|
+
return hits.count > 0 ? JSON.stringify(redacted) : text;
|
|
23558
|
+
} catch (error) {
|
|
23559
|
+
log32.debug(`Request body redacted whole: ${getErrorMessage(error)}`);
|
|
23560
|
+
return REDACTED;
|
|
23561
|
+
}
|
|
23562
|
+
}
|
|
23563
|
+
function redactJson(value, sensitive, hits) {
|
|
23564
|
+
if (Array.isArray(value)) return value.map((item) => redactJson(item, sensitive, hits));
|
|
23565
|
+
if (value !== null && typeof value === "object") {
|
|
23566
|
+
return Object.fromEntries(
|
|
23567
|
+
Object.entries(value).map(([key, field]) => [
|
|
23568
|
+
key,
|
|
23569
|
+
redactJson(field, sensitive || isSensitiveField2(key), hits)
|
|
23570
|
+
])
|
|
23571
|
+
);
|
|
23572
|
+
}
|
|
23573
|
+
if (!sensitive || !["string", "number", "boolean"].includes(typeof value)) return value;
|
|
23574
|
+
hits.count++;
|
|
23575
|
+
return REDACTED;
|
|
23576
|
+
}
|
|
23577
|
+
|
|
23578
|
+
// src/telemetry/har/sanitize.ts
|
|
23579
|
+
var URL_REDACTED = encodeURIComponent(REDACTED);
|
|
23580
|
+
var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
|
|
23581
|
+
"authorization",
|
|
23582
|
+
"proxy-authorization",
|
|
23583
|
+
"authentication",
|
|
23584
|
+
"cookie",
|
|
23585
|
+
"set-cookie"
|
|
23586
|
+
]);
|
|
23587
|
+
var SENSITIVE_CUSTOM_HEADER = /^x-.*(key|token|secret|auth)/i;
|
|
23588
|
+
var SENSITIVE_HEADER_SEGMENT = /(^|-)(api-?key|apikey|token|secret|jwt|subscription-key|session(-?id)?)(-|$)/i;
|
|
23589
|
+
var URL_HEADERS = /* @__PURE__ */ new Set(["location", "referer"]);
|
|
23590
|
+
var SENSITIVE_URL_PARAM = /^(code|sig|key)$/i;
|
|
23591
|
+
function sanitizeEntry(entry) {
|
|
23592
|
+
const { request, response } = entry;
|
|
23593
|
+
const postData = request.postData;
|
|
23594
|
+
return {
|
|
23595
|
+
...entry,
|
|
23596
|
+
request: {
|
|
23597
|
+
...request,
|
|
23598
|
+
url: redactUrl(request.url),
|
|
23599
|
+
cookies: request.cookies.map(redactCookie),
|
|
23600
|
+
headers: request.headers.map(redactHeader),
|
|
23601
|
+
queryString: request.queryString.map(redactQueryParam),
|
|
23602
|
+
...postData?.text !== void 0 && {
|
|
23603
|
+
postData: { ...postData, text: redactRequestBody(postData.text, postData.mimeType) }
|
|
23604
|
+
}
|
|
23605
|
+
},
|
|
23606
|
+
response: {
|
|
23607
|
+
...response,
|
|
23608
|
+
cookies: response.cookies.map(redactCookie),
|
|
23609
|
+
headers: response.headers.map(redactHeader),
|
|
23610
|
+
redirectURL: redactUrl(response.redirectURL)
|
|
23611
|
+
}
|
|
23612
|
+
};
|
|
23613
|
+
}
|
|
23614
|
+
function isSensitiveHeader(name) {
|
|
23615
|
+
return SENSITIVE_HEADERS.has(name.toLowerCase()) || SENSITIVE_CUSTOM_HEADER.test(name) || SENSITIVE_HEADER_SEGMENT.test(name);
|
|
23616
|
+
}
|
|
23617
|
+
function redactHeader(header) {
|
|
23618
|
+
if (isSensitiveHeader(header.name)) return { ...header, value: REDACTED };
|
|
23619
|
+
if (!URL_HEADERS.has(header.name.toLowerCase())) return header;
|
|
23620
|
+
const value = redactUrl(header.value);
|
|
23621
|
+
return value === header.value ? header : { ...header, value };
|
|
23622
|
+
}
|
|
23623
|
+
function redactCookie(cookie) {
|
|
23624
|
+
return { ...cookie, value: REDACTED };
|
|
23625
|
+
}
|
|
23626
|
+
function isSensitiveParam(name) {
|
|
23627
|
+
return isSensitiveField2(name) || SENSITIVE_URL_PARAM.test(name);
|
|
23628
|
+
}
|
|
23629
|
+
function redactQueryParam(param) {
|
|
23630
|
+
return isSensitiveParam(param.name) ? { ...param, value: REDACTED } : param;
|
|
23631
|
+
}
|
|
23632
|
+
function redactUrl(url) {
|
|
23633
|
+
const hash = url.indexOf("#");
|
|
23634
|
+
const beforeHash = hash === -1 ? url : url.slice(0, hash);
|
|
23635
|
+
const fragment = hash === -1 ? "" : `#${redactPairs(url.slice(hash + 1), isSensitiveParam, URL_REDACTED)}`;
|
|
23636
|
+
const question = beforeHash.indexOf("?");
|
|
23637
|
+
if (question === -1) return beforeHash + fragment;
|
|
23638
|
+
const query = redactPairs(beforeHash.slice(question + 1), isSensitiveParam, URL_REDACTED);
|
|
23639
|
+
return `${beforeHash.slice(0, question)}?${query}${fragment}`;
|
|
23640
|
+
}
|
|
23641
|
+
|
|
23642
|
+
// src/telemetry/har/builder.ts
|
|
23153
23643
|
var requireBuiltin = createRequire2(import.meta.url);
|
|
23154
23644
|
var UNKNOWN_TIMING = -1;
|
|
23155
23645
|
var DEFAULT_HTTP_VERSION = "HTTP/1.1";
|
|
23156
|
-
function buildHAR(requests, metadata) {
|
|
23157
|
-
const
|
|
23646
|
+
function buildHAR(requests, metadata, options = {}) {
|
|
23647
|
+
const built = [...requests].sort((a, b) => a.timestamp - b.timestamp).map(buildEntry);
|
|
23648
|
+
const entries = options.includeSensitive ? built : built.map(sanitizeEntry);
|
|
23158
23649
|
const log36 = {
|
|
23159
23650
|
version: "1.2",
|
|
23160
23651
|
creator: {
|
|
@@ -23162,7 +23653,8 @@ function buildHAR(requests, metadata) {
|
|
|
23162
23653
|
version: metadata.version,
|
|
23163
23654
|
comment: "Browser Debugger CLI - https://github.com/szymdzum/browser-debugger-cli"
|
|
23164
23655
|
},
|
|
23165
|
-
entries
|
|
23656
|
+
entries,
|
|
23657
|
+
...!options.includeSensitive && { comment: harSanitizedComment() }
|
|
23166
23658
|
};
|
|
23167
23659
|
if (metadata.chromeVersion) {
|
|
23168
23660
|
log36.browser = {
|
|
@@ -23381,8 +23873,8 @@ function extractRedirectURL(req) {
|
|
|
23381
23873
|
}
|
|
23382
23874
|
function estimateRequestHeadersSize(method, url, headers) {
|
|
23383
23875
|
const parsedUrl = new URL(url);
|
|
23384
|
-
const
|
|
23385
|
-
const requestLine = `${method} ${
|
|
23876
|
+
const path8 = parsedUrl.pathname + parsedUrl.search;
|
|
23877
|
+
const requestLine = `${method} ${path8} ${DEFAULT_HTTP_VERSION}\r
|
|
23386
23878
|
`;
|
|
23387
23879
|
const headersString = headers ? Object.entries(headers).map(([name, value]) => `${name}: ${value}\r
|
|
23388
23880
|
`).join("") : "";
|
|
@@ -23505,6 +23997,11 @@ function formatSessionStatus(metadata, pid, activity, pageState, verbose = false
|
|
|
23505
23997
|
if (activity.lastNetworkRequestAt) {
|
|
23506
23998
|
fmt.keyValue(" Last Request", formatTimeAgo(activity.lastNetworkRequestAt), 18);
|
|
23507
23999
|
}
|
|
24000
|
+
const evictedNote = networkEvictedNote({
|
|
24001
|
+
requestsDropped: activity.networkRequestsDropped ?? 0,
|
|
24002
|
+
bodiesEvicted: activity.networkBodiesEvicted ?? 0
|
|
24003
|
+
});
|
|
24004
|
+
if (evictedNote) fmt.text(` ${evictedNote}`);
|
|
23508
24005
|
fmt.keyValue("Console Messages", `${activity.consoleMessagesCaptured} captured`, 18);
|
|
23509
24006
|
if (activity.lastConsoleMessageAt) {
|
|
23510
24007
|
fmt.keyValue(" Last Message", formatTimeAgo(activity.lastConsoleMessageAt), 18);
|
|
@@ -23614,6 +24111,7 @@ init_constants();
|
|
|
23614
24111
|
init_formatting();
|
|
23615
24112
|
init_commands();
|
|
23616
24113
|
init_sessionCommand();
|
|
24114
|
+
init_strings();
|
|
23617
24115
|
function inferResourceTypeFromMime(mimeType) {
|
|
23618
24116
|
if (!mimeType) return void 0;
|
|
23619
24117
|
return MIME_TYPE_RULES.find((rule) => rule.match.test(mimeType))?.type;
|
|
@@ -23645,17 +24143,25 @@ function buildPreviewJsonData(output, options) {
|
|
|
23645
24143
|
...output.totals && { totals: output.totals },
|
|
23646
24144
|
...output.pageCrashedAt !== void 0 && { pageCrashedAt: output.pageCrashedAt },
|
|
23647
24145
|
...pick("network") && output.data.network && { network: last(output.data.network) },
|
|
23648
|
-
...pick("console") && output.data.console && {
|
|
24146
|
+
...pick("console") && output.data.console && {
|
|
24147
|
+
console: last(output.data.console)?.map((message) => capMessageText(message, options.full))
|
|
24148
|
+
}
|
|
23649
24149
|
};
|
|
23650
24150
|
}
|
|
23651
24151
|
function formatPreviewAsJson(output, options) {
|
|
23652
24152
|
const envelope = buildSuccessResponse(buildPreviewJsonData(output, options));
|
|
23653
|
-
return options.follow ? JSON.stringify(envelope) :
|
|
24153
|
+
return options.follow ? JSON.stringify(envelope) : stringifyEnvelope(envelope);
|
|
23654
24154
|
}
|
|
23655
24155
|
function formatPreviewHumanReadable(output, options) {
|
|
23656
24156
|
const body = options.verbose ? formatPreviewVerbose(output, options) : formatPreviewCompact(output, options);
|
|
23657
24157
|
return withPageCrashedNote(body, output.pageCrashedAt);
|
|
23658
24158
|
}
|
|
24159
|
+
function compactConsoleText(text, full) {
|
|
24160
|
+
if (full) return text;
|
|
24161
|
+
const capped = capLength(text, MAX_CONSOLE_TEXT_LENGTH);
|
|
24162
|
+
const shown = truncateText(capped.text, 2);
|
|
24163
|
+
return capped.truncatedFrom === void 0 ? shown : `${shown}${moreCharsNote(capped.truncatedFrom - capped.text.length)}`;
|
|
24164
|
+
}
|
|
23659
24165
|
function formatPreviewCompact(output, options) {
|
|
23660
24166
|
const fmt = new OutputFormatter();
|
|
23661
24167
|
fmt.text(
|
|
@@ -23675,6 +24181,8 @@ function formatPreviewCompact(output, options) {
|
|
|
23675
24181
|
const totalCount = output.totals?.network ?? output.data.network.length;
|
|
23676
24182
|
const limitHint = formatLimitHint(showingCount, totalCount);
|
|
23677
24183
|
fmt.text(`NETWORK (${showingCount}/${totalCount})${limitHint}:`);
|
|
24184
|
+
const evictedNote = previewEvictedNote(output);
|
|
24185
|
+
if (evictedNote) fmt.text(` ${evictedNote}`);
|
|
23678
24186
|
if (requests.length === 0) {
|
|
23679
24187
|
if (options.filteredTypes && options.filteredTypes.length > 0 && options.unfilteredNetworkCount && options.unfilteredNetworkCount > 0) {
|
|
23680
24188
|
const typesStr = options.filteredTypes.join(", ");
|
|
@@ -23711,8 +24219,7 @@ function formatPreviewCompact(output, options) {
|
|
|
23711
24219
|
} else {
|
|
23712
24220
|
const consoleLines = messages.map((msg) => {
|
|
23713
24221
|
const prefix = msg.type.toUpperCase().padEnd(5);
|
|
23714
|
-
|
|
23715
|
-
return `${prefix} ${text}`;
|
|
24222
|
+
return `${prefix} ${compactConsoleText(msg.text, options.full)}`;
|
|
23716
24223
|
});
|
|
23717
24224
|
fmt.list(consoleLines, 2);
|
|
23718
24225
|
}
|
|
@@ -23724,6 +24231,12 @@ function formatPreviewCompact(output, options) {
|
|
|
23724
24231
|
}
|
|
23725
24232
|
return fmt.build();
|
|
23726
24233
|
}
|
|
24234
|
+
function previewEvictedNote(output) {
|
|
24235
|
+
return networkEvictedNote({
|
|
24236
|
+
requestsDropped: output.totals?.networkDropped ?? 0,
|
|
24237
|
+
bodiesEvicted: output.totals?.networkBodiesEvicted ?? 0
|
|
24238
|
+
});
|
|
24239
|
+
}
|
|
23727
24240
|
function formatPreviewVerbose(output, options) {
|
|
23728
24241
|
const fmt = new OutputFormatter();
|
|
23729
24242
|
fmt.text(PREVIEW_HEADERS.LIVE_PREVIEW).separator("\u2501", 50);
|
|
@@ -23747,6 +24260,8 @@ function formatPreviewVerbose(output, options) {
|
|
|
23747
24260
|
const requests = lastCount === 0 ? output.data.network : output.data.network.slice(-lastCount);
|
|
23748
24261
|
const title = lastCount === 0 ? `Network Requests (all ${requests.length})` : `Network Requests (last ${requests.length} of ${output.totals?.network ?? output.data.network.length})`;
|
|
23749
24262
|
fmt.text(title).separator("\u2501", 50);
|
|
24263
|
+
const evictedNote = previewEvictedNote(output);
|
|
24264
|
+
if (evictedNote) fmt.text(evictedNote);
|
|
23750
24265
|
if (requests.length === 0) {
|
|
23751
24266
|
if (options.filteredTypes && options.filteredTypes.length > 0 && options.unfilteredNetworkCount && options.unfilteredNetworkCount > 0) {
|
|
23752
24267
|
const typesStr = options.filteredTypes.join(", ");
|
|
@@ -23791,7 +24306,9 @@ function formatPreviewVerbose(output, options) {
|
|
|
23791
24306
|
} else {
|
|
23792
24307
|
messages.forEach((msg) => {
|
|
23793
24308
|
const icon = msg.type === "error" ? "ERR" : msg.type === "warning" ? "WARN" : "INFO";
|
|
23794
|
-
fmt.text(
|
|
24309
|
+
fmt.text(
|
|
24310
|
+
`${icon} [${msg.type}] ${capForDisplay(msg.text, MAX_CONSOLE_TEXT_LENGTH, options.full)}`
|
|
24311
|
+
);
|
|
23795
24312
|
});
|
|
23796
24313
|
}
|
|
23797
24314
|
fmt.blank();
|
|
@@ -23930,10 +24447,10 @@ function generateHARFilename() {
|
|
|
23930
24447
|
const minutes = String(now.getMinutes()).padStart(2, "0");
|
|
23931
24448
|
const seconds = String(now.getSeconds()).padStart(2, "0");
|
|
23932
24449
|
const base = `capture-${year}-${month}-${day}-${hours}${minutes}${seconds}`;
|
|
23933
|
-
const sessionDir =
|
|
23934
|
-
let candidate =
|
|
24450
|
+
const sessionDir = path7.dirname(getSessionFilePath("OUTPUT"));
|
|
24451
|
+
let candidate = path7.join(sessionDir, `${base}.har`);
|
|
23935
24452
|
for (let n = 2; fs16.existsSync(candidate); n++) {
|
|
23936
|
-
candidate =
|
|
24453
|
+
candidate = path7.join(sessionDir, `${base}-${n}.har`);
|
|
23937
24454
|
}
|
|
23938
24455
|
return candidate;
|
|
23939
24456
|
}
|
|
@@ -23949,16 +24466,20 @@ async function getChromeVersion() {
|
|
|
23949
24466
|
}
|
|
23950
24467
|
function formatHARExport(data) {
|
|
23951
24468
|
if ("log" in data) return JSON.stringify(data, null, 2);
|
|
23952
|
-
|
|
23953
|
-
return `\u2713 Exported ${data.entries} requests${filterNote} to ${data.file}`;
|
|
24469
|
+
return harExportedMessage(data);
|
|
23954
24470
|
}
|
|
24471
|
+
var HAR_FILE_MODE = 384;
|
|
23955
24472
|
var STDOUT_PATH = "-";
|
|
23956
24473
|
var filterDslOption = new Option(
|
|
23957
24474
|
"--filter <dsl>",
|
|
23958
24475
|
'Filter requests using DevTools DSL (e.g., "status-code:>=400")'
|
|
23959
24476
|
);
|
|
24477
|
+
var includeSensitiveOption = new Option(
|
|
24478
|
+
"--include-sensitive",
|
|
24479
|
+
"Keep credentials (auth/cookie/API key headers, cookie values, URL tokens, password and token body fields); redacted by default"
|
|
24480
|
+
);
|
|
23960
24481
|
function registerHarCommand(networkCmd) {
|
|
23961
|
-
networkCmd.command("har [output-file]").addHelpText("after", "\nUse - as the output file to write the HAR to stdout.").description("Export network data as HAR 1.2 format").addOption(jsonOption()).addOption(filterDslOption).action(async (outputFile, options) => {
|
|
24482
|
+
networkCmd.command("har [output-file]").addHelpText("after", "\nUse - as the output file to write the HAR to stdout.").description("Export network data as HAR 1.2 format").addOption(jsonOption()).addOption(filterDslOption).addOption(includeSensitiveOption).action(async (outputFile, options) => {
|
|
23962
24483
|
await runCommand(
|
|
23963
24484
|
async () => {
|
|
23964
24485
|
let requests = await getNetworkRequests();
|
|
@@ -23973,20 +24494,26 @@ function registerHarCommand(networkCmd) {
|
|
|
23973
24494
|
const outputPath = outputFile ?? generateHARFilename();
|
|
23974
24495
|
if (outputPath !== STDOUT_PATH) assertFilePath(outputPath, ".har");
|
|
23975
24496
|
const chromeVersion = await getChromeVersion();
|
|
23976
|
-
const
|
|
23977
|
-
|
|
23978
|
-
|
|
23979
|
-
|
|
24497
|
+
const includeSensitive = options.includeSensitive === true;
|
|
24498
|
+
const har = buildHAR(
|
|
24499
|
+
requests,
|
|
24500
|
+
{ version: VERSION, ...chromeVersion && { chromeVersion } },
|
|
24501
|
+
{ includeSensitive }
|
|
24502
|
+
);
|
|
23980
24503
|
if (outputPath === STDOUT_PATH) return { success: true, data: har };
|
|
23981
|
-
const file = await writeOutputFile(
|
|
23982
|
-
|
|
23983
|
-
|
|
23984
|
-
|
|
23985
|
-
|
|
23986
|
-
|
|
23987
|
-
|
|
23988
|
-
|
|
24504
|
+
const file = await writeOutputFile(
|
|
24505
|
+
outputPath,
|
|
24506
|
+
JSON.stringify(har, null, 2),
|
|
24507
|
+
".har",
|
|
24508
|
+
HAR_FILE_MODE
|
|
24509
|
+
);
|
|
24510
|
+
const data = {
|
|
24511
|
+
file,
|
|
24512
|
+
entries: har.log.entries.length,
|
|
24513
|
+
filtered,
|
|
24514
|
+
sanitized: !includeSensitive
|
|
23989
24515
|
};
|
|
24516
|
+
return { success: true, data };
|
|
23990
24517
|
},
|
|
23991
24518
|
options,
|
|
23992
24519
|
formatHARExport
|
|
@@ -24153,6 +24680,8 @@ function formatNetworkListHuman(requests, options) {
|
|
|
24153
24680
|
const header = buildHeader(requests.length, options.filteredCount ?? totalCount, totalCount);
|
|
24154
24681
|
fmt.text(header);
|
|
24155
24682
|
fmt.separator("\u2500", SEPARATOR_WIDTH2);
|
|
24683
|
+
const evictedNote = options.evictions && networkEvictedNote(options.evictions);
|
|
24684
|
+
if (evictedNote) fmt.text(evictedNote);
|
|
24156
24685
|
if (requests.length === 0) {
|
|
24157
24686
|
fmt.text("No matching requests found.");
|
|
24158
24687
|
return fmt.build();
|
|
@@ -24169,6 +24698,8 @@ var FOLLOW_ID_WIDTH = 14;
|
|
|
24169
24698
|
function formatNetworkFollowRows(requests, options = {}) {
|
|
24170
24699
|
const fmt = new OutputFormatter();
|
|
24171
24700
|
const widths = columnWidths(requests, FOLLOW_ID_WIDTH);
|
|
24701
|
+
const evictedNote = options.evictions && networkEvictedNote(options.evictions);
|
|
24702
|
+
if (evictedNote) fmt.text(evictedNote);
|
|
24172
24703
|
if (options.header) {
|
|
24173
24704
|
fmt.text(formatColumnHeader(widths));
|
|
24174
24705
|
fmt.separator("\u2500", SEPARATOR_WIDTH2);
|
|
@@ -24236,12 +24767,35 @@ function buildFormatOptions2(options, result, lastLimit) {
|
|
|
24236
24767
|
last: lastLimit,
|
|
24237
24768
|
totalCount: result.totalCount,
|
|
24238
24769
|
filteredCount: result.filteredCount,
|
|
24239
|
-
...result.pageStart && { pageStart: result.pageStart }
|
|
24770
|
+
...result.pageStart && { pageStart: result.pageStart },
|
|
24771
|
+
evictions: {
|
|
24772
|
+
requestsDropped: result.dropped ?? 0,
|
|
24773
|
+
bodiesEvicted: result.bodiesEvicted ?? 0
|
|
24774
|
+
}
|
|
24775
|
+
};
|
|
24776
|
+
}
|
|
24777
|
+
function newEvictionKinds() {
|
|
24778
|
+
let requestsNoted = false;
|
|
24779
|
+
let bodiesNoted = false;
|
|
24780
|
+
return (counts) => {
|
|
24781
|
+
const newRequests = !requestsNoted && counts.requestsDropped > 0;
|
|
24782
|
+
const newBodies = !bodiesNoted && counts.bodiesEvicted > 0;
|
|
24783
|
+
requestsNoted ||= newRequests;
|
|
24784
|
+
bodiesNoted ||= newBodies;
|
|
24785
|
+
return newRequests || newBodies ? counts : void 0;
|
|
24786
|
+
};
|
|
24787
|
+
}
|
|
24788
|
+
function evictionFields(counts) {
|
|
24789
|
+
if (!counts) return {};
|
|
24790
|
+
return {
|
|
24791
|
+
...counts.requestsDropped > 0 && { dropped: counts.requestsDropped },
|
|
24792
|
+
...counts.bodiesEvicted > 0 && { bodiesEvicted: counts.bodiesEvicted }
|
|
24240
24793
|
};
|
|
24241
24794
|
}
|
|
24242
24795
|
async function runFollowMode2(options, resourceTypes, lastN) {
|
|
24243
24796
|
const shown = /* @__PURE__ */ new Set();
|
|
24244
24797
|
const newCrash = newPageCrashes();
|
|
24798
|
+
const newEviction = newEvictionKinds();
|
|
24245
24799
|
let started = false;
|
|
24246
24800
|
const showNetwork = async () => {
|
|
24247
24801
|
const result = await fetchNetworkRequests(filtersNeedHeaders(options));
|
|
@@ -24251,6 +24805,7 @@ async function runFollowMode2(options, resourceTypes, lastN) {
|
|
|
24251
24805
|
noteFollowConnected();
|
|
24252
24806
|
const { requests } = result.data;
|
|
24253
24807
|
const crashedAt = newCrash(result.data.pageCrashedAt);
|
|
24808
|
+
const evictions = newEviction(result.data.evictions);
|
|
24254
24809
|
const finished = filterRequests(requests, options, resourceTypes).filter(
|
|
24255
24810
|
(request) => request.duration !== void 0 && !shown.has(request.requestId)
|
|
24256
24811
|
);
|
|
@@ -24259,12 +24814,13 @@ async function runFollowMode2(options, resourceTypes, lastN) {
|
|
|
24259
24814
|
finished.forEach((request) => shown.add(request.requestId));
|
|
24260
24815
|
const fresh = started || lastN === 0 ? finished : finished.slice(-lastN);
|
|
24261
24816
|
if (options.json) {
|
|
24262
|
-
if (!started || fresh.length > 0 || crashedAt !== void 0) {
|
|
24817
|
+
if (!started || fresh.length > 0 || crashedAt !== void 0 || evictions) {
|
|
24263
24818
|
const data = {
|
|
24264
24819
|
requests: fresh,
|
|
24265
24820
|
totalCount: requests.length,
|
|
24266
24821
|
filteredCount: fresh.length,
|
|
24267
|
-
...crashedAt !== void 0 && { pageCrashedAt: crashedAt }
|
|
24822
|
+
...crashedAt !== void 0 && { pageCrashedAt: crashedAt },
|
|
24823
|
+
...evictionFields(evictions)
|
|
24268
24824
|
};
|
|
24269
24825
|
console.log(JSON.stringify(buildSuccessResponse(data)));
|
|
24270
24826
|
}
|
|
@@ -24273,7 +24829,8 @@ async function runFollowMode2(options, resourceTypes, lastN) {
|
|
|
24273
24829
|
const text = formatNetworkFollowRows(fresh, {
|
|
24274
24830
|
header: !started,
|
|
24275
24831
|
verbose: options.verbose ?? false,
|
|
24276
|
-
...pageStart && { pageStart }
|
|
24832
|
+
...pageStart && { pageStart },
|
|
24833
|
+
...evictions && { evictions }
|
|
24277
24834
|
});
|
|
24278
24835
|
if (text) console.log(text);
|
|
24279
24836
|
if (crashedAt !== void 0) console.log(pageCrashedNote(crashedAt));
|
|
@@ -24349,7 +24906,7 @@ ${formatPresetHelp()}`
|
|
|
24349
24906
|
}
|
|
24350
24907
|
return createErrorResult(result.error, result.exitCode, result.suggestion);
|
|
24351
24908
|
}
|
|
24352
|
-
const { requests, pageCrashedAt } = result.data;
|
|
24909
|
+
const { requests, pageCrashedAt, evictions } = result.data;
|
|
24353
24910
|
const filtered = filterRequests(requests, options, resourceTypes);
|
|
24354
24911
|
const pageStart = pageStartOf(requests);
|
|
24355
24912
|
return {
|
|
@@ -24359,7 +24916,8 @@ ${formatPresetHelp()}`
|
|
|
24359
24916
|
totalCount: requests.length,
|
|
24360
24917
|
filteredCount: filtered.length,
|
|
24361
24918
|
...pageStart && { pageStart },
|
|
24362
|
-
...pageCrashedAt !== void 0 && { pageCrashedAt }
|
|
24919
|
+
...pageCrashedAt !== void 0 && { pageCrashedAt },
|
|
24920
|
+
...evictionFields(evictions)
|
|
24363
24921
|
}
|
|
24364
24922
|
};
|
|
24365
24923
|
},
|
|
@@ -24548,6 +25106,7 @@ function registerPageCommands(program2) {
|
|
|
24548
25106
|
}
|
|
24549
25107
|
|
|
24550
25108
|
// src/commands/peek.ts
|
|
25109
|
+
init_constants();
|
|
24551
25110
|
init_errors3();
|
|
24552
25111
|
init_messages();
|
|
24553
25112
|
init_exitCodes();
|
|
@@ -24645,7 +25204,8 @@ function previewDisplayOptions(options, lastN) {
|
|
|
24645
25204
|
console: options.console,
|
|
24646
25205
|
last: lastN,
|
|
24647
25206
|
verbose: options.verbose,
|
|
24648
|
-
follow: options.follow
|
|
25207
|
+
follow: options.follow,
|
|
25208
|
+
full: options.full
|
|
24649
25209
|
};
|
|
24650
25210
|
}
|
|
24651
25211
|
async function followPreview(options) {
|
|
@@ -24658,6 +25218,10 @@ function registerPeekCommand(program2) {
|
|
|
24658
25218
|
program2.command("peek").description("Preview collected data without stopping the session").addOption(jsonOption()).option("-v, --verbose", "Use verbose output with full URLs and formatting", false).option("-n, --network", "Show only network requests", false).option("-c, --console", "Show only console messages", false).option("-f, --follow", "Watch for updates (like tail -f)", false).option("--interval <ms>", "Refresh interval of --follow in ms, 100-60000 (default: 1000)").option("--last <count>", "Show last N items, 0 for all", "10").option(
|
|
24659
25219
|
"--type <types>",
|
|
24660
25220
|
"Filter network requests by resource type (comma-separated: Document,XHR,Fetch,etc.)"
|
|
25221
|
+
).option(
|
|
25222
|
+
"--full",
|
|
25223
|
+
`Print console message texts whole (default: the first ${MAX_CONSOLE_TEXT_LENGTH} characters, ${MAX_CONSOLE_JSON_TEXT_LENGTH} in JSON)`,
|
|
25224
|
+
false
|
|
24661
25225
|
).action(async (options) => {
|
|
24662
25226
|
showBothSectionsWhenBothRequested(options);
|
|
24663
25227
|
if (options.network && !options.json) {
|
|
@@ -24785,6 +25349,10 @@ async function describeSession(entry) {
|
|
|
24785
25349
|
const socketPath = sessionFilePathIn(dir, "DAEMON_SOCKET");
|
|
24786
25350
|
const probe = await probeDaemonSocket(socketPath);
|
|
24787
25351
|
if (probe !== "alive") return describeWithoutSocket(entry, probe);
|
|
25352
|
+
const untrusted = secureSessionDir(dir);
|
|
25353
|
+
if (untrusted) {
|
|
25354
|
+
return { name, state: "untrusted", untrusted: `${untrusted.dir}: ${untrusted.reason}` };
|
|
25355
|
+
}
|
|
24788
25356
|
try {
|
|
24789
25357
|
const response = await getStatus(socketPath);
|
|
24790
25358
|
if (response.status === "ok" && response.data) return toRunningSession(name, response.data);
|
|
@@ -24900,6 +25468,15 @@ function formatSessionList(data) {
|
|
|
24900
25468
|
if (ended.length > 0) {
|
|
24901
25469
|
fmt.blank().section("Ended without bdg stop:", ended);
|
|
24902
25470
|
}
|
|
25471
|
+
const untrusted = data.sessions.flatMap(
|
|
25472
|
+
({ name, untrusted: why }) => why ? [untrustedSessionText(name ?? DEFAULT_SESSION_LABEL, why)] : []
|
|
25473
|
+
);
|
|
25474
|
+
if (untrusted.length > 0) {
|
|
25475
|
+
fmt.blank().section(
|
|
25476
|
+
"Directory not safe to use (not asked; bdg status --session <name> says how to fix it):",
|
|
25477
|
+
untrusted
|
|
25478
|
+
);
|
|
25479
|
+
}
|
|
24903
25480
|
const cleanups = data.sessions.flatMap((session) => session.cleanup ? [session.cleanup] : []);
|
|
24904
25481
|
if (cleanups.length > 0) {
|
|
24905
25482
|
fmt.hints("Crashed or stale sessions; clean up with:", cleanups);
|
|
@@ -25017,6 +25594,7 @@ function registerStatusCommand(program2) {
|
|
|
25017
25594
|
}
|
|
25018
25595
|
return { success: true, data: withSessionName(jsonOutput) };
|
|
25019
25596
|
} catch (error) {
|
|
25597
|
+
if (error instanceof CommandError) throw error;
|
|
25020
25598
|
const errorMessage = getErrorMessage(error);
|
|
25021
25599
|
if (error instanceof IPCTimeoutError) {
|
|
25022
25600
|
const err = sessionNotRespondingError(error.timeoutMs / 1e3);
|
|
@@ -25065,6 +25643,7 @@ function registerStatusCommand(program2) {
|
|
|
25065
25643
|
}
|
|
25066
25644
|
|
|
25067
25645
|
// src/commands/stop.ts
|
|
25646
|
+
init_errors3();
|
|
25068
25647
|
init_client();
|
|
25069
25648
|
init_transport();
|
|
25070
25649
|
init_formatting();
|
|
@@ -25135,7 +25714,7 @@ function registerStopCommand(program2) {
|
|
|
25135
25714
|
};
|
|
25136
25715
|
}
|
|
25137
25716
|
} catch (error) {
|
|
25138
|
-
if (error instanceof IPCTimeoutError) throw error;
|
|
25717
|
+
if (error instanceof IPCTimeoutError || error instanceof CommandError) throw error;
|
|
25139
25718
|
const errorMessage = getErrorMessage(error);
|
|
25140
25719
|
if (isDaemonNotRunningError(errorMessage)) {
|
|
25141
25720
|
return {
|
|
@@ -25206,18 +25785,38 @@ var commandRegistry = [
|
|
|
25206
25785
|
registerInstallSkillCommand
|
|
25207
25786
|
];
|
|
25208
25787
|
|
|
25788
|
+
// src/program.ts
|
|
25789
|
+
var CLI_NAME = "bdg";
|
|
25790
|
+
var CLI_DESCRIPTION = "Browser telemetry via Chrome DevTools Protocol";
|
|
25791
|
+
var SESSION_OPTION_FLAGS = "--session <name>";
|
|
25792
|
+
var SESSION_OPTION_DESCRIPTION = "Use a named session (own daemon, Chrome and port) instead of the default one; env: BDG_SESSION";
|
|
25793
|
+
function addGlobalOptions(command) {
|
|
25794
|
+
for (const sub of command.commands) {
|
|
25795
|
+
if (!sub.options.some((option) => option.long === "--debug")) {
|
|
25796
|
+
sub.addOption(new Option("--debug", "Enable debug logging").hideHelp());
|
|
25797
|
+
}
|
|
25798
|
+
if (!sub.options.some((option) => option.long === "--quiet")) {
|
|
25799
|
+
sub.addOption(new Option("-q, --quiet", "Hide tips and hints").hideHelp());
|
|
25800
|
+
}
|
|
25801
|
+
if (!sub.options.some((option) => option.long === "--session")) {
|
|
25802
|
+
sub.addOption(new Option(SESSION_OPTION_FLAGS, SESSION_OPTION_DESCRIPTION).hideHelp());
|
|
25803
|
+
}
|
|
25804
|
+
addGlobalOptions(sub);
|
|
25805
|
+
}
|
|
25806
|
+
}
|
|
25807
|
+
function buildProgram(output = {}) {
|
|
25808
|
+
const program2 = new Command().name(CLI_NAME).description(CLI_DESCRIPTION).version(VERSION).option("--debug", "Enable debug logging (verbose output)").option(SESSION_OPTION_FLAGS, SESSION_OPTION_DESCRIPTION).enablePositionalOptions().exitOverride().configureOutput(output);
|
|
25809
|
+
commandRegistry.forEach((register) => register(program2));
|
|
25810
|
+
addGlobalOptions(program2);
|
|
25811
|
+
return program2;
|
|
25812
|
+
}
|
|
25813
|
+
|
|
25209
25814
|
// src/index.ts
|
|
25210
|
-
init_errors3();
|
|
25211
|
-
init_messages();
|
|
25212
25815
|
init_formatting();
|
|
25213
25816
|
init_logging();
|
|
25214
25817
|
init_errors();
|
|
25215
25818
|
init_exitCodes();
|
|
25216
|
-
var CLI_NAME = "bdg";
|
|
25217
|
-
var CLI_DESCRIPTION = "Browser telemetry via Chrome DevTools Protocol";
|
|
25218
|
-
var SESSION_OPTION_FLAGS = "--session <name>";
|
|
25219
25819
|
var FULL_HELP_FLAG = "--full";
|
|
25220
|
-
var SESSION_OPTION_DESCRIPTION = "Use a named session (own daemon, Chrome and port) instead of the default one; env: BDG_SESSION";
|
|
25221
25820
|
function flagArgs() {
|
|
25222
25821
|
const args = process.argv.slice(2);
|
|
25223
25822
|
const end = args.indexOf("--");
|
|
@@ -25258,14 +25857,12 @@ async function main() {
|
|
|
25258
25857
|
configureStdio();
|
|
25259
25858
|
const helpTopic = rewriteHelpCommand();
|
|
25260
25859
|
const jsonMode = isJsonMode();
|
|
25261
|
-
const program2 =
|
|
25860
|
+
const program2 = buildProgram({
|
|
25262
25861
|
writeOut: (text) => {
|
|
25263
25862
|
if (!jsonMode) process.stdout.write(text);
|
|
25264
25863
|
},
|
|
25265
25864
|
outputError: () => void 0
|
|
25266
25865
|
});
|
|
25267
|
-
commandRegistry.forEach((register) => register(program2));
|
|
25268
|
-
addGlobalOptions(program2);
|
|
25269
25866
|
program2.hook("preAction", (_root, actionCommand) => applyGlobalOptions(program2, actionCommand));
|
|
25270
25867
|
assertKnownHelpTopic(program2, helpTopic);
|
|
25271
25868
|
assertNotMistypedCommand(program2, process.argv);
|
|
@@ -25288,20 +25885,6 @@ function helpJson(program2) {
|
|
|
25288
25885
|
if (command !== program2) return generateCommandHelp(program2, command);
|
|
25289
25886
|
return flagArgs().includes(FULL_HELP_FLAG) ? generateMachineReadableHelp(program2) : generateCompactHelp(program2);
|
|
25290
25887
|
}
|
|
25291
|
-
function addGlobalOptions(command) {
|
|
25292
|
-
for (const sub of command.commands) {
|
|
25293
|
-
if (!sub.options.some((option) => option.long === "--debug")) {
|
|
25294
|
-
sub.addOption(new Option("--debug", "Enable debug logging").hideHelp());
|
|
25295
|
-
}
|
|
25296
|
-
if (!sub.options.some((option) => option.long === "--quiet")) {
|
|
25297
|
-
sub.addOption(new Option("-q, --quiet", "Hide tips and hints").hideHelp());
|
|
25298
|
-
}
|
|
25299
|
-
if (!sub.options.some((option) => option.long === "--session")) {
|
|
25300
|
-
sub.addOption(new Option(SESSION_OPTION_FLAGS, SESSION_OPTION_DESCRIPTION).hideHelp());
|
|
25301
|
-
}
|
|
25302
|
-
addGlobalOptions(sub);
|
|
25303
|
-
}
|
|
25304
|
-
}
|
|
25305
25888
|
function sessionFromArgv(argv) {
|
|
25306
25889
|
let value;
|
|
25307
25890
|
argv.forEach((arg, i) => {
|
|
@@ -25329,7 +25912,7 @@ function applyGlobalOptions(program2, actionCommand) {
|
|
|
25329
25912
|
}
|
|
25330
25913
|
function handleUsageError(error, command, jsonMode) {
|
|
25331
25914
|
if (error.code === "commander.version") {
|
|
25332
|
-
if (jsonMode) console.log(
|
|
25915
|
+
if (jsonMode) console.log(stringifyEnvelope(buildSuccessResponse({ version: VERSION })));
|
|
25333
25916
|
process.exit(EXIT_CODES.SUCCESS);
|
|
25334
25917
|
}
|
|
25335
25918
|
if (error.code === "commander.helpDisplayed" || error.code === "commander.help" && error.exitCode === 0) {
|
|
@@ -25338,9 +25921,7 @@ function handleUsageError(error, command, jsonMode) {
|
|
|
25338
25921
|
const exitCode = EXIT_CODES.INVALID_ARGUMENTS;
|
|
25339
25922
|
const { message, suggestion } = usageErrorDetails(error, command);
|
|
25340
25923
|
if (jsonMode) {
|
|
25341
|
-
console.log(
|
|
25342
|
-
JSON.stringify(OutputBuilder.buildJsonError(message, { exitCode, suggestion }), null, 2)
|
|
25343
|
-
);
|
|
25924
|
+
console.log(stringifyEnvelope(OutputBuilder.buildJsonError(message, { exitCode, suggestion })));
|
|
25344
25925
|
} else if (error.code !== "commander.help") {
|
|
25345
25926
|
console.error(genericError(message, suggestion));
|
|
25346
25927
|
}
|
|
@@ -25352,7 +25933,7 @@ function handleFatalError(error) {
|
|
|
25352
25933
|
const metadata = error instanceof CommandError ? error.metadata : {};
|
|
25353
25934
|
if (isJsonMode()) {
|
|
25354
25935
|
console.log(
|
|
25355
|
-
|
|
25936
|
+
stringifyEnvelope(OutputBuilder.buildJsonError(message, { ...metadata, exitCode }))
|
|
25356
25937
|
);
|
|
25357
25938
|
} else {
|
|
25358
25939
|
console.error(genericError(message));
|