browser-debugger-cli 0.14.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 +1 -1
- package/dist/commands/cdp.js +1 -0
- package/dist/commands/cleanup.js +3 -0
- package/dist/commands/dom/eval.d.ts +2 -1
- package/dist/commands/dom/eval.js +6 -21
- package/dist/commands/dom/helpers/evalResult.d.ts +36 -0
- package/dist/commands/dom/helpers/evalResult.js +59 -0
- package/dist/commands/helpJson.d.ts +1 -1
- package/dist/commands/helpJson.js +3 -3
- package/dist/commands/helpTopic.js +10 -4
- package/dist/commands/network/har.js +18 -14
- package/dist/commands/optionBehaviors.js +8 -3
- package/dist/commands/shared/optionTypes.d.ts +1 -0
- package/dist/commands/shared/outputFile.d.ts +2 -1
- package/dist/commands/shared/outputFile.js +7 -4
- package/dist/commands/status.js +3 -1
- package/dist/commands/stop.js +2 -1
- 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 +2 -4
- package/dist/constants.js +2 -4
- package/dist/daemon/launcher.d.ts +17 -3
- package/dist/daemon/launcher.js +37 -7
- package/dist/daemon/session/commandRegistry.js +2 -2
- package/dist/daemon.js +8107 -7962
- package/dist/errors/messages.d.ts +23 -0
- package/dist/errors/messages.js +86 -6
- package/dist/index.js +555 -166
- package/dist/ipc/client.d.ts +6 -1
- package/dist/ipc/client.js +11 -2
- package/dist/ipc/protocol/commands.d.ts +4 -0
- package/dist/ipc/transport/index.d.ts +6 -0
- package/dist/ipc/transport/index.js +16 -1
- package/dist/runtime/dom/elementInfo.d.ts +7 -0
- package/dist/runtime/dom/elementInfo.js +8 -1
- package/dist/runtime/dom/evalHelpers.d.ts +24 -4
- package/dist/runtime/dom/evalHelpers.js +40 -12
- package/dist/runtime/dom/frames.d.ts +2 -1
- package/dist/runtime/dom/frames.js +3 -1
- 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/har/builder.d.ts +12 -1
- package/dist/telemetry/har/builder.js +10 -2
- 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/ui/formatters/sessions.d.ts +3 -2
- package/dist/ui/formatters/sessions.js +10 -3
- package/dist/ui/messages/chrome.d.ts +14 -6
- package/dist/ui/messages/chrome.js +52 -12
- package/dist/ui/messages/networkMessages.d.ts +26 -0
- package/dist/ui/messages/networkMessages.js +21 -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/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -960,7 +960,7 @@ var require_node = __commonJS({
|
|
|
960
960
|
var tty2 = __require("tty");
|
|
961
961
|
var util = __require("util");
|
|
962
962
|
exports.init = init;
|
|
963
|
-
exports.log =
|
|
963
|
+
exports.log = log36;
|
|
964
964
|
exports.formatArgs = formatArgs;
|
|
965
965
|
exports.save = save;
|
|
966
966
|
exports.load = load;
|
|
@@ -1095,7 +1095,7 @@ var require_node = __commonJS({
|
|
|
1095
1095
|
}
|
|
1096
1096
|
return (/* @__PURE__ */ new Date()).toISOString() + " ";
|
|
1097
1097
|
}
|
|
1098
|
-
function
|
|
1098
|
+
function log36(...args) {
|
|
1099
1099
|
return process.stderr.write(util.formatWithOptions(exports.inspectOpts, ...args) + "\n");
|
|
1100
1100
|
}
|
|
1101
1101
|
function save(namespaces) {
|
|
@@ -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,7 +1327,7 @@ function resourceTypeFromName(name) {
|
|
|
1327
1327
|
([type, abbreviation]) => type.toLowerCase() === wanted || abbreviation.toLowerCase() === wanted
|
|
1328
1328
|
)?.[0];
|
|
1329
1329
|
}
|
|
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, LAYOUT_ELEMENT_LIMIT, MAX_TRIGGERED_REQUESTS, 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";
|
|
@@ -1343,6 +1343,7 @@ var init_constants = __esm({
|
|
|
1343
1343
|
CHROME_NETWORK_BUFFER_PER_RESOURCE = 10 * 1024 * 1024;
|
|
1344
1344
|
CHROME_POST_DATA_LIMIT = 1 * 1024 * 1024;
|
|
1345
1345
|
QUERY_JSON_LIST_LIMIT = 100;
|
|
1346
|
+
EVAL_JSON_ARRAY_LIMIT = 100;
|
|
1346
1347
|
LAYOUT_ELEMENT_LIMIT = 100;
|
|
1347
1348
|
MAX_TRIGGERED_REQUESTS = 50;
|
|
1348
1349
|
MAX_JSONL_BUFFER_SIZE = 256 * 1024 * 1024;
|
|
@@ -1421,8 +1422,8 @@ function createLogger(context) {
|
|
|
1421
1422
|
logger.debug = (message) => logMessage(message, "debug");
|
|
1422
1423
|
return logger;
|
|
1423
1424
|
}
|
|
1424
|
-
function logDebugError(
|
|
1425
|
-
|
|
1425
|
+
function logDebugError(log36, context, error) {
|
|
1426
|
+
log36.debug(`Failed to ${context}: ${getErrorMessage(error)}`);
|
|
1426
1427
|
}
|
|
1427
1428
|
var debugEnabled;
|
|
1428
1429
|
var init_logger = __esm({
|
|
@@ -1489,11 +1490,30 @@ function makeDirectory(dir, mode) {
|
|
|
1489
1490
|
}
|
|
1490
1491
|
fs2.mkdirSync(dir, { recursive: true, ...mode !== void 0 && { mode } });
|
|
1491
1492
|
}
|
|
1492
|
-
|
|
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;
|
|
1493
1510
|
var init_directories = __esm({
|
|
1494
1511
|
"src/utils/directories.ts"() {
|
|
1495
1512
|
"use strict";
|
|
1496
1513
|
PSEUDO_FILESYSTEM = /^\/(proc|sys)(\/|$)/;
|
|
1514
|
+
GROUP_OTHER_WRITE = 18;
|
|
1515
|
+
OTHER_WRITE = 2;
|
|
1516
|
+
STICKY = 512;
|
|
1497
1517
|
}
|
|
1498
1518
|
});
|
|
1499
1519
|
|
|
@@ -1502,12 +1522,16 @@ import * as fs3 from "fs";
|
|
|
1502
1522
|
import * as os2 from "os";
|
|
1503
1523
|
import * as path3 from "path";
|
|
1504
1524
|
function getSessionBaseDir() {
|
|
1505
|
-
const override =
|
|
1506
|
-
if (override
|
|
1525
|
+
const override = sessionDirOverride();
|
|
1526
|
+
if (override !== null) {
|
|
1507
1527
|
return path3.isAbsolute(override) ? override : path3.resolve(override);
|
|
1508
1528
|
}
|
|
1509
1529
|
return path3.join(os2.homedir(), ".bdg");
|
|
1510
1530
|
}
|
|
1531
|
+
function sessionDirOverride() {
|
|
1532
|
+
const override = process.env[SESSION_DIR_OVERRIDE_ENV];
|
|
1533
|
+
return override && override.trim().length > 0 ? override : null;
|
|
1534
|
+
}
|
|
1511
1535
|
function getSessionName() {
|
|
1512
1536
|
const name = process.env[SESSION_NAME_ENV]?.trim();
|
|
1513
1537
|
if (!name) return null;
|
|
@@ -1543,14 +1567,81 @@ function getDaemonSocketPath() {
|
|
|
1543
1567
|
return getSessionFilePath("DAEMON_SOCKET");
|
|
1544
1568
|
}
|
|
1545
1569
|
function ensureSessionDir() {
|
|
1546
|
-
makeDirectory(getSessionDir());
|
|
1570
|
+
makeDirectory(getSessionDir(), PRIVATE_DIR_MODE);
|
|
1547
1571
|
}
|
|
1548
|
-
|
|
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
|
+
};
|
|
1611
|
+
}
|
|
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;
|
|
1549
1639
|
var init_paths = __esm({
|
|
1550
1640
|
"src/session/paths.ts"() {
|
|
1551
1641
|
"use strict";
|
|
1552
1642
|
init_logging();
|
|
1553
1643
|
init_directories();
|
|
1644
|
+
init_errors();
|
|
1554
1645
|
log = createLogger("session");
|
|
1555
1646
|
SESSION_FILES = {
|
|
1556
1647
|
OUTPUT: "session.json",
|
|
@@ -1572,6 +1663,9 @@ var init_paths = __esm({
|
|
|
1572
1663
|
"CHROME_PID",
|
|
1573
1664
|
"METADATA"
|
|
1574
1665
|
];
|
|
1666
|
+
PRIVATE_DIR_MODE = 448;
|
|
1667
|
+
GROUP_OTHER_ACCESS = 63;
|
|
1668
|
+
SESSION_DIR_TRUST = { allowGroupWrite: true };
|
|
1575
1669
|
}
|
|
1576
1670
|
});
|
|
1577
1671
|
|
|
@@ -1653,14 +1747,17 @@ var init_atomicFile = __esm({
|
|
|
1653
1747
|
*
|
|
1654
1748
|
* @param filePath - Target file path
|
|
1655
1749
|
* @param data - Data to write
|
|
1656
|
-
* @param options - Write options
|
|
1750
|
+
* @param options - Write options (`mode`: permissions of the new file, less the umask)
|
|
1657
1751
|
* @returns Promise that resolves when write completes
|
|
1658
1752
|
* @throws Error if write operation fails
|
|
1659
1753
|
*/
|
|
1660
1754
|
static async writeAsync(filePath, data, options = {}) {
|
|
1661
1755
|
const tmpPath = this.getTempPath(filePath);
|
|
1662
1756
|
try {
|
|
1663
|
-
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
|
+
});
|
|
1664
1761
|
await fs4.promises.rename(tmpPath, filePath);
|
|
1665
1762
|
} catch (error) {
|
|
1666
1763
|
try {
|
|
@@ -2192,8 +2289,8 @@ function chromeClosedMessage(pid) {
|
|
|
2192
2289
|
function orphanedDaemonsCleanedMessage(count) {
|
|
2193
2290
|
return `Cleaned up ${count} orphaned daemon process${count === 1 ? "" : "es"}`;
|
|
2194
2291
|
}
|
|
2195
|
-
function skillBackupMessage(
|
|
2196
|
-
return `previous copy kept in ${
|
|
2292
|
+
function skillBackupMessage(path8) {
|
|
2293
|
+
return `previous copy kept in ${path8}`;
|
|
2197
2294
|
}
|
|
2198
2295
|
function fieldLabelList(labels) {
|
|
2199
2296
|
const shown = labels.slice(0, 5).map((label) => label.replace(/\s*:\s*$/, "")).join(", ");
|
|
@@ -3120,6 +3217,7 @@ var init_shellDetection = __esm({
|
|
|
3120
3217
|
});
|
|
3121
3218
|
|
|
3122
3219
|
// src/errors/messages.ts
|
|
3220
|
+
import * as os3 from "os";
|
|
3123
3221
|
import * as path4 from "path";
|
|
3124
3222
|
function sessionAlreadyRunningError(pid, duration, targetUrl) {
|
|
3125
3223
|
return joinLines(
|
|
@@ -3291,8 +3389,13 @@ function unknownHelpTopicError(topic, closest) {
|
|
|
3291
3389
|
suggestion: closest ? `Did you mean: bdg help ${closest}?` : `Run "bdg ${parent ? `${parent} ` : ""}--help" for commands`
|
|
3292
3390
|
};
|
|
3293
3391
|
}
|
|
3294
|
-
function
|
|
3295
|
-
|
|
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`;
|
|
3296
3399
|
}
|
|
3297
3400
|
function missingSubcommandMessage() {
|
|
3298
3401
|
return "Missing subcommand";
|
|
@@ -3349,7 +3452,7 @@ function invalidSessionNameError(name, maxLength) {
|
|
|
3349
3452
|
function sessionNameSocketTooLongError(name, socketPath, max) {
|
|
3350
3453
|
return {
|
|
3351
3454
|
message: `Session name "${name}" makes the daemon socket path too long (${Buffer.byteLength(socketPath)} bytes, at most ${max}): ${socketPath}`,
|
|
3352
|
-
suggestion:
|
|
3455
|
+
suggestion: `Use a shorter session name, or a shorter BDG_SESSION_DIR (e.g. ${privateSessionDirExample()})`
|
|
3353
3456
|
};
|
|
3354
3457
|
}
|
|
3355
3458
|
function purgeNeedsNamedSessionError() {
|
|
@@ -3364,19 +3467,52 @@ function purgeRefusedError(dir, reason) {
|
|
|
3364
3467
|
suggestion: `End the session first (${sessionCommand("bdg cleanup --force")}), then retry: ${sessionCommand("bdg cleanup --purge")}`
|
|
3365
3468
|
};
|
|
3366
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
|
+
}
|
|
3367
3479
|
function sessionDirIsFileError(dir) {
|
|
3368
|
-
return { message: `Session directory ${dir} is a file`, suggestion:
|
|
3480
|
+
return { message: `Session directory ${dir} is a file`, suggestion: sessionDirSuggestion() };
|
|
3369
3481
|
}
|
|
3370
3482
|
function sessionDirNotWritableError(dir, reason) {
|
|
3371
3483
|
return {
|
|
3372
3484
|
message: `Session directory ${dir} is not writable (${reason})`,
|
|
3373
|
-
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`
|
|
3374
3510
|
};
|
|
3375
3511
|
}
|
|
3376
3512
|
function socketPathTooLongError(socketPath, max) {
|
|
3377
3513
|
return {
|
|
3378
3514
|
message: `Session directory path is too long for the daemon socket (${Buffer.byteLength(socketPath)} bytes, at most ${max}): ${socketPath}`,
|
|
3379
|
-
suggestion:
|
|
3515
|
+
suggestion: sessionDirSuggestion()
|
|
3380
3516
|
};
|
|
3381
3517
|
}
|
|
3382
3518
|
function externalChromeUnreachableError(endpoint, secure) {
|
|
@@ -3715,10 +3851,10 @@ function skillSourceMissingError(source) {
|
|
|
3715
3851
|
suggestion: "Reinstall bdg: npm install -g browser-debugger-cli"
|
|
3716
3852
|
};
|
|
3717
3853
|
}
|
|
3718
|
-
function skillWriteFailedError(
|
|
3854
|
+
function skillWriteFailedError(path8, reason, pathIsFile, otherTargetFlag) {
|
|
3719
3855
|
const fix = pathIsFile ? "A part of that path is a file, not a directory: move it away" : "Check the permissions of that directory";
|
|
3720
3856
|
return {
|
|
3721
|
-
message: `Could not write ${
|
|
3857
|
+
message: `Could not write ${path8}: ${reason}`,
|
|
3722
3858
|
suggestion: otherTargetFlag ? `${fix}, or install for the other agent only (${otherTargetFlag})` : fix
|
|
3723
3859
|
};
|
|
3724
3860
|
}
|
|
@@ -3731,7 +3867,7 @@ function intervalWithoutFollowError() {
|
|
|
3731
3867
|
function unknownAuditCheckMessage(value, similar, checks) {
|
|
3732
3868
|
return `Unknown check "${value}"${similar.length > 0 ? `; did you mean ${similar[0]}?` : ""} (checks: ${checks.join(", ")})`;
|
|
3733
3869
|
}
|
|
3734
|
-
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;
|
|
3735
3871
|
var init_messages = __esm({
|
|
3736
3872
|
"src/errors/messages.ts"() {
|
|
3737
3873
|
"use strict";
|
|
@@ -3752,7 +3888,6 @@ var init_messages = __esm({
|
|
|
3752
3888
|
"empty-has": () => ":has() has an empty selector",
|
|
3753
3889
|
"scope-in-has": () => ":has() with filters is already relative to the element; write :has(> a:visible) instead of :has(:scope > a:visible)"
|
|
3754
3890
|
};
|
|
3755
|
-
SESSION_DIR_SUGGESTION = "Set BDG_SESSION_DIR to a short, writable directory, e.g. BDG_SESSION_DIR=/tmp/bdg";
|
|
3756
3891
|
SCROLL_PROBLEMS = {
|
|
3757
3892
|
"index-without-selector": [
|
|
3758
3893
|
"--index requires a selector",
|
|
@@ -4075,7 +4210,17 @@ var init_validation2 = __esm({
|
|
|
4075
4210
|
});
|
|
4076
4211
|
|
|
4077
4212
|
// src/ipc/transport/index.ts
|
|
4213
|
+
import * as path5 from "path";
|
|
4078
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
|
+
}
|
|
4079
4224
|
return new Promise((resolve4, reject2) => {
|
|
4080
4225
|
const buffer = new JSONLBuffer();
|
|
4081
4226
|
let resolved = false;
|
|
@@ -4134,8 +4279,11 @@ var init_transport = __esm({
|
|
|
4134
4279
|
"src/ipc/transport/index.ts"() {
|
|
4135
4280
|
"use strict";
|
|
4136
4281
|
init_constants();
|
|
4282
|
+
init_errors3();
|
|
4283
|
+
init_messages();
|
|
4137
4284
|
init_paths();
|
|
4138
4285
|
init_logging();
|
|
4286
|
+
init_exitCodes();
|
|
4139
4287
|
init_errors4();
|
|
4140
4288
|
init_jsonl();
|
|
4141
4289
|
init_socket();
|
|
@@ -4311,8 +4459,12 @@ async function callCDP(method, params, options = {}) {
|
|
|
4311
4459
|
function callBdgScript(method, params) {
|
|
4312
4460
|
return callCDP(method, params, { isolated: true });
|
|
4313
4461
|
}
|
|
4314
|
-
async function domEval(script, frame) {
|
|
4315
|
-
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
|
+
});
|
|
4316
4468
|
}
|
|
4317
4469
|
async function domFrames() {
|
|
4318
4470
|
return sendCommand("dom_frames", {});
|
|
@@ -8109,7 +8261,7 @@ function textPreview2(text, length = PREVIEW_LENGTH) {
|
|
|
8109
8261
|
const characters = Array.from(collapsed);
|
|
8110
8262
|
return characters.length > length ? characters.slice(0, length).join("") + "..." : collapsed;
|
|
8111
8263
|
}
|
|
8112
|
-
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;
|
|
8113
8265
|
var init_elementInfo = __esm({
|
|
8114
8266
|
"src/runtime/dom/elementInfo.ts"() {
|
|
8115
8267
|
"use strict";
|
|
@@ -8200,12 +8352,13 @@ var init_elementInfo = __esm({
|
|
|
8200
8352
|
return withoutDecorations(el, start);
|
|
8201
8353
|
}`;
|
|
8202
8354
|
MASKED_VALUE = "\u2022\u2022\u2022\u2022";
|
|
8355
|
+
SENSITIVE_NAME_SOURCE = "passw|passwd|pwd|passcode|(^|[^a-z])otp([^a-z]|$)|cvv|cvc";
|
|
8203
8356
|
SENSITIVE_FIELD_JS = `(el) => {
|
|
8204
8357
|
const autocomplete = el.getAttribute('autocomplete') || '';
|
|
8205
8358
|
if (/(^|\\s)(cc-[a-z-]+|one-time-code|current-password|new-password)(\\s|$)/i.test(autocomplete)) return true;
|
|
8206
8359
|
if (el.type === 'password' || /^password$/i.test(el.getAttribute('type') || '')) return true;
|
|
8207
8360
|
const names = [el.getAttribute('name'), el.id, autocomplete].join(' ');
|
|
8208
|
-
if (/
|
|
8361
|
+
if (/${SENSITIVE_NAME_SOURCE}/i.test(names)) return true;
|
|
8209
8362
|
try {
|
|
8210
8363
|
const security = el.ownerDocument.defaultView.getComputedStyle(el).getPropertyValue('-webkit-text-security');
|
|
8211
8364
|
return Boolean(security) && security !== 'none';
|
|
@@ -8990,10 +9143,10 @@ function findHasFilters(text, selector) {
|
|
|
8990
9143
|
}
|
|
8991
9144
|
function parseHasArgument(argument, selector) {
|
|
8992
9145
|
if (!FILTER_NAME_HINT.test(argument)) return null;
|
|
8993
|
-
const selectors = splitSelectorList(argument).map((
|
|
8994
|
-
if (
|
|
8995
|
-
if (SCOPE_PSEUDO.test(
|
|
8996
|
-
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);
|
|
8997
9150
|
if (leading && leading !== ">") reject(siblingInHasError(selector, leading));
|
|
8998
9151
|
const [first, ...rest] = steps;
|
|
8999
9152
|
return first ? [{ ...first, combinator: leading || " " }, ...rest] : [];
|
|
@@ -9761,7 +9914,7 @@ var init_query = __esm({
|
|
|
9761
9914
|
|
|
9762
9915
|
// src/commands/shared/outputFile.ts
|
|
9763
9916
|
import * as fs15 from "fs";
|
|
9764
|
-
import * as
|
|
9917
|
+
import * as path6 from "path";
|
|
9765
9918
|
function outputPathError(filePath, error, extension2) {
|
|
9766
9919
|
const code = error.code ?? "";
|
|
9767
9920
|
const problem = PATH_PROBLEMS[code];
|
|
@@ -9790,13 +9943,16 @@ function assertFilePath(filePath, extension2) {
|
|
|
9790
9943
|
);
|
|
9791
9944
|
}
|
|
9792
9945
|
}
|
|
9793
|
-
async function writeOutputFile(filePath, data, extension2) {
|
|
9946
|
+
async function writeOutputFile(filePath, data, extension2, mode) {
|
|
9794
9947
|
assertFilePath(filePath, extension2);
|
|
9795
|
-
const absolutePath =
|
|
9948
|
+
const absolutePath = path6.resolve(filePath);
|
|
9796
9949
|
try {
|
|
9797
|
-
makeDirectory(
|
|
9798
|
-
if (typeof data === "string")
|
|
9799
|
-
|
|
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
|
+
}
|
|
9800
9956
|
} catch (error) {
|
|
9801
9957
|
throw outputPathError(filePath, error, extension2);
|
|
9802
9958
|
}
|
|
@@ -9830,10 +9986,13 @@ var init_outputFile = __esm({
|
|
|
9830
9986
|
});
|
|
9831
9987
|
|
|
9832
9988
|
// src/runtime/page/userAgent.ts
|
|
9989
|
+
var log24;
|
|
9833
9990
|
var init_userAgent = __esm({
|
|
9834
9991
|
"src/runtime/page/userAgent.ts"() {
|
|
9835
9992
|
"use strict";
|
|
9993
|
+
init_logging();
|
|
9836
9994
|
init_errors();
|
|
9995
|
+
log24 = createLogger("session");
|
|
9837
9996
|
}
|
|
9838
9997
|
});
|
|
9839
9998
|
|
|
@@ -9847,7 +10006,7 @@ function viewportOverride(viewport, deviceScaleFactor = 0) {
|
|
|
9847
10006
|
mobile
|
|
9848
10007
|
};
|
|
9849
10008
|
}
|
|
9850
|
-
var
|
|
10009
|
+
var log25, MOBILE_PIXEL_RATIO;
|
|
9851
10010
|
var init_emulation = __esm({
|
|
9852
10011
|
"src/runtime/page/emulation.ts"() {
|
|
9853
10012
|
"use strict";
|
|
@@ -9855,7 +10014,7 @@ var init_emulation = __esm({
|
|
|
9855
10014
|
init_userAgent();
|
|
9856
10015
|
init_logging();
|
|
9857
10016
|
init_errors();
|
|
9858
|
-
|
|
10017
|
+
log25 = createLogger("session");
|
|
9859
10018
|
MOBILE_PIXEL_RATIO = 3;
|
|
9860
10019
|
}
|
|
9861
10020
|
});
|
|
@@ -9920,12 +10079,12 @@ async function waitForPostScrollStability() {
|
|
|
9920
10079
|
const networkIdle = value?.networkIdle ?? 0;
|
|
9921
10080
|
const domIdle = value?.domIdle ?? 0;
|
|
9922
10081
|
if (networkIdle >= POST_SCROLL_NETWORK_IDLE_MS && domIdle >= POST_SCROLL_DOM_STABLE_MS) {
|
|
9923
|
-
|
|
10082
|
+
log26.debug(`Post-scroll stable: network ${networkIdle}ms, DOM ${domIdle}ms`);
|
|
9924
10083
|
return;
|
|
9925
10084
|
}
|
|
9926
10085
|
await new Promise((resolve4) => setTimeout(resolve4, STABILITY_CHECK_INTERVAL_MS));
|
|
9927
10086
|
}
|
|
9928
|
-
|
|
10087
|
+
log26.debug("Post-scroll stability timeout, proceeding anyway");
|
|
9929
10088
|
} finally {
|
|
9930
10089
|
await callBdgScript("Runtime.evaluate", {
|
|
9931
10090
|
expression: `
|
|
@@ -10224,7 +10383,7 @@ async function paintedArea(ref, bounds) {
|
|
|
10224
10383
|
height: bounds.height + top + bottom
|
|
10225
10384
|
};
|
|
10226
10385
|
} catch (error) {
|
|
10227
|
-
|
|
10386
|
+
log26.debug(`Could not measure overflowing content: ${String(error)}`);
|
|
10228
10387
|
return bounds;
|
|
10229
10388
|
} finally {
|
|
10230
10389
|
await callCDP("Runtime.releaseObjectGroup", { objectGroup }).catch(() => void 0);
|
|
@@ -10321,7 +10480,7 @@ function roundBounds(bounds) {
|
|
|
10321
10480
|
height: Math.round(bounds.height)
|
|
10322
10481
|
};
|
|
10323
10482
|
}
|
|
10324
|
-
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;
|
|
10325
10484
|
var init_screenshot = __esm({
|
|
10326
10485
|
"src/commands/dom/helpers/screenshot.ts"() {
|
|
10327
10486
|
"use strict";
|
|
@@ -10336,7 +10495,7 @@ var init_screenshot = __esm({
|
|
|
10336
10495
|
init_metadata();
|
|
10337
10496
|
init_logging();
|
|
10338
10497
|
init_exitCodes();
|
|
10339
|
-
|
|
10498
|
+
log26 = createLogger("dom");
|
|
10340
10499
|
POST_SCROLL_NETWORK_IDLE_MS = 150;
|
|
10341
10500
|
POST_SCROLL_DOM_STABLE_MS = 200;
|
|
10342
10501
|
POST_SCROLL_MAX_WAIT_MS = 2e3;
|
|
@@ -13537,9 +13696,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
13537
13696
|
* @param {string} [path]
|
|
13538
13697
|
* @return {(string|null|Command)}
|
|
13539
13698
|
*/
|
|
13540
|
-
executableDir(
|
|
13541
|
-
if (
|
|
13542
|
-
this._executableDir =
|
|
13699
|
+
executableDir(path8) {
|
|
13700
|
+
if (path8 === void 0) return this._executableDir;
|
|
13701
|
+
this._executableDir = path8;
|
|
13543
13702
|
return this;
|
|
13544
13703
|
}
|
|
13545
13704
|
/**
|
|
@@ -13882,9 +14041,9 @@ var OPTION_BEHAVIORS = {
|
|
|
13882
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."
|
|
13883
14042
|
},
|
|
13884
14043
|
"eval:--full": {
|
|
13885
|
-
default: 'Human output prints the first 20000 characters of the value followed by "\u2026 N more chars (use --full)"
|
|
13886
|
-
whenEnabled: "Prints the whole value, byte for byte",
|
|
13887
|
-
tokenImpact:
|
|
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'
|
|
13888
14047
|
},
|
|
13889
14048
|
"console:--full": {
|
|
13890
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)',
|
|
@@ -14111,6 +14270,11 @@ var OPTION_BEHAVIORS = {
|
|
|
14111
14270
|
whenEnabled: "Streams requests as they finish",
|
|
14112
14271
|
automaticBehavior: FOLLOW_BEHAVIOR
|
|
14113
14272
|
},
|
|
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
|
+
},
|
|
14114
14278
|
"peek:--verbose": {
|
|
14115
14279
|
default: "Compact output (truncated URLs, no resource types)",
|
|
14116
14280
|
whenEnabled: "Verbose output with full URLs and resource types"
|
|
@@ -14231,25 +14395,25 @@ var loggingBufferColumns = 25;
|
|
|
14231
14395
|
var level_;
|
|
14232
14396
|
var Log = class _Log {
|
|
14233
14397
|
static _logToStdErr(title, argsArray) {
|
|
14234
|
-
const
|
|
14235
|
-
|
|
14398
|
+
const log36 = _Log.loggerfn(title);
|
|
14399
|
+
log36(...argsArray);
|
|
14236
14400
|
}
|
|
14237
14401
|
/**
|
|
14238
14402
|
* @param {string} title
|
|
14239
14403
|
*/
|
|
14240
14404
|
static loggerfn(title) {
|
|
14241
14405
|
title = `LH:${title}`;
|
|
14242
|
-
let
|
|
14243
|
-
if (!
|
|
14244
|
-
|
|
14245
|
-
loggersByTitle[title] =
|
|
14406
|
+
let log36 = loggersByTitle[title];
|
|
14407
|
+
if (!log36) {
|
|
14408
|
+
log36 = (0, import_debug.default)(title);
|
|
14409
|
+
loggersByTitle[title] = log36;
|
|
14246
14410
|
if (title.endsWith("error")) {
|
|
14247
|
-
|
|
14411
|
+
log36.color = colors.red;
|
|
14248
14412
|
} else if (title.endsWith("warn")) {
|
|
14249
|
-
|
|
14413
|
+
log36.color = colors.yellow;
|
|
14250
14414
|
}
|
|
14251
14415
|
}
|
|
14252
|
-
return
|
|
14416
|
+
return log36;
|
|
14253
14417
|
}
|
|
14254
14418
|
/**
|
|
14255
14419
|
* @param {string} level
|
|
@@ -14846,12 +15010,12 @@ async function waitUntil(condition, timeoutMs, pollMs = 25) {
|
|
|
14846
15010
|
// src/utils/file.ts
|
|
14847
15011
|
init_logging();
|
|
14848
15012
|
import * as fs9 from "fs";
|
|
14849
|
-
function safeRemoveFile(
|
|
15013
|
+
function safeRemoveFile(path8, context, log36) {
|
|
14850
15014
|
try {
|
|
14851
|
-
fs9.rmSync(
|
|
15015
|
+
fs9.rmSync(path8, { force: true });
|
|
14852
15016
|
return true;
|
|
14853
15017
|
} catch (error) {
|
|
14854
|
-
logDebugError(
|
|
15018
|
+
logDebugError(log36, `remove ${context}`, error);
|
|
14855
15019
|
return false;
|
|
14856
15020
|
}
|
|
14857
15021
|
}
|
|
@@ -15342,7 +15506,7 @@ function convertCompactCommand(command) {
|
|
|
15342
15506
|
return {
|
|
15343
15507
|
name: command.name(),
|
|
15344
15508
|
...aliases.length > 0 && { aliases },
|
|
15345
|
-
description: command.description().split("\n")[0] ?? "",
|
|
15509
|
+
description: command.summary() || (command.description().split("\n")[0] ?? ""),
|
|
15346
15510
|
...args && { arguments: args },
|
|
15347
15511
|
...options.length > 0 && {
|
|
15348
15512
|
options: Object.fromEntries(options.map((option) => [option.flags, option.description]))
|
|
@@ -15439,14 +15603,14 @@ init_errors3();
|
|
|
15439
15603
|
init_messages();
|
|
15440
15604
|
init_exitCodes();
|
|
15441
15605
|
init_suggestions();
|
|
15442
|
-
var COMMANDER_HINT = /\n?\(Did you mean (.+)\?\)\s*$/;
|
|
15606
|
+
var COMMANDER_HINT = /\n?\(Did you mean (?:one of )?(.+)\?\)\s*$/;
|
|
15443
15607
|
function helpTopicPath(args) {
|
|
15444
15608
|
const firstOption = args.findIndex((arg) => arg.startsWith("-"));
|
|
15445
15609
|
return firstOption === -1 ? args : args.slice(0, firstOption);
|
|
15446
15610
|
}
|
|
15447
15611
|
function assertKnownHelpTopic(program2, topic) {
|
|
15448
15612
|
let command = program2;
|
|
15449
|
-
const
|
|
15613
|
+
const path8 = [];
|
|
15450
15614
|
for (const word of topic) {
|
|
15451
15615
|
if (command.commands.length === 0 || word === "help") return;
|
|
15452
15616
|
const sub = command.commands.find((c) => c.name() === word || c.aliases().includes(word));
|
|
@@ -15456,8 +15620,8 @@ function assertKnownHelpTopic(program2, topic) {
|
|
|
15456
15620
|
command.commands.map((c) => c.name())
|
|
15457
15621
|
);
|
|
15458
15622
|
const err = unknownHelpTopicError(
|
|
15459
|
-
[...
|
|
15460
|
-
closest && [...
|
|
15623
|
+
[...path8, word].join(" "),
|
|
15624
|
+
closest && [...path8, closest].join(" ")
|
|
15461
15625
|
);
|
|
15462
15626
|
throw new CommandError(
|
|
15463
15627
|
err.message,
|
|
@@ -15465,7 +15629,7 @@ function assertKnownHelpTopic(program2, topic) {
|
|
|
15465
15629
|
EXIT_CODES.INVALID_ARGUMENTS
|
|
15466
15630
|
);
|
|
15467
15631
|
}
|
|
15468
|
-
|
|
15632
|
+
path8.push(sub.name());
|
|
15469
15633
|
command = sub;
|
|
15470
15634
|
}
|
|
15471
15635
|
}
|
|
@@ -15473,7 +15637,10 @@ function splitCommanderHint(text) {
|
|
|
15473
15637
|
const message = text.replace(/^error:\s*/i, "");
|
|
15474
15638
|
const hint = COMMANDER_HINT.exec(message);
|
|
15475
15639
|
if (!hint) return { message };
|
|
15476
|
-
return {
|
|
15640
|
+
return {
|
|
15641
|
+
message: message.slice(0, hint.index).trim(),
|
|
15642
|
+
suggestion: didYouMeanSuggestion((hint[1] ?? "").split(", "))
|
|
15643
|
+
};
|
|
15477
15644
|
}
|
|
15478
15645
|
var UNKNOWN_OPTION = /unknown option '([^'=]+)/;
|
|
15479
15646
|
function closestOption(flag, command) {
|
|
@@ -15504,7 +15671,7 @@ function usageErrorDetails(error, command, argv = process.argv) {
|
|
|
15504
15671
|
|
|
15505
15672
|
// src/commands/start.ts
|
|
15506
15673
|
import fs11 from "node:fs";
|
|
15507
|
-
import
|
|
15674
|
+
import os4 from "node:os";
|
|
15508
15675
|
|
|
15509
15676
|
// src/commands/shared/commonOptions.ts
|
|
15510
15677
|
function jsonOption() {
|
|
@@ -15529,14 +15696,14 @@ init_messages();
|
|
|
15529
15696
|
|
|
15530
15697
|
// src/utils/version.ts
|
|
15531
15698
|
import { readFileSync as readFileSync6 } from "fs";
|
|
15532
|
-
import { join as
|
|
15699
|
+
import { join as join6 } from "path";
|
|
15533
15700
|
var cachedVersion = "";
|
|
15534
15701
|
function getVersion() {
|
|
15535
15702
|
if (cachedVersion) {
|
|
15536
15703
|
return cachedVersion;
|
|
15537
15704
|
}
|
|
15538
15705
|
try {
|
|
15539
|
-
const pkgPath =
|
|
15706
|
+
const pkgPath = join6(PACKAGE_ROOT, "package.json");
|
|
15540
15707
|
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
15541
15708
|
cachedVersion = pkg.version ?? "0.0.0";
|
|
15542
15709
|
} catch {
|
|
@@ -15792,6 +15959,9 @@ function lastSessionEndText(end) {
|
|
|
15792
15959
|
function endedSessionText(label, end) {
|
|
15793
15960
|
return `${label} ended ${sessionEndText(end)}`;
|
|
15794
15961
|
}
|
|
15962
|
+
function untrustedSessionText(label, why) {
|
|
15963
|
+
return `${label}: ${why}`;
|
|
15964
|
+
}
|
|
15795
15965
|
function sessionEndText(end) {
|
|
15796
15966
|
const why = {
|
|
15797
15967
|
crash: "Chrome crashed or was closed",
|
|
@@ -15849,7 +16019,7 @@ var SessionDirError = class extends DaemonError {
|
|
|
15849
16019
|
// src/daemon/launcher.ts
|
|
15850
16020
|
import { spawn } from "child_process";
|
|
15851
16021
|
import fs10 from "fs";
|
|
15852
|
-
import { join as
|
|
16022
|
+
import { join as join7 } from "path";
|
|
15853
16023
|
init_messages();
|
|
15854
16024
|
init_paths();
|
|
15855
16025
|
init_logging();
|
|
@@ -15860,6 +16030,7 @@ var log8 = createLogger("launcher");
|
|
|
15860
16030
|
var DAEMON_READY_TIMEOUT_MS = 5e3;
|
|
15861
16031
|
var DAEMON_READY_POLL_MS = 20;
|
|
15862
16032
|
async function launchDaemon() {
|
|
16033
|
+
assertUsableSessionDir();
|
|
15863
16034
|
if (await isDaemonAlive()) {
|
|
15864
16035
|
log8.debug("Daemon already running");
|
|
15865
16036
|
return void 0;
|
|
@@ -15870,11 +16041,10 @@ async function launchDaemon() {
|
|
|
15870
16041
|
"DAEMON_SCRIPT_NOT_FOUND"
|
|
15871
16042
|
);
|
|
15872
16043
|
}
|
|
15873
|
-
assertUsableSessionDir();
|
|
15874
16044
|
await stopUnreachableDaemon();
|
|
15875
|
-
const logPath =
|
|
16045
|
+
const logPath = join7(getSessionDir(), "daemon.log");
|
|
15876
16046
|
rotateLog(logPath);
|
|
15877
|
-
const logFd =
|
|
16047
|
+
const logFd = openDaemonLog(logPath);
|
|
15878
16048
|
log8.debug(`Starting daemon: ${DAEMON_SCRIPT_PATH}`);
|
|
15879
16049
|
const daemon = spawn(process.execPath, [DAEMON_SCRIPT_PATH], {
|
|
15880
16050
|
detached: true,
|
|
@@ -15930,6 +16100,8 @@ function assertUsableSessionDir() {
|
|
|
15930
16100
|
problem.denied ? EXIT_CODES.PERMISSION_DENIED : EXIT_CODES.SESSION_FILE_ERROR
|
|
15931
16101
|
);
|
|
15932
16102
|
}
|
|
16103
|
+
const untrusted = secureSessionDir(dir);
|
|
16104
|
+
if (untrusted) fail(untrustedSessionDirError(untrusted));
|
|
15933
16105
|
try {
|
|
15934
16106
|
ensureSessionDir();
|
|
15935
16107
|
fs10.accessSync(dir, fs10.constants.W_OK);
|
|
@@ -15942,6 +16114,18 @@ function assertUsableSessionDir() {
|
|
|
15942
16114
|
);
|
|
15943
16115
|
}
|
|
15944
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
|
+
}
|
|
15945
16129
|
var MAX_LOG_BYTES = 5 * 1024 * 1024;
|
|
15946
16130
|
function rotateLog(logPath) {
|
|
15947
16131
|
try {
|
|
@@ -15961,7 +16145,7 @@ async function waitForDaemonReady(hasExited) {
|
|
|
15961
16145
|
}
|
|
15962
16146
|
if (hasExited() && !await isDaemonAlive()) {
|
|
15963
16147
|
throw new DaemonStartupError(
|
|
15964
|
-
`Daemon exited during startup. See ${
|
|
16148
|
+
`Daemon exited during startup. See ${join7(getSessionDir(), "daemon.log")}`,
|
|
15965
16149
|
"DAEMON_EXITED"
|
|
15966
16150
|
);
|
|
15967
16151
|
}
|
|
@@ -16568,7 +16752,7 @@ async function probeDevToolsEndpoint(port, logger, options) {
|
|
|
16568
16752
|
init_suggestions();
|
|
16569
16753
|
init_url();
|
|
16570
16754
|
function expandHome(value) {
|
|
16571
|
-
return value.startsWith("~/") ? value.replace(/^~/,
|
|
16755
|
+
return value.startsWith("~/") ? value.replace(/^~/, os4.homedir()) : value;
|
|
16572
16756
|
}
|
|
16573
16757
|
function extractUserDataDirFromFlags(flags) {
|
|
16574
16758
|
const rest = [];
|
|
@@ -17244,7 +17428,7 @@ function getMethodHint(methodName, result) {
|
|
|
17244
17428
|
return void 0;
|
|
17245
17429
|
}
|
|
17246
17430
|
function registerCdpCommand(program2) {
|
|
17247
|
-
program2.command("cdp").description(
|
|
17431
|
+
program2.command("cdp").summary("CDP protocol introspection and execution").description(
|
|
17248
17432
|
"CDP protocol introspection and execution\n Discovery: --list, --search, --describe\n Execution: case-insensitive (network.getcookies works)"
|
|
17249
17433
|
).argument("[method]", "CDP method name (e.g., Network.getCookies, network.getcookies)").addOption(new Option("--params <json>", "Method parameters as JSON")).addOption(
|
|
17250
17434
|
new Option("--list", "List all domains or methods in a domain").conflicts([
|
|
@@ -18742,6 +18926,15 @@ function headerRepeatedNote(count) {
|
|
|
18742
18926
|
function localProxyNote() {
|
|
18743
18927
|
return "(loopback; likely a local proxy)";
|
|
18744
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
|
+
}
|
|
18745
18938
|
|
|
18746
18939
|
// src/telemetry/networkRetention.ts
|
|
18747
18940
|
var SKIPPED_BODY_PATTERN = /^\[SKIPPED: (.*)\]$/s;
|
|
@@ -19912,9 +20105,9 @@ init_url();
|
|
|
19912
20105
|
var MAX_VALUE_LENGTH2 = 40;
|
|
19913
20106
|
function fileName(src) {
|
|
19914
20107
|
if (/^(data|blob):/i.test(src)) return src;
|
|
19915
|
-
const
|
|
19916
|
-
const name =
|
|
19917
|
-
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;
|
|
19918
20111
|
}
|
|
19919
20112
|
function hostOf(src) {
|
|
19920
20113
|
const parsed = /^https?:/i.test(src) ? safeParseUrl(src) : null;
|
|
@@ -20043,7 +20236,7 @@ init_elementInfo();
|
|
|
20043
20236
|
init_frameScopedConnection();
|
|
20044
20237
|
init_logging();
|
|
20045
20238
|
init_errors();
|
|
20046
|
-
var
|
|
20239
|
+
var log27 = createLogger("dom");
|
|
20047
20240
|
function childFrameIds(tree) {
|
|
20048
20241
|
return (tree?.childFrames ?? []).flatMap((child) => [child.frame.id, ...childFrameIds(child)]);
|
|
20049
20242
|
}
|
|
@@ -20765,9 +20958,34 @@ async function audit(checks, options) {
|
|
|
20765
20958
|
return { success: true, data: response.data };
|
|
20766
20959
|
}
|
|
20767
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
|
+
|
|
20768
20987
|
// src/commands/dom/eval.ts
|
|
20769
20988
|
init_query();
|
|
20770
|
-
init_constants();
|
|
20771
20989
|
init_messages();
|
|
20772
20990
|
init_client();
|
|
20773
20991
|
|
|
@@ -20861,7 +21079,6 @@ function formatDomScreenshot(data) {
|
|
|
20861
21079
|
// src/commands/dom/eval.ts
|
|
20862
21080
|
init_commands();
|
|
20863
21081
|
init_exitCodes();
|
|
20864
|
-
init_strings();
|
|
20865
21082
|
async function handleDomEval(script, options) {
|
|
20866
21083
|
await runCommand(
|
|
20867
21084
|
async () => {
|
|
@@ -20874,7 +21091,7 @@ async function handleDomEval(script, options) {
|
|
|
20874
21091
|
errorContext: { suggestion: err.suggestion }
|
|
20875
21092
|
};
|
|
20876
21093
|
}
|
|
20877
|
-
const response = await domEval(script, options.frame);
|
|
21094
|
+
const response = await domEval(script, options.frame, options.full);
|
|
20878
21095
|
if (response.status === "error" || !response.data) {
|
|
20879
21096
|
const suggestion = await frameErrorSuggestion(response, options.frame);
|
|
20880
21097
|
return {
|
|
@@ -20884,7 +21101,7 @@ async function handleDomEval(script, options) {
|
|
|
20884
21101
|
...suggestion && { errorContext: { suggestion } }
|
|
20885
21102
|
};
|
|
20886
21103
|
}
|
|
20887
|
-
const { value, type, subtype, frame, warning } = response.data;
|
|
21104
|
+
const { value, type, subtype, length, frame, warning } = response.data;
|
|
20888
21105
|
const hint = [
|
|
20889
21106
|
...frame !== void 0 ? [evalFrameLine(frame)] : [],
|
|
20890
21107
|
...warning ? [warningMessage(warning)] : []
|
|
@@ -20892,7 +21109,7 @@ async function handleDomEval(script, options) {
|
|
|
20892
21109
|
return {
|
|
20893
21110
|
success: true,
|
|
20894
21111
|
data: {
|
|
20895
|
-
...
|
|
21112
|
+
...options.json && !options.full ? boundEvalResult(value, length) : { result: value },
|
|
20896
21113
|
type,
|
|
20897
21114
|
...subtype && { subtype },
|
|
20898
21115
|
...frame !== void 0 && { frame },
|
|
@@ -20905,11 +21122,6 @@ async function handleDomEval(script, options) {
|
|
|
20905
21122
|
(data) => formatDomEval(data, { full: options.full })
|
|
20906
21123
|
);
|
|
20907
21124
|
}
|
|
20908
|
-
function jsonResult(value, options) {
|
|
20909
|
-
if (!options.json || options.full || typeof value !== "string") return { result: value };
|
|
20910
|
-
const { text, truncatedFrom } = capLength(value, MAX_VALUE_LENGTH);
|
|
20911
|
-
return { result: text, ...truncatedFrom !== void 0 && { truncatedFrom } };
|
|
20912
|
-
}
|
|
20913
21125
|
async function frameErrorSuggestion(response, frame) {
|
|
20914
21126
|
if (frame === void 0 || response.exitCode !== EXIT_CODES.RESOURCE_NOT_FOUND) {
|
|
20915
21127
|
return response.suggestion;
|
|
@@ -21227,7 +21439,7 @@ function formatFormDiscovery(result) {
|
|
|
21227
21439
|
init_logging();
|
|
21228
21440
|
init_errors();
|
|
21229
21441
|
init_exitCodes();
|
|
21230
|
-
var
|
|
21442
|
+
var log28 = createLogger("dom");
|
|
21231
21443
|
function buildValidation(raw) {
|
|
21232
21444
|
const hasNativeError = !raw.isValid && !raw.valueMissing && raw.validationMessage;
|
|
21233
21445
|
const hasAriaError = raw.ariaInvalid;
|
|
@@ -21384,7 +21596,7 @@ async function cacheFormElements(forms, document2) {
|
|
|
21384
21596
|
const elements = forms.flatMap((form) => [...form.fields, ...form.buttons]);
|
|
21385
21597
|
const backendNodeIds = await resolveBackendNodeIds(elements.map((el) => el.selector)).catch(
|
|
21386
21598
|
(error) => {
|
|
21387
|
-
|
|
21599
|
+
log28.debug(`Form fields not cached by node: ${getErrorMessage(error)}`);
|
|
21388
21600
|
return [];
|
|
21389
21601
|
}
|
|
21390
21602
|
);
|
|
@@ -21400,7 +21612,7 @@ async function cacheFormElements(forms, document2) {
|
|
|
21400
21612
|
},
|
|
21401
21613
|
document2
|
|
21402
21614
|
);
|
|
21403
|
-
|
|
21615
|
+
log28.debug(`Cached ${elements.length} form elements`);
|
|
21404
21616
|
}
|
|
21405
21617
|
async function handleFormCommand(options) {
|
|
21406
21618
|
await runCommand(
|
|
@@ -22386,7 +22598,7 @@ init_logging();
|
|
|
22386
22598
|
init_directories();
|
|
22387
22599
|
init_errors();
|
|
22388
22600
|
init_exitCodes();
|
|
22389
|
-
var
|
|
22601
|
+
var log29 = createLogger("dom");
|
|
22390
22602
|
var EXTENSION_FORMATS = {
|
|
22391
22603
|
".png": "png",
|
|
22392
22604
|
".jpg": "jpeg",
|
|
@@ -22512,8 +22724,8 @@ async function captureSequenceFrame(outputPath, options) {
|
|
|
22512
22724
|
}
|
|
22513
22725
|
async function handleSequenceCapture(outputDir, options) {
|
|
22514
22726
|
const fs19 = await import("fs");
|
|
22515
|
-
const
|
|
22516
|
-
const absoluteDir =
|
|
22727
|
+
const path8 = await import("path");
|
|
22728
|
+
const absoluteDir = path8.resolve(outputDir);
|
|
22517
22729
|
ensureDirectory(absoluteDir, fs19);
|
|
22518
22730
|
const intervalRule = positiveIntRule({ name: "--interval", min: 100, max: 6e4, default: 1e3 });
|
|
22519
22731
|
const limitRule = positiveIntRule({ name: "--limit", min: 1, max: 1e4, required: false });
|
|
@@ -22529,7 +22741,7 @@ async function handleSequenceCapture(outputDir, options) {
|
|
|
22529
22741
|
while (!stopping) {
|
|
22530
22742
|
frameCount++;
|
|
22531
22743
|
const filename = formatFrameFilename(frameCount, format);
|
|
22532
|
-
const outputPath =
|
|
22744
|
+
const outputPath = path8.join(absoluteDir, filename);
|
|
22533
22745
|
try {
|
|
22534
22746
|
await captureSequenceFrame(outputPath, options);
|
|
22535
22747
|
} catch (error) {
|
|
@@ -22538,7 +22750,7 @@ async function handleSequenceCapture(outputDir, options) {
|
|
|
22538
22750
|
if (options.json) {
|
|
22539
22751
|
console.log(JSON.stringify(buildSuccessResponse({ frame: frameCount, path: outputPath })));
|
|
22540
22752
|
} else {
|
|
22541
|
-
|
|
22753
|
+
log29.info(`Frame ${frameCount}: ${filename}`);
|
|
22542
22754
|
}
|
|
22543
22755
|
if (limit > 0 && frameCount >= limit) break;
|
|
22544
22756
|
await delay2(interval);
|
|
@@ -22721,8 +22933,8 @@ function registerDomCommands(program2) {
|
|
|
22721
22933
|
"Image format: png or jpeg/jpg (default: from the file extension, else png)",
|
|
22722
22934
|
screenshotFormatOption
|
|
22723
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(
|
|
22724
|
-
async (
|
|
22725
|
-
await handleDomScreenshot(
|
|
22936
|
+
async (path8, target, options) => {
|
|
22937
|
+
await handleDomScreenshot(path8, target, options);
|
|
22726
22938
|
}
|
|
22727
22939
|
);
|
|
22728
22940
|
}
|
|
@@ -22730,7 +22942,7 @@ function registerDomCommands(program2) {
|
|
|
22730
22942
|
// src/commands/installSkill.ts
|
|
22731
22943
|
import { existsSync as existsSync11, mkdirSync as mkdirSync2, readFileSync as readFileSync8, renameSync as renameSync2, rmSync as rmSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
22732
22944
|
import { homedir as homedir3 } from "os";
|
|
22733
|
-
import { dirname as
|
|
22945
|
+
import { dirname as dirname7, join as join8 } from "path";
|
|
22734
22946
|
init_errors3();
|
|
22735
22947
|
init_messages();
|
|
22736
22948
|
|
|
@@ -22739,9 +22951,9 @@ init_formatting();
|
|
|
22739
22951
|
init_commands();
|
|
22740
22952
|
import { homedir as homedir2 } from "os";
|
|
22741
22953
|
var PATH_INDENT = " ".repeat(2 + 6 + 2 + 9 + 2);
|
|
22742
|
-
function tildePath(
|
|
22954
|
+
function tildePath(path8) {
|
|
22743
22955
|
const home = homedir2();
|
|
22744
|
-
return
|
|
22956
|
+
return path8.startsWith(`${home}/`) ? `~${path8.slice(home.length)}` : path8;
|
|
22745
22957
|
}
|
|
22746
22958
|
function addSkillTargets(fmt, skills) {
|
|
22747
22959
|
fmt.text("bdg skill:");
|
|
@@ -22765,11 +22977,11 @@ function formatInstalledSkills(data) {
|
|
|
22765
22977
|
init_logging();
|
|
22766
22978
|
init_errors();
|
|
22767
22979
|
init_exitCodes();
|
|
22768
|
-
var
|
|
22769
|
-
var SKILL_SOURCE_PATH =
|
|
22980
|
+
var log30 = createLogger("bdg");
|
|
22981
|
+
var SKILL_SOURCE_PATH = join8(PACKAGE_ROOT, ".claude", "skills", "bdg", "SKILL.md");
|
|
22770
22982
|
var SKILL_ROOTS = {
|
|
22771
|
-
claude:
|
|
22772
|
-
agents:
|
|
22983
|
+
claude: join8(".claude", "skills"),
|
|
22984
|
+
agents: join8(".agents", "skills")
|
|
22773
22985
|
};
|
|
22774
22986
|
function installSkill(targets, home = homedir3(), source = SKILL_SOURCE_PATH) {
|
|
22775
22987
|
if (!existsSync11(source)) {
|
|
@@ -22784,7 +22996,7 @@ function installSkill(targets, home = homedir3(), source = SKILL_SOURCE_PATH) {
|
|
|
22784
22996
|
const skills = [];
|
|
22785
22997
|
const failures = [];
|
|
22786
22998
|
for (const target of targets) {
|
|
22787
|
-
const result = writeSkill(target,
|
|
22999
|
+
const result = writeSkill(target, join8(home, SKILL_ROOTS[target], "bdg", "SKILL.md"), content);
|
|
22788
23000
|
if ("error" in result) failures.push(result);
|
|
22789
23001
|
else skills.push(result);
|
|
22790
23002
|
}
|
|
@@ -22793,10 +23005,10 @@ function installSkill(targets, home = homedir3(), source = SKILL_SOURCE_PATH) {
|
|
|
22793
23005
|
}
|
|
22794
23006
|
function skillFailureError(failures, targets) {
|
|
22795
23007
|
const other = failures.length === 1 ? targets.find((t) => t !== failures[0]?.target) : void 0;
|
|
22796
|
-
const errors = failures.map(({ path:
|
|
23008
|
+
const errors = failures.map(({ path: path8, error }) => {
|
|
22797
23009
|
const code = error.code;
|
|
22798
23010
|
return skillWriteFailedError(
|
|
22799
|
-
|
|
23011
|
+
path8,
|
|
22800
23012
|
getErrorMessage(error),
|
|
22801
23013
|
code === "ENOTDIR" || code === "EEXIST",
|
|
22802
23014
|
other && `--${other}`
|
|
@@ -22808,30 +23020,30 @@ function skillFailureError(failures, targets) {
|
|
|
22808
23020
|
EXIT_CODES.PERMISSION_DENIED
|
|
22809
23021
|
);
|
|
22810
23022
|
}
|
|
22811
|
-
function writeSkill(target,
|
|
22812
|
-
const existing = existsSync11(
|
|
23023
|
+
function writeSkill(target, path8, content) {
|
|
23024
|
+
const existing = existsSync11(path8) ? readFileSync8(path8, "utf-8") : void 0;
|
|
22813
23025
|
if (existing === content) {
|
|
22814
|
-
return { target, path:
|
|
23026
|
+
return { target, path: path8, status: "unchanged" };
|
|
22815
23027
|
}
|
|
22816
|
-
const backup = existing === void 0 ? void 0 : `${
|
|
22817
|
-
const temporary = `${
|
|
22818
|
-
let writing =
|
|
23028
|
+
const backup = existing === void 0 ? void 0 : `${path8}.bak`;
|
|
23029
|
+
const temporary = `${path8}.tmp`;
|
|
23030
|
+
let writing = path8;
|
|
22819
23031
|
try {
|
|
22820
|
-
mkdirSync2(
|
|
23032
|
+
mkdirSync2(dirname7(path8), { recursive: true });
|
|
22821
23033
|
writeFileSync2(temporary, content);
|
|
22822
23034
|
if (backup !== void 0) {
|
|
22823
23035
|
writing = backup;
|
|
22824
23036
|
rmSync6(backup, { force: true });
|
|
22825
23037
|
writeFileSync2(backup, existing ?? "");
|
|
22826
23038
|
}
|
|
22827
|
-
writing =
|
|
22828
|
-
renameSync2(temporary,
|
|
23039
|
+
writing = path8;
|
|
23040
|
+
renameSync2(temporary, path8);
|
|
22829
23041
|
} catch (error) {
|
|
22830
|
-
safeRemoveFile(temporary, "temporary skill copy",
|
|
23042
|
+
safeRemoveFile(temporary, "temporary skill copy", log30);
|
|
22831
23043
|
return { target, path: writing, error };
|
|
22832
23044
|
}
|
|
22833
|
-
if (backup === void 0) return { target, path:
|
|
22834
|
-
return { target, path:
|
|
23045
|
+
if (backup === void 0) return { target, path: path8, status: "installed" };
|
|
23046
|
+
return { target, path: path8, status: "updated", backup };
|
|
22835
23047
|
}
|
|
22836
23048
|
function selectedTargets(options) {
|
|
22837
23049
|
const picked = Object.keys(SKILL_ROOTS).filter((target) => options[target]);
|
|
@@ -22862,7 +23074,7 @@ function registerInstallSkillCommand(program2) {
|
|
|
22862
23074
|
|
|
22863
23075
|
// src/commands/network/har.ts
|
|
22864
23076
|
import * as fs16 from "fs";
|
|
22865
|
-
import * as
|
|
23077
|
+
import * as path7 from "path";
|
|
22866
23078
|
init_outputFile();
|
|
22867
23079
|
init_client();
|
|
22868
23080
|
init_paths();
|
|
@@ -22982,7 +23194,7 @@ function filterByResourceType(requests, types) {
|
|
|
22982
23194
|
init_logging();
|
|
22983
23195
|
init_suggestions();
|
|
22984
23196
|
init_url();
|
|
22985
|
-
var
|
|
23197
|
+
var log31 = createLogger("network");
|
|
22986
23198
|
var SIZE_UNITS = {
|
|
22987
23199
|
b: 1,
|
|
22988
23200
|
kb: 1024,
|
|
@@ -23092,7 +23304,7 @@ function validateSizeValue(value) {
|
|
|
23092
23304
|
parseSize(value);
|
|
23093
23305
|
return null;
|
|
23094
23306
|
} catch (error) {
|
|
23095
|
-
|
|
23307
|
+
log31.debug(
|
|
23096
23308
|
`Size parse failed for "${value}": ${error instanceof Error ? error.message : String(error)}`
|
|
23097
23309
|
);
|
|
23098
23310
|
return createError(
|
|
@@ -23278,27 +23490,179 @@ Multiple filters:
|
|
|
23278
23490
|
|
|
23279
23491
|
// src/telemetry/har/builder.ts
|
|
23280
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
|
|
23281
23643
|
var requireBuiltin = createRequire2(import.meta.url);
|
|
23282
23644
|
var UNKNOWN_TIMING = -1;
|
|
23283
23645
|
var DEFAULT_HTTP_VERSION = "HTTP/1.1";
|
|
23284
|
-
function buildHAR(requests, metadata) {
|
|
23285
|
-
const
|
|
23286
|
-
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);
|
|
23649
|
+
const log36 = {
|
|
23287
23650
|
version: "1.2",
|
|
23288
23651
|
creator: {
|
|
23289
23652
|
name: "bdg",
|
|
23290
23653
|
version: metadata.version,
|
|
23291
23654
|
comment: "Browser Debugger CLI - https://github.com/szymdzum/browser-debugger-cli"
|
|
23292
23655
|
},
|
|
23293
|
-
entries
|
|
23656
|
+
entries,
|
|
23657
|
+
...!options.includeSensitive && { comment: harSanitizedComment() }
|
|
23294
23658
|
};
|
|
23295
23659
|
if (metadata.chromeVersion) {
|
|
23296
|
-
|
|
23660
|
+
log36.browser = {
|
|
23297
23661
|
name: "Chrome",
|
|
23298
23662
|
version: metadata.chromeVersion
|
|
23299
23663
|
};
|
|
23300
23664
|
}
|
|
23301
|
-
return { log:
|
|
23665
|
+
return { log: log36 };
|
|
23302
23666
|
}
|
|
23303
23667
|
function buildEntry(req) {
|
|
23304
23668
|
const timings = buildTimings(req);
|
|
@@ -23509,8 +23873,8 @@ function extractRedirectURL(req) {
|
|
|
23509
23873
|
}
|
|
23510
23874
|
function estimateRequestHeadersSize(method, url, headers) {
|
|
23511
23875
|
const parsedUrl = new URL(url);
|
|
23512
|
-
const
|
|
23513
|
-
const requestLine = `${method} ${
|
|
23876
|
+
const path8 = parsedUrl.pathname + parsedUrl.search;
|
|
23877
|
+
const requestLine = `${method} ${path8} ${DEFAULT_HTTP_VERSION}\r
|
|
23514
23878
|
`;
|
|
23515
23879
|
const headersString = headers ? Object.entries(headers).map(([name, value]) => `${name}: ${value}\r
|
|
23516
23880
|
`).join("") : "";
|
|
@@ -24073,7 +24437,7 @@ function registerDocumentCommand(networkCmd) {
|
|
|
24073
24437
|
}
|
|
24074
24438
|
|
|
24075
24439
|
// src/commands/network/har.ts
|
|
24076
|
-
var
|
|
24440
|
+
var log33 = createLogger("network");
|
|
24077
24441
|
function generateHARFilename() {
|
|
24078
24442
|
const now = /* @__PURE__ */ new Date();
|
|
24079
24443
|
const year = now.getFullYear();
|
|
@@ -24083,10 +24447,10 @@ function generateHARFilename() {
|
|
|
24083
24447
|
const minutes = String(now.getMinutes()).padStart(2, "0");
|
|
24084
24448
|
const seconds = String(now.getSeconds()).padStart(2, "0");
|
|
24085
24449
|
const base = `capture-${year}-${month}-${day}-${hours}${minutes}${seconds}`;
|
|
24086
|
-
const sessionDir =
|
|
24087
|
-
let candidate =
|
|
24450
|
+
const sessionDir = path7.dirname(getSessionFilePath("OUTPUT"));
|
|
24451
|
+
let candidate = path7.join(sessionDir, `${base}.har`);
|
|
24088
24452
|
for (let n = 2; fs16.existsSync(candidate); n++) {
|
|
24089
|
-
candidate =
|
|
24453
|
+
candidate = path7.join(sessionDir, `${base}-${n}.har`);
|
|
24090
24454
|
}
|
|
24091
24455
|
return candidate;
|
|
24092
24456
|
}
|
|
@@ -24096,22 +24460,26 @@ async function getChromeVersion() {
|
|
|
24096
24460
|
const product = response.data?.result?.product;
|
|
24097
24461
|
return product?.split("/")[1];
|
|
24098
24462
|
} catch (error) {
|
|
24099
|
-
|
|
24463
|
+
log33.debug(`Could not read Chrome version: ${getErrorMessage(error)}`);
|
|
24100
24464
|
return void 0;
|
|
24101
24465
|
}
|
|
24102
24466
|
}
|
|
24103
24467
|
function formatHARExport(data) {
|
|
24104
24468
|
if ("log" in data) return JSON.stringify(data, null, 2);
|
|
24105
|
-
|
|
24106
|
-
return `\u2713 Exported ${data.entries} requests${filterNote} to ${data.file}`;
|
|
24469
|
+
return harExportedMessage(data);
|
|
24107
24470
|
}
|
|
24471
|
+
var HAR_FILE_MODE = 384;
|
|
24108
24472
|
var STDOUT_PATH = "-";
|
|
24109
24473
|
var filterDslOption = new Option(
|
|
24110
24474
|
"--filter <dsl>",
|
|
24111
24475
|
'Filter requests using DevTools DSL (e.g., "status-code:>=400")'
|
|
24112
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
|
+
);
|
|
24113
24481
|
function registerHarCommand(networkCmd) {
|
|
24114
|
-
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) => {
|
|
24115
24483
|
await runCommand(
|
|
24116
24484
|
async () => {
|
|
24117
24485
|
let requests = await getNetworkRequests();
|
|
@@ -24126,20 +24494,26 @@ function registerHarCommand(networkCmd) {
|
|
|
24126
24494
|
const outputPath = outputFile ?? generateHARFilename();
|
|
24127
24495
|
if (outputPath !== STDOUT_PATH) assertFilePath(outputPath, ".har");
|
|
24128
24496
|
const chromeVersion = await getChromeVersion();
|
|
24129
|
-
const
|
|
24130
|
-
|
|
24131
|
-
|
|
24132
|
-
|
|
24497
|
+
const includeSensitive = options.includeSensitive === true;
|
|
24498
|
+
const har = buildHAR(
|
|
24499
|
+
requests,
|
|
24500
|
+
{ version: VERSION, ...chromeVersion && { chromeVersion } },
|
|
24501
|
+
{ includeSensitive }
|
|
24502
|
+
);
|
|
24133
24503
|
if (outputPath === STDOUT_PATH) return { success: true, data: har };
|
|
24134
|
-
const file = await writeOutputFile(
|
|
24135
|
-
|
|
24136
|
-
|
|
24137
|
-
|
|
24138
|
-
|
|
24139
|
-
|
|
24140
|
-
|
|
24141
|
-
|
|
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
|
|
24142
24515
|
};
|
|
24516
|
+
return { success: true, data };
|
|
24143
24517
|
},
|
|
24144
24518
|
options,
|
|
24145
24519
|
formatHARExport
|
|
@@ -24898,13 +25272,13 @@ import * as fs17 from "fs";
|
|
|
24898
25272
|
init_paths();
|
|
24899
25273
|
init_logging();
|
|
24900
25274
|
init_directories();
|
|
24901
|
-
var
|
|
25275
|
+
var log34 = createLogger("session");
|
|
24902
25276
|
function readPortFile(portPath) {
|
|
24903
25277
|
try {
|
|
24904
25278
|
const port = parseInt(fs17.readFileSync(portPath, "utf-8").trim(), 10);
|
|
24905
25279
|
return Number.isInteger(port) && port >= 1 && port <= 65535 ? port : null;
|
|
24906
25280
|
} catch (error) {
|
|
24907
|
-
logDebugError(
|
|
25281
|
+
logDebugError(log34, `read ${portPath}`, error);
|
|
24908
25282
|
return null;
|
|
24909
25283
|
}
|
|
24910
25284
|
}
|
|
@@ -24956,7 +25330,7 @@ function selectSession(optionValue) {
|
|
|
24956
25330
|
// src/session/sessionList.ts
|
|
24957
25331
|
init_logging();
|
|
24958
25332
|
init_sessionCommand();
|
|
24959
|
-
var
|
|
25333
|
+
var log35 = createLogger("session");
|
|
24960
25334
|
function toRunningSession(name, data) {
|
|
24961
25335
|
const meta = data.sessionMetadata;
|
|
24962
25336
|
const state = meta ? "active" : data.ending ? "ending" : "starting";
|
|
@@ -24975,11 +25349,15 @@ async function describeSession(entry) {
|
|
|
24975
25349
|
const socketPath = sessionFilePathIn(dir, "DAEMON_SOCKET");
|
|
24976
25350
|
const probe = await probeDaemonSocket(socketPath);
|
|
24977
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
|
+
}
|
|
24978
25356
|
try {
|
|
24979
25357
|
const response = await getStatus(socketPath);
|
|
24980
25358
|
if (response.status === "ok" && response.data) return toRunningSession(name, response.data);
|
|
24981
25359
|
} catch (error) {
|
|
24982
|
-
logDebugError(
|
|
25360
|
+
logDebugError(log35, `get status of ${dir}`, error);
|
|
24983
25361
|
}
|
|
24984
25362
|
return { name, state: "unresponsive" };
|
|
24985
25363
|
}
|
|
@@ -25018,7 +25396,7 @@ function leftoverPort(dir) {
|
|
|
25018
25396
|
const meta = JSON.parse(fs18.readFileSync(sessionFilePathIn(dir, "METADATA"), "utf8"));
|
|
25019
25397
|
if (typeof meta.port === "number") return meta.port;
|
|
25020
25398
|
} catch (error) {
|
|
25021
|
-
logDebugError(
|
|
25399
|
+
logDebugError(log35, `read the metadata in ${dir}`, error);
|
|
25022
25400
|
}
|
|
25023
25401
|
return readPortFile(sessionFilePathIn(dir, "PORT"));
|
|
25024
25402
|
}
|
|
@@ -25031,7 +25409,7 @@ function isSameDir(a, b) {
|
|
|
25031
25409
|
const second = fs18.statSync(b);
|
|
25032
25410
|
return first.ino === second.ino && first.dev === second.dev;
|
|
25033
25411
|
} catch (error) {
|
|
25034
|
-
logDebugError(
|
|
25412
|
+
logDebugError(log35, `compare ${a} with ${b}`, error);
|
|
25035
25413
|
return false;
|
|
25036
25414
|
}
|
|
25037
25415
|
}
|
|
@@ -25090,6 +25468,15 @@ function formatSessionList(data) {
|
|
|
25090
25468
|
if (ended.length > 0) {
|
|
25091
25469
|
fmt.blank().section("Ended without bdg stop:", ended);
|
|
25092
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
|
+
}
|
|
25093
25480
|
const cleanups = data.sessions.flatMap((session) => session.cleanup ? [session.cleanup] : []);
|
|
25094
25481
|
if (cleanups.length > 0) {
|
|
25095
25482
|
fmt.hints("Crashed or stale sessions; clean up with:", cleanups);
|
|
@@ -25207,6 +25594,7 @@ function registerStatusCommand(program2) {
|
|
|
25207
25594
|
}
|
|
25208
25595
|
return { success: true, data: withSessionName(jsonOutput) };
|
|
25209
25596
|
} catch (error) {
|
|
25597
|
+
if (error instanceof CommandError) throw error;
|
|
25210
25598
|
const errorMessage = getErrorMessage(error);
|
|
25211
25599
|
if (error instanceof IPCTimeoutError) {
|
|
25212
25600
|
const err = sessionNotRespondingError(error.timeoutMs / 1e3);
|
|
@@ -25255,6 +25643,7 @@ function registerStatusCommand(program2) {
|
|
|
25255
25643
|
}
|
|
25256
25644
|
|
|
25257
25645
|
// src/commands/stop.ts
|
|
25646
|
+
init_errors3();
|
|
25258
25647
|
init_client();
|
|
25259
25648
|
init_transport();
|
|
25260
25649
|
init_formatting();
|
|
@@ -25325,7 +25714,7 @@ function registerStopCommand(program2) {
|
|
|
25325
25714
|
};
|
|
25326
25715
|
}
|
|
25327
25716
|
} catch (error) {
|
|
25328
|
-
if (error instanceof IPCTimeoutError) throw error;
|
|
25717
|
+
if (error instanceof IPCTimeoutError || error instanceof CommandError) throw error;
|
|
25329
25718
|
const errorMessage = getErrorMessage(error);
|
|
25330
25719
|
if (isDaemonNotRunningError(errorMessage)) {
|
|
25331
25720
|
return {
|