@xbrowser/cli 1.4.2 → 1.4.4
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/dist/{browser-VKZVOOVV.js → browser-DS24BWJW.js} +1 -1
- package/dist/{browser-WMQRPYXX.js → browser-KBUORWR3.js} +2 -2
- package/dist/{browser-ALV2PLW4.js → browser-YKJO3BOQ.js} +2 -2
- package/dist/{cdp-driver-JZPU5YXY.js → cdp-driver-ACRHJMS3.js} +20 -10
- package/dist/{cdp-driver-72HOBP4C.js → cdp-driver-VRXHK6P6.js} +1 -1
- package/dist/{chunk-7OGKQWXE.js → chunk-4W54GEMV.js} +1 -1
- package/dist/{chunk-TLOHV3FP.js → chunk-CFPPWKVO.js} +20 -10
- package/dist/{chunk-N4PIGZDG.js → chunk-IDJ5NILK.js} +20 -10
- package/dist/{chunk-UZQYMXR5.js → chunk-X3FKWJV4.js} +1 -1
- package/dist/cli.js +35 -10
- package/dist/daemon-main.js +28 -9
- package/dist/index.d.ts +6 -20
- package/dist/index.js +36 -11
- package/dist/{session-replayer-JIVFXVUL.js → session-replayer-F4ORJMCL.js} +1 -1
- package/package.json +2 -2
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
saveSessionDiskMeta,
|
|
21
21
|
setActivePage,
|
|
22
22
|
touchSession
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-X3FKWJV4.js";
|
|
24
|
+
import "./chunk-IDJ5NILK.js";
|
|
25
25
|
import "./chunk-TNEN6VQ2.js";
|
|
26
26
|
import "./chunk-GDKLH7ZY.js";
|
|
27
27
|
import "./chunk-ABXMBNQ6.js";
|
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
saveSessionDiskMeta,
|
|
21
21
|
setActivePage,
|
|
22
22
|
touchSession
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-4W54GEMV.js";
|
|
24
|
+
import "./chunk-IDJ5NILK.js";
|
|
25
25
|
import "./chunk-TNEN6VQ2.js";
|
|
26
26
|
import "./chunk-GDKLH7ZY.js";
|
|
27
27
|
import "./chunk-KFQGP6VL.js";
|
|
@@ -1167,11 +1167,21 @@ var XBPageImpl = class _XBPageImpl {
|
|
|
1167
1167
|
}
|
|
1168
1168
|
async goBack(opts = {}) {
|
|
1169
1169
|
await this.evaluate("() => history.back()");
|
|
1170
|
-
await this.waitForLoadState(opts.waitUntil ?? "
|
|
1170
|
+
await this.waitForLoadState(opts.waitUntil ?? "domcontentloaded", opts.timeout ?? 5e3).catch(() => {
|
|
1171
|
+
});
|
|
1172
|
+
try {
|
|
1173
|
+
this._url = await this.evaluate("location.href");
|
|
1174
|
+
} catch {
|
|
1175
|
+
}
|
|
1171
1176
|
}
|
|
1172
1177
|
async goForward(opts = {}) {
|
|
1173
1178
|
await this.evaluate("() => history.forward()");
|
|
1174
|
-
await this.waitForLoadState(opts.waitUntil ?? "
|
|
1179
|
+
await this.waitForLoadState(opts.waitUntil ?? "domcontentloaded", opts.timeout ?? 5e3).catch(() => {
|
|
1180
|
+
});
|
|
1181
|
+
try {
|
|
1182
|
+
this._url = await this.evaluate("location.href");
|
|
1183
|
+
} catch {
|
|
1184
|
+
}
|
|
1175
1185
|
}
|
|
1176
1186
|
async reload(opts = {}) {
|
|
1177
1187
|
this._loadState = { loadFired: false, domContentFired: false, networkIdle: false };
|
|
@@ -1629,16 +1639,16 @@ Last error: ${lastError.message}` : "";
|
|
|
1629
1639
|
const result = await this.conn.send("Page.getFrameTree", void 0, this.sessionId);
|
|
1630
1640
|
const frames = [];
|
|
1631
1641
|
const collect = (node) => {
|
|
1632
|
-
|
|
1642
|
+
const frame = {
|
|
1633
1643
|
name: () => node.frame.name || "",
|
|
1634
1644
|
url: () => node.frame.url,
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1645
|
+
isDetached: () => false,
|
|
1646
|
+
page: () => this,
|
|
1647
|
+
evaluate: (fn, ...args) => this.evaluate(fn, ...args),
|
|
1648
|
+
$: (sel) => this.$(sel),
|
|
1649
|
+
$$: (sel) => this.$$(sel)
|
|
1650
|
+
};
|
|
1651
|
+
frames.push(frame);
|
|
1642
1652
|
for (const child of node.childFrames || []) {
|
|
1643
1653
|
collect({ frame: child.frame, childFrames: [] });
|
|
1644
1654
|
}
|
|
@@ -1172,11 +1172,21 @@ var XBPageImpl = class _XBPageImpl {
|
|
|
1172
1172
|
}
|
|
1173
1173
|
async goBack(opts = {}) {
|
|
1174
1174
|
await this.evaluate("() => history.back()");
|
|
1175
|
-
await this.waitForLoadState(opts.waitUntil ?? "
|
|
1175
|
+
await this.waitForLoadState(opts.waitUntil ?? "domcontentloaded", opts.timeout ?? 5e3).catch(() => {
|
|
1176
|
+
});
|
|
1177
|
+
try {
|
|
1178
|
+
this._url = await this.evaluate("location.href");
|
|
1179
|
+
} catch {
|
|
1180
|
+
}
|
|
1176
1181
|
}
|
|
1177
1182
|
async goForward(opts = {}) {
|
|
1178
1183
|
await this.evaluate("() => history.forward()");
|
|
1179
|
-
await this.waitForLoadState(opts.waitUntil ?? "
|
|
1184
|
+
await this.waitForLoadState(opts.waitUntil ?? "domcontentloaded", opts.timeout ?? 5e3).catch(() => {
|
|
1185
|
+
});
|
|
1186
|
+
try {
|
|
1187
|
+
this._url = await this.evaluate("location.href");
|
|
1188
|
+
} catch {
|
|
1189
|
+
}
|
|
1180
1190
|
}
|
|
1181
1191
|
async reload(opts = {}) {
|
|
1182
1192
|
this._loadState = { loadFired: false, domContentFired: false, networkIdle: false };
|
|
@@ -1634,16 +1644,16 @@ Last error: ${lastError.message}` : "";
|
|
|
1634
1644
|
const result = await this.conn.send("Page.getFrameTree", void 0, this.sessionId);
|
|
1635
1645
|
const frames = [];
|
|
1636
1646
|
const collect = (node) => {
|
|
1637
|
-
|
|
1647
|
+
const frame = {
|
|
1638
1648
|
name: () => node.frame.name || "",
|
|
1639
1649
|
url: () => node.frame.url,
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1650
|
+
isDetached: () => false,
|
|
1651
|
+
page: () => this,
|
|
1652
|
+
evaluate: (fn, ...args) => this.evaluate(fn, ...args),
|
|
1653
|
+
$: (sel) => this.$(sel),
|
|
1654
|
+
$$: (sel) => this.$$(sel)
|
|
1655
|
+
};
|
|
1656
|
+
frames.push(frame);
|
|
1647
1657
|
for (const child of node.childFrames || []) {
|
|
1648
1658
|
collect({ frame: child.frame, childFrames: [] });
|
|
1649
1659
|
}
|
|
@@ -1166,11 +1166,21 @@ var XBPageImpl = class _XBPageImpl {
|
|
|
1166
1166
|
}
|
|
1167
1167
|
async goBack(opts = {}) {
|
|
1168
1168
|
await this.evaluate("() => history.back()");
|
|
1169
|
-
await this.waitForLoadState(opts.waitUntil ?? "
|
|
1169
|
+
await this.waitForLoadState(opts.waitUntil ?? "domcontentloaded", opts.timeout ?? 5e3).catch(() => {
|
|
1170
|
+
});
|
|
1171
|
+
try {
|
|
1172
|
+
this._url = await this.evaluate("location.href");
|
|
1173
|
+
} catch {
|
|
1174
|
+
}
|
|
1170
1175
|
}
|
|
1171
1176
|
async goForward(opts = {}) {
|
|
1172
1177
|
await this.evaluate("() => history.forward()");
|
|
1173
|
-
await this.waitForLoadState(opts.waitUntil ?? "
|
|
1178
|
+
await this.waitForLoadState(opts.waitUntil ?? "domcontentloaded", opts.timeout ?? 5e3).catch(() => {
|
|
1179
|
+
});
|
|
1180
|
+
try {
|
|
1181
|
+
this._url = await this.evaluate("location.href");
|
|
1182
|
+
} catch {
|
|
1183
|
+
}
|
|
1174
1184
|
}
|
|
1175
1185
|
async reload(opts = {}) {
|
|
1176
1186
|
this._loadState = { loadFired: false, domContentFired: false, networkIdle: false };
|
|
@@ -1628,16 +1638,16 @@ Last error: ${lastError.message}` : "";
|
|
|
1628
1638
|
const result = await this.conn.send("Page.getFrameTree", void 0, this.sessionId);
|
|
1629
1639
|
const frames = [];
|
|
1630
1640
|
const collect = (node) => {
|
|
1631
|
-
|
|
1641
|
+
const frame = {
|
|
1632
1642
|
name: () => node.frame.name || "",
|
|
1633
1643
|
url: () => node.frame.url,
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1644
|
+
isDetached: () => false,
|
|
1645
|
+
page: () => this,
|
|
1646
|
+
evaluate: (fn, ...args) => this.evaluate(fn, ...args),
|
|
1647
|
+
$: (sel) => this.$(sel),
|
|
1648
|
+
$$: (sel) => this.$$(sel)
|
|
1649
|
+
};
|
|
1650
|
+
frames.push(frame);
|
|
1641
1651
|
for (const child of node.childFrames || []) {
|
|
1642
1652
|
collect({ frame: child.frame, childFrames: [] });
|
|
1643
1653
|
}
|
package/dist/cli.js
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
resolveLaunchOpts,
|
|
26
26
|
saveSessionDiskMeta,
|
|
27
27
|
setActivePage
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-CFPPWKVO.js";
|
|
29
29
|
import "./chunk-TNEN6VQ2.js";
|
|
30
30
|
import {
|
|
31
31
|
forwardCommandLog,
|
|
@@ -376,7 +376,8 @@ var backCommand = registerCommand({
|
|
|
376
376
|
result: z.object({ url: z.string() }),
|
|
377
377
|
handler: async (_p, ctx) => {
|
|
378
378
|
await ctx.page.goBack();
|
|
379
|
-
|
|
379
|
+
const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
|
|
380
|
+
return ok({ url });
|
|
380
381
|
}
|
|
381
382
|
});
|
|
382
383
|
var forwardCommand = registerCommand({
|
|
@@ -386,7 +387,8 @@ var forwardCommand = registerCommand({
|
|
|
386
387
|
result: z.object({ url: z.string() }),
|
|
387
388
|
handler: async (_p, ctx) => {
|
|
388
389
|
await ctx.page.goForward();
|
|
389
|
-
|
|
390
|
+
const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
|
|
391
|
+
return ok({ url });
|
|
390
392
|
}
|
|
391
393
|
});
|
|
392
394
|
var refreshCommand = registerCommand({
|
|
@@ -2678,7 +2680,7 @@ async function discoverUrls(page, baseUrl, options) {
|
|
|
2678
2680
|
`);
|
|
2679
2681
|
const basePath = new URL(baseUrl).pathname;
|
|
2680
2682
|
let filtered = Array.from(allUrls).filter(
|
|
2681
|
-
(u) => isSameDomain(u, baseHostname, options.includeSubdomains ?? false) && isWithinPathScope(u, basePath)
|
|
2683
|
+
(u) => options.allowExternalLinks || isSameDomain(u, baseHostname, options.includeSubdomains ?? false) && isWithinPathScope(u, basePath)
|
|
2682
2684
|
);
|
|
2683
2685
|
filtered = deduplicateUrls(filtered);
|
|
2684
2686
|
if (options.search) {
|
|
@@ -2699,6 +2701,7 @@ var mapCommand = registerCommand({
|
|
|
2699
2701
|
search: z16.string().optional(),
|
|
2700
2702
|
sitemap: z16.enum(["include", "only"]).optional(),
|
|
2701
2703
|
includeSubdomains: z16.boolean().optional(),
|
|
2704
|
+
allowExternalLinks: z16.boolean().optional().describe("Include links to external domains"),
|
|
2702
2705
|
limit: z16.number().optional(),
|
|
2703
2706
|
verbose: z16.boolean().default(false).describe("Show progress feedback")
|
|
2704
2707
|
}),
|
|
@@ -2708,6 +2711,7 @@ var mapCommand = registerCommand({
|
|
|
2708
2711
|
const links = await discoverUrls(page, p.url, {
|
|
2709
2712
|
sitemap: p.sitemap,
|
|
2710
2713
|
includeSubdomains: p.includeSubdomains,
|
|
2714
|
+
allowExternalLinks: p.allowExternalLinks,
|
|
2711
2715
|
limit: p.limit,
|
|
2712
2716
|
search: p.search,
|
|
2713
2717
|
verbose: p.verbose
|
|
@@ -4892,8 +4896,23 @@ async function waitForPage(page, input) {
|
|
|
4892
4896
|
return { success: true, matched: "selector", timeout, elapsed: Date.now() - startedAt };
|
|
4893
4897
|
}
|
|
4894
4898
|
if (input.text) {
|
|
4895
|
-
|
|
4896
|
-
|
|
4899
|
+
const textToFind = input.text;
|
|
4900
|
+
const matched = await pollUntil(timeout, pollInterval, async () => {
|
|
4901
|
+
return page.evaluate((text) => {
|
|
4902
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
|
4903
|
+
while (walker.nextNode()) {
|
|
4904
|
+
if (walker.currentNode.textContent?.includes(text)) return true;
|
|
4905
|
+
}
|
|
4906
|
+
return false;
|
|
4907
|
+
}, textToFind);
|
|
4908
|
+
});
|
|
4909
|
+
return {
|
|
4910
|
+
success: matched,
|
|
4911
|
+
matched: "text",
|
|
4912
|
+
timeout,
|
|
4913
|
+
elapsed: Date.now() - startedAt,
|
|
4914
|
+
...matched ? {} : { message: `Timed out waiting for text: ${input.text}` }
|
|
4915
|
+
};
|
|
4897
4916
|
}
|
|
4898
4917
|
if (input.url) {
|
|
4899
4918
|
const matched = await pollUntil(timeout, pollInterval, async () => matchUrlPattern(page.url(), input.url));
|
|
@@ -7058,7 +7077,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7058
7077
|
}
|
|
7059
7078
|
let targetPageOverride = null;
|
|
7060
7079
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7061
|
-
const { findTargetPage } = await import("./browser-
|
|
7080
|
+
const { findTargetPage } = await import("./browser-DS24BWJW.js");
|
|
7062
7081
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7063
7082
|
if (!targetPageOverride) {
|
|
7064
7083
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -10009,6 +10028,12 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10009
10028
|
};
|
|
10010
10029
|
break;
|
|
10011
10030
|
}
|
|
10031
|
+
case "waitForTimeout":
|
|
10032
|
+
cmdName = "waitForTimeout";
|
|
10033
|
+
params = {
|
|
10034
|
+
timeout: args[0] ? Number(args[0]) : options.timeout ? Number(options.timeout) : 1e3
|
|
10035
|
+
};
|
|
10036
|
+
break;
|
|
10012
10037
|
default:
|
|
10013
10038
|
cmdName = command;
|
|
10014
10039
|
params = { ...options };
|
|
@@ -12408,7 +12433,7 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12408
12433
|
const possibleCmd = argv[0].substring(0, spaceIdx);
|
|
12409
12434
|
if (/^[a-zA-Z][\w-]*$/.test(possibleCmd)) {
|
|
12410
12435
|
const remainder = argv[0].substring(spaceIdx + 1);
|
|
12411
|
-
if (remainder.
|
|
12436
|
+
if (remainder.includes("--")) {
|
|
12412
12437
|
const remainderParts = remainder.split(/\s+/).filter(Boolean);
|
|
12413
12438
|
argv = [possibleCmd, ...remainderParts, ...argv.slice(1)];
|
|
12414
12439
|
} else {
|
|
@@ -12427,7 +12452,7 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12427
12452
|
const mode = options.json ? "json" : options.yaml ? "yaml" : "text";
|
|
12428
12453
|
const sessionName = options.session || process.env.XBROWSER_SESSION || "default";
|
|
12429
12454
|
const cdpEndpoint = options.cdp || process.env.XBROWSER_CDP;
|
|
12430
|
-
if (options.version
|
|
12455
|
+
if (options.version) {
|
|
12431
12456
|
console.log(`xbrowser v${version}`);
|
|
12432
12457
|
return;
|
|
12433
12458
|
}
|
|
@@ -13024,7 +13049,7 @@ async function main() {
|
|
|
13024
13049
|
const command = process.argv[2];
|
|
13025
13050
|
const isLongRunning = command === "preview" || command === "serve";
|
|
13026
13051
|
if (!isLongRunning) {
|
|
13027
|
-
const { ensureProcessCanExit } = await import("./browser-
|
|
13052
|
+
const { ensureProcessCanExit } = await import("./browser-DS24BWJW.js");
|
|
13028
13053
|
await ensureProcessCanExit().catch(() => {
|
|
13029
13054
|
});
|
|
13030
13055
|
process.exit(exitCode);
|
package/dist/daemon-main.js
CHANGED
|
@@ -21,8 +21,8 @@ import {
|
|
|
21
21
|
resolveLaunchOpts,
|
|
22
22
|
saveSessionDiskMeta,
|
|
23
23
|
setActivePage
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import "./chunk-
|
|
24
|
+
} from "./chunk-4W54GEMV.js";
|
|
25
|
+
import "./chunk-IDJ5NILK.js";
|
|
26
26
|
import "./chunk-TNEN6VQ2.js";
|
|
27
27
|
import {
|
|
28
28
|
getPluginLoader
|
|
@@ -337,7 +337,8 @@ var backCommand = registerCommand({
|
|
|
337
337
|
result: z.object({ url: z.string() }),
|
|
338
338
|
handler: async (_p, ctx) => {
|
|
339
339
|
await ctx.page.goBack();
|
|
340
|
-
|
|
340
|
+
const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
|
|
341
|
+
return ok({ url });
|
|
341
342
|
}
|
|
342
343
|
});
|
|
343
344
|
var forwardCommand = registerCommand({
|
|
@@ -347,7 +348,8 @@ var forwardCommand = registerCommand({
|
|
|
347
348
|
result: z.object({ url: z.string() }),
|
|
348
349
|
handler: async (_p, ctx) => {
|
|
349
350
|
await ctx.page.goForward();
|
|
350
|
-
|
|
351
|
+
const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
|
|
352
|
+
return ok({ url });
|
|
351
353
|
}
|
|
352
354
|
});
|
|
353
355
|
var refreshCommand = registerCommand({
|
|
@@ -2639,7 +2641,7 @@ async function discoverUrls(page, baseUrl, options) {
|
|
|
2639
2641
|
`);
|
|
2640
2642
|
const basePath = new URL(baseUrl).pathname;
|
|
2641
2643
|
let filtered = Array.from(allUrls).filter(
|
|
2642
|
-
(u) => isSameDomain(u, baseHostname, options.includeSubdomains ?? false) && isWithinPathScope(u, basePath)
|
|
2644
|
+
(u) => options.allowExternalLinks || isSameDomain(u, baseHostname, options.includeSubdomains ?? false) && isWithinPathScope(u, basePath)
|
|
2643
2645
|
);
|
|
2644
2646
|
filtered = deduplicateUrls(filtered);
|
|
2645
2647
|
if (options.search) {
|
|
@@ -2660,6 +2662,7 @@ var mapCommand = registerCommand({
|
|
|
2660
2662
|
search: z16.string().optional(),
|
|
2661
2663
|
sitemap: z16.enum(["include", "only"]).optional(),
|
|
2662
2664
|
includeSubdomains: z16.boolean().optional(),
|
|
2665
|
+
allowExternalLinks: z16.boolean().optional().describe("Include links to external domains"),
|
|
2663
2666
|
limit: z16.number().optional(),
|
|
2664
2667
|
verbose: z16.boolean().default(false).describe("Show progress feedback")
|
|
2665
2668
|
}),
|
|
@@ -2669,6 +2672,7 @@ var mapCommand = registerCommand({
|
|
|
2669
2672
|
const links = await discoverUrls(page, p.url, {
|
|
2670
2673
|
sitemap: p.sitemap,
|
|
2671
2674
|
includeSubdomains: p.includeSubdomains,
|
|
2675
|
+
allowExternalLinks: p.allowExternalLinks,
|
|
2672
2676
|
limit: p.limit,
|
|
2673
2677
|
search: p.search,
|
|
2674
2678
|
verbose: p.verbose
|
|
@@ -4853,8 +4857,23 @@ async function waitForPage(page, input) {
|
|
|
4853
4857
|
return { success: true, matched: "selector", timeout, elapsed: Date.now() - startedAt };
|
|
4854
4858
|
}
|
|
4855
4859
|
if (input.text) {
|
|
4856
|
-
|
|
4857
|
-
|
|
4860
|
+
const textToFind = input.text;
|
|
4861
|
+
const matched = await pollUntil(timeout, pollInterval, async () => {
|
|
4862
|
+
return page.evaluate((text) => {
|
|
4863
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
|
4864
|
+
while (walker.nextNode()) {
|
|
4865
|
+
if (walker.currentNode.textContent?.includes(text)) return true;
|
|
4866
|
+
}
|
|
4867
|
+
return false;
|
|
4868
|
+
}, textToFind);
|
|
4869
|
+
});
|
|
4870
|
+
return {
|
|
4871
|
+
success: matched,
|
|
4872
|
+
matched: "text",
|
|
4873
|
+
timeout,
|
|
4874
|
+
elapsed: Date.now() - startedAt,
|
|
4875
|
+
...matched ? {} : { message: `Timed out waiting for text: ${input.text}` }
|
|
4876
|
+
};
|
|
4858
4877
|
}
|
|
4859
4878
|
if (input.url) {
|
|
4860
4879
|
const matched = await pollUntil(timeout, pollInterval, async () => matchUrlPattern(page.url(), input.url));
|
|
@@ -6589,7 +6608,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6589
6608
|
}
|
|
6590
6609
|
let targetPageOverride = null;
|
|
6591
6610
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
6592
|
-
const { findTargetPage } = await import("./browser-
|
|
6611
|
+
const { findTargetPage } = await import("./browser-YKJO3BOQ.js");
|
|
6593
6612
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
6594
6613
|
if (!targetPageOverride) {
|
|
6595
6614
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -8305,7 +8324,7 @@ function createRPCHandler() {
|
|
|
8305
8324
|
const isNewFormat = Array.isArray(parsed.actions);
|
|
8306
8325
|
if (isNewFormat) {
|
|
8307
8326
|
try {
|
|
8308
|
-
const { SessionReplayer } = await import("./session-replayer-
|
|
8327
|
+
const { SessionReplayer } = await import("./session-replayer-F4ORJMCL.js");
|
|
8309
8328
|
const replayer = new SessionReplayer({
|
|
8310
8329
|
page: session.page,
|
|
8311
8330
|
stepDelay: slowMo * 500,
|
package/dist/index.d.ts
CHANGED
|
@@ -172,6 +172,7 @@ interface XBPage {
|
|
|
172
172
|
height: number;
|
|
173
173
|
}): Promise<void>;
|
|
174
174
|
addInitScript(script: string): Promise<void>;
|
|
175
|
+
discoverFrames(): Promise<XBFrame[]>;
|
|
175
176
|
bringToFront(): Promise<void>;
|
|
176
177
|
setExtraHTTPHeaders(headers: Record<string, string>): Promise<void>;
|
|
177
178
|
setOfflineMode(offline: boolean): Promise<void>;
|
|
@@ -1606,6 +1607,10 @@ declare class RecorderController {
|
|
|
1606
1607
|
private getDefaultOutputPath;
|
|
1607
1608
|
}
|
|
1608
1609
|
|
|
1610
|
+
/**
|
|
1611
|
+
* Recording-related type definitions.
|
|
1612
|
+
* Extracted from session-recorder.ts to keep the main file focused on logic.
|
|
1613
|
+
*/
|
|
1609
1614
|
interface ClickContextItem {
|
|
1610
1615
|
text: string;
|
|
1611
1616
|
tag?: string;
|
|
@@ -1650,17 +1655,13 @@ interface UserAction {
|
|
|
1650
1655
|
tag: string;
|
|
1651
1656
|
selector?: string;
|
|
1652
1657
|
text: string;
|
|
1653
|
-
/** Which strategy from generateUniqueSelector produced this selector */
|
|
1654
1658
|
strategy?: string;
|
|
1655
|
-
/** Reliability rating from generateUniqueSelector: high / medium / low */
|
|
1656
1659
|
confidence?: 'high' | 'medium' | 'low';
|
|
1657
|
-
/** Text-based fallback for low-confidence selectors (e.g. menu items) */
|
|
1658
1660
|
textFallback?: {
|
|
1659
1661
|
type: 'text';
|
|
1660
1662
|
value: string;
|
|
1661
1663
|
selector: string;
|
|
1662
1664
|
};
|
|
1663
|
-
/** Popup/menu context when element is inside a dropdown */
|
|
1664
1665
|
popup?: {
|
|
1665
1666
|
containerSelector: string;
|
|
1666
1667
|
containerText: string;
|
|
@@ -1677,9 +1678,7 @@ interface UserAction {
|
|
|
1677
1678
|
y?: number;
|
|
1678
1679
|
scrollX?: number;
|
|
1679
1680
|
scrollY?: number;
|
|
1680
|
-
/** Click context: popover/dropdown/menu items captured 200ms after click */
|
|
1681
1681
|
clickContext?: ClickContext;
|
|
1682
|
-
/** File upload info (type=filechooser only) */
|
|
1683
1682
|
files?: {
|
|
1684
1683
|
names: string[];
|
|
1685
1684
|
count: number;
|
|
@@ -1691,36 +1690,30 @@ interface UserAction {
|
|
|
1691
1690
|
dataUrl: string | null;
|
|
1692
1691
|
}>;
|
|
1693
1692
|
};
|
|
1694
|
-
/** Drag & drop info (type=drag only) */
|
|
1695
1693
|
drag?: {
|
|
1696
1694
|
fromX: number;
|
|
1697
1695
|
fromY: number;
|
|
1698
1696
|
toX: number;
|
|
1699
1697
|
toY: number;
|
|
1700
|
-
/** Source element description */
|
|
1701
1698
|
source?: {
|
|
1702
1699
|
tag: string;
|
|
1703
1700
|
selector?: string;
|
|
1704
1701
|
text: string;
|
|
1705
1702
|
};
|
|
1706
|
-
/** Target (drop zone) element description */
|
|
1707
1703
|
target?: {
|
|
1708
1704
|
tag: string;
|
|
1709
1705
|
selector?: string;
|
|
1710
1706
|
text: string;
|
|
1711
1707
|
};
|
|
1712
1708
|
};
|
|
1713
|
-
/** Resize info (type=resize only) */
|
|
1714
1709
|
resize?: {
|
|
1715
1710
|
width: number;
|
|
1716
1711
|
height: number;
|
|
1717
1712
|
};
|
|
1718
|
-
/** Clipboard info (type=clipboard only) */
|
|
1719
1713
|
clipboard?: {
|
|
1720
1714
|
operation: 'copy' | 'paste' | 'cut';
|
|
1721
1715
|
textPreview?: string;
|
|
1722
1716
|
};
|
|
1723
|
-
/** Touch info (type=touch only) */
|
|
1724
1717
|
touch?: {
|
|
1725
1718
|
touchType: 'start' | 'move' | 'end';
|
|
1726
1719
|
touches: Array<{
|
|
@@ -1728,26 +1721,19 @@ interface UserAction {
|
|
|
1728
1721
|
y: number;
|
|
1729
1722
|
}>;
|
|
1730
1723
|
};
|
|
1731
|
-
/** Focus info (type=focus only) */
|
|
1732
1724
|
focus?: {
|
|
1733
1725
|
focusType: 'focus' | 'blur';
|
|
1734
1726
|
};
|
|
1735
|
-
/** Visibility info (type=visibility only) */
|
|
1736
1727
|
visibility?: {
|
|
1737
1728
|
state: 'visible' | 'hidden';
|
|
1738
1729
|
};
|
|
1739
|
-
/** Mouse trajectory from previous action's position to this action's position.
|
|
1740
|
-
* Captured as simplified waypoints with relative timestamps for realistic replay. */
|
|
1741
1730
|
trajectory?: {
|
|
1742
|
-
/** Waypoints: [x, y, deltaMs from previous point] */
|
|
1743
1731
|
points: Array<{
|
|
1744
1732
|
x: number;
|
|
1745
1733
|
y: number;
|
|
1746
1734
|
dt: number;
|
|
1747
1735
|
}>;
|
|
1748
|
-
/** Total distance in pixels (approximate) */
|
|
1749
1736
|
distance: number;
|
|
1750
|
-
/** Total duration in ms */
|
|
1751
1737
|
duration: number;
|
|
1752
1738
|
};
|
|
1753
1739
|
}
|
|
@@ -1825,13 +1811,13 @@ interface RecordingData {
|
|
|
1825
1811
|
contextChanges: ContextChange[];
|
|
1826
1812
|
checkpoints: CheckpointEntry[];
|
|
1827
1813
|
}
|
|
1828
|
-
/** Written to disk so `record stop` (separate process) can signal the recorder. */
|
|
1829
1814
|
interface RecordingControlFile {
|
|
1830
1815
|
pid: number;
|
|
1831
1816
|
startedAt: string;
|
|
1832
1817
|
startUrl: string;
|
|
1833
1818
|
sessionName: string;
|
|
1834
1819
|
}
|
|
1820
|
+
|
|
1835
1821
|
declare class SessionRecorder {
|
|
1836
1822
|
private context;
|
|
1837
1823
|
private page;
|
package/dist/index.js
CHANGED
|
@@ -81,8 +81,8 @@ import {
|
|
|
81
81
|
resolveLaunchOpts,
|
|
82
82
|
saveSessionDiskMeta,
|
|
83
83
|
setActivePage
|
|
84
|
-
} from "./chunk-
|
|
85
|
-
import "./chunk-
|
|
84
|
+
} from "./chunk-X3FKWJV4.js";
|
|
85
|
+
import "./chunk-IDJ5NILK.js";
|
|
86
86
|
import "./chunk-TNEN6VQ2.js";
|
|
87
87
|
import {
|
|
88
88
|
errMsg
|
|
@@ -416,7 +416,8 @@ var backCommand = registerCommand({
|
|
|
416
416
|
result: z.object({ url: z.string() }),
|
|
417
417
|
handler: async (_p, ctx) => {
|
|
418
418
|
await ctx.page.goBack();
|
|
419
|
-
|
|
419
|
+
const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
|
|
420
|
+
return ok({ url });
|
|
420
421
|
}
|
|
421
422
|
});
|
|
422
423
|
var forwardCommand = registerCommand({
|
|
@@ -426,7 +427,8 @@ var forwardCommand = registerCommand({
|
|
|
426
427
|
result: z.object({ url: z.string() }),
|
|
427
428
|
handler: async (_p, ctx) => {
|
|
428
429
|
await ctx.page.goForward();
|
|
429
|
-
|
|
430
|
+
const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
|
|
431
|
+
return ok({ url });
|
|
430
432
|
}
|
|
431
433
|
});
|
|
432
434
|
var refreshCommand = registerCommand({
|
|
@@ -2718,7 +2720,7 @@ async function discoverUrls(page, baseUrl, options) {
|
|
|
2718
2720
|
`);
|
|
2719
2721
|
const basePath = new URL(baseUrl).pathname;
|
|
2720
2722
|
let filtered = Array.from(allUrls).filter(
|
|
2721
|
-
(u) => isSameDomain(u, baseHostname, options.includeSubdomains ?? false) && isWithinPathScope(u, basePath)
|
|
2723
|
+
(u) => options.allowExternalLinks || isSameDomain(u, baseHostname, options.includeSubdomains ?? false) && isWithinPathScope(u, basePath)
|
|
2722
2724
|
);
|
|
2723
2725
|
filtered = deduplicateUrls(filtered);
|
|
2724
2726
|
if (options.search) {
|
|
@@ -2739,6 +2741,7 @@ var mapCommand = registerCommand({
|
|
|
2739
2741
|
search: z16.string().optional(),
|
|
2740
2742
|
sitemap: z16.enum(["include", "only"]).optional(),
|
|
2741
2743
|
includeSubdomains: z16.boolean().optional(),
|
|
2744
|
+
allowExternalLinks: z16.boolean().optional().describe("Include links to external domains"),
|
|
2742
2745
|
limit: z16.number().optional(),
|
|
2743
2746
|
verbose: z16.boolean().default(false).describe("Show progress feedback")
|
|
2744
2747
|
}),
|
|
@@ -2748,6 +2751,7 @@ var mapCommand = registerCommand({
|
|
|
2748
2751
|
const links = await discoverUrls(page, p.url, {
|
|
2749
2752
|
sitemap: p.sitemap,
|
|
2750
2753
|
includeSubdomains: p.includeSubdomains,
|
|
2754
|
+
allowExternalLinks: p.allowExternalLinks,
|
|
2751
2755
|
limit: p.limit,
|
|
2752
2756
|
search: p.search,
|
|
2753
2757
|
verbose: p.verbose
|
|
@@ -5209,8 +5213,23 @@ async function waitForPage(page, input) {
|
|
|
5209
5213
|
return { success: true, matched: "selector", timeout, elapsed: Date.now() - startedAt };
|
|
5210
5214
|
}
|
|
5211
5215
|
if (input.text) {
|
|
5212
|
-
|
|
5213
|
-
|
|
5216
|
+
const textToFind = input.text;
|
|
5217
|
+
const matched = await pollUntil(timeout, pollInterval, async () => {
|
|
5218
|
+
return page.evaluate((text) => {
|
|
5219
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
|
5220
|
+
while (walker.nextNode()) {
|
|
5221
|
+
if (walker.currentNode.textContent?.includes(text)) return true;
|
|
5222
|
+
}
|
|
5223
|
+
return false;
|
|
5224
|
+
}, textToFind);
|
|
5225
|
+
});
|
|
5226
|
+
return {
|
|
5227
|
+
success: matched,
|
|
5228
|
+
matched: "text",
|
|
5229
|
+
timeout,
|
|
5230
|
+
elapsed: Date.now() - startedAt,
|
|
5231
|
+
...matched ? {} : { message: `Timed out waiting for text: ${input.text}` }
|
|
5232
|
+
};
|
|
5214
5233
|
}
|
|
5215
5234
|
if (input.url) {
|
|
5216
5235
|
const matched = await pollUntil(timeout, pollInterval, async () => matchUrlPattern(page.url(), input.url));
|
|
@@ -7378,7 +7397,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7378
7397
|
}
|
|
7379
7398
|
let targetPageOverride = null;
|
|
7380
7399
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7381
|
-
const { findTargetPage } = await import("./browser-
|
|
7400
|
+
const { findTargetPage } = await import("./browser-KBUORWR3.js");
|
|
7382
7401
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7383
7402
|
if (!targetPageOverride) {
|
|
7384
7403
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -10349,6 +10368,12 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10349
10368
|
};
|
|
10350
10369
|
break;
|
|
10351
10370
|
}
|
|
10371
|
+
case "waitForTimeout":
|
|
10372
|
+
cmdName = "waitForTimeout";
|
|
10373
|
+
params = {
|
|
10374
|
+
timeout: args[0] ? Number(args[0]) : options.timeout ? Number(options.timeout) : 1e3
|
|
10375
|
+
};
|
|
10376
|
+
break;
|
|
10352
10377
|
default:
|
|
10353
10378
|
cmdName = command;
|
|
10354
10379
|
params = { ...options };
|
|
@@ -12748,7 +12773,7 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12748
12773
|
const possibleCmd = argv[0].substring(0, spaceIdx);
|
|
12749
12774
|
if (/^[a-zA-Z][\w-]*$/.test(possibleCmd)) {
|
|
12750
12775
|
const remainder = argv[0].substring(spaceIdx + 1);
|
|
12751
|
-
if (remainder.
|
|
12776
|
+
if (remainder.includes("--")) {
|
|
12752
12777
|
const remainderParts = remainder.split(/\s+/).filter(Boolean);
|
|
12753
12778
|
argv = [possibleCmd, ...remainderParts, ...argv.slice(1)];
|
|
12754
12779
|
} else {
|
|
@@ -12767,7 +12792,7 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12767
12792
|
const mode = options.json ? "json" : options.yaml ? "yaml" : "text";
|
|
12768
12793
|
const sessionName = options.session || process.env.XBROWSER_SESSION || "default";
|
|
12769
12794
|
const cdpEndpoint = options.cdp || process.env.XBROWSER_CDP;
|
|
12770
|
-
if (options.version
|
|
12795
|
+
if (options.version) {
|
|
12771
12796
|
console.log(`xbrowser v${version}`);
|
|
12772
12797
|
return;
|
|
12773
12798
|
}
|
|
@@ -16151,7 +16176,7 @@ var DataCollector = class {
|
|
|
16151
16176
|
return results;
|
|
16152
16177
|
}
|
|
16153
16178
|
async createBrowserContext() {
|
|
16154
|
-
const { launch } = await import("./cdp-driver-
|
|
16179
|
+
const { launch } = await import("./cdp-driver-VRXHK6P6.js");
|
|
16155
16180
|
const { browser } = await launch({
|
|
16156
16181
|
headless: true,
|
|
16157
16182
|
args: ["--no-sandbox", "--disable-setuid-sandbox"]
|
|
@@ -31,7 +31,7 @@ var SessionReplayer = class {
|
|
|
31
31
|
if (this.opts.page) {
|
|
32
32
|
this.page = this.opts.page;
|
|
33
33
|
} else if (this.opts.cdpUrl) {
|
|
34
|
-
const { launch } = await import("./cdp-driver-
|
|
34
|
+
const { launch } = await import("./cdp-driver-VRXHK6P6.js");
|
|
35
35
|
const { browser } = await launch({ cdpEndpoint: this.opts.cdpUrl });
|
|
36
36
|
let contexts = browser.contexts();
|
|
37
37
|
for (let i = 0; i < 10 && contexts.length === 0; i++) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xbrowser/cli",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
4
4
|
"description": "Browser automation CLI for web scraping, headless browsing, SEO analysis, and AI agent workflows. A command-line alternative to Playwright, Puppeteer, and Selenium.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -122,6 +122,6 @@
|
|
|
122
122
|
]
|
|
123
123
|
},
|
|
124
124
|
"optionalDependencies": {
|
|
125
|
-
"undici": "^7.
|
|
125
|
+
"undici": "^7.28.0"
|
|
126
126
|
}
|
|
127
127
|
}
|