@xbrowser/cli 1.13.0 → 1.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/dist/{browser-T7OVLAEC.js → browser-4FKHBUBJ.js} +2 -2
- package/dist/{browser-OMGJMSBB.js → browser-JNT2I73V.js} +1 -1
- package/dist/{browser-AQ2AT2YZ.js → browser-YYMKZWTU.js} +2 -2
- package/dist/{cdp-driver-VS7Z7W7Y.js → cdp-driver-7RW4NY2X.js} +1 -1
- package/dist/{cdp-driver-ALDNOZYD.js → cdp-driver-FTGIYCFO.js} +224 -44
- package/dist/{cdp-driver-HYCLE4YP.js → cdp-driver-QX72T7SU.js} +1 -1
- package/dist/{chunk-BY5TT6WZ.js → chunk-HFP4QCZL.js} +244 -53
- package/dist/{chunk-CW7L53JE.js → chunk-K5E4CWRV.js} +1 -1
- package/dist/{chunk-R2POF2GP.js → chunk-NTTFKS6T.js} +244 -53
- package/dist/{chunk-T2CNKWPV.js → chunk-UXDGEDT7.js} +244 -53
- package/dist/{chunk-HD4FX2N5.js → chunk-XQ4KOZ37.js} +1 -1
- package/dist/cli.js +75 -21
- package/dist/daemon-main.js +38 -16
- package/dist/index.d.ts +2 -0
- package/dist/index.js +76 -21
- package/dist/{session-replayer-UVH5FGY2.js → session-replayer-DKHXF3DZ.js} +1 -1
- package/package.json +1 -1
package/dist/daemon-main.js
CHANGED
|
@@ -30,10 +30,13 @@ import {
|
|
|
30
30
|
resolveLaunchOpts,
|
|
31
31
|
saveSessionDiskMeta,
|
|
32
32
|
setActivePage
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-XQ4KOZ37.js";
|
|
34
34
|
import {
|
|
35
|
-
createRuleEngine
|
|
36
|
-
|
|
35
|
+
createRuleEngine,
|
|
36
|
+
rand,
|
|
37
|
+
sleep,
|
|
38
|
+
wheelDelta
|
|
39
|
+
} from "./chunk-UXDGEDT7.js";
|
|
37
40
|
import {
|
|
38
41
|
queryJS
|
|
39
42
|
} from "./chunk-3FWLW7FS.js";
|
|
@@ -312,7 +315,8 @@ var gotoCommand = registerCommand({
|
|
|
312
315
|
parameters: z.object({
|
|
313
316
|
url: z.string(),
|
|
314
317
|
waitUntil: z.enum(["load", "domcontentloaded", "networkidle", "commit"]).optional(),
|
|
315
|
-
timeout: z.number().optional()
|
|
318
|
+
timeout: z.number().optional(),
|
|
319
|
+
referrer: z.string().optional()
|
|
316
320
|
}),
|
|
317
321
|
result: z.object({
|
|
318
322
|
url: z.string(),
|
|
@@ -333,7 +337,9 @@ var gotoCommand = registerCommand({
|
|
|
333
337
|
try {
|
|
334
338
|
response = await ctx.page.goto(url, {
|
|
335
339
|
waitUntil: p.waitUntil || "domcontentloaded",
|
|
336
|
-
...p.timeout ? { timeout: p.timeout } : {}
|
|
340
|
+
...p.timeout ? { timeout: p.timeout } : {},
|
|
341
|
+
// referrer(d54):模拟从源页面点链接进入 —— document.referrer 非空
|
|
342
|
+
...p.referrer ? { referer: p.referrer } : {}
|
|
337
343
|
});
|
|
338
344
|
} catch (err) {
|
|
339
345
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -860,21 +866,34 @@ var scrollCommand = registerCommand({
|
|
|
860
866
|
}),
|
|
861
867
|
handler: async (p, ctx) => {
|
|
862
868
|
const distance = p.distance ?? 500;
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
right: [distance, 0],
|
|
867
|
-
left: [-distance, 0]
|
|
868
|
-
};
|
|
869
|
-
const [dx, dy] = deltas[p.direction];
|
|
869
|
+
const sign = { down: 1, up: -1, right: 1, left: -1 };
|
|
870
|
+
const vertical = p.direction === "down" || p.direction === "up";
|
|
871
|
+
const s = sign[p.direction];
|
|
870
872
|
if (p.selector) {
|
|
871
873
|
const element = ctx.page.locator(p.selector).first();
|
|
872
874
|
await element.evaluate((el, args) => {
|
|
873
875
|
const [dxx, dyy] = args;
|
|
874
876
|
el.scrollBy(dxx, dyy);
|
|
875
|
-
}, [
|
|
877
|
+
}, [vertical ? 0 : distance * s, vertical ? distance * s : 0]);
|
|
878
|
+
} else if (process.env.XBROWSER_STEALTH !== "off") {
|
|
879
|
+
let acc = 0;
|
|
880
|
+
let step = 0;
|
|
881
|
+
while (acc < distance && step < 60) {
|
|
882
|
+
const d = Math.min(wheelDelta(step), distance - acc);
|
|
883
|
+
if (d < 1) break;
|
|
884
|
+
await ctx.page.mouse.wheel(
|
|
885
|
+
vertical ? 0 : d * s,
|
|
886
|
+
vertical ? d * s : 0
|
|
887
|
+
);
|
|
888
|
+
acc += d;
|
|
889
|
+
step++;
|
|
890
|
+
await sleep(rand(16, 28));
|
|
891
|
+
}
|
|
876
892
|
} else {
|
|
877
|
-
await ctx.page.mouse.wheel(
|
|
893
|
+
await ctx.page.mouse.wheel(
|
|
894
|
+
vertical ? 0 : distance * s,
|
|
895
|
+
vertical ? distance * s : 0
|
|
896
|
+
);
|
|
878
897
|
}
|
|
879
898
|
return ok5({ direction: p.direction, distance });
|
|
880
899
|
}
|
|
@@ -7041,7 +7060,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7041
7060
|
}
|
|
7042
7061
|
let targetPageOverride = null;
|
|
7043
7062
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7044
|
-
const { findTargetPage } = await import("./browser-
|
|
7063
|
+
const { findTargetPage } = await import("./browser-YYMKZWTU.js");
|
|
7045
7064
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7046
7065
|
if (!targetPageOverride) {
|
|
7047
7066
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -7321,6 +7340,9 @@ async function executeChain(input, options) {
|
|
|
7321
7340
|
for (const pipeline of pipelines) {
|
|
7322
7341
|
const { type, pipeline: commands } = pipeline;
|
|
7323
7342
|
for (const cmdStr of commands) {
|
|
7343
|
+
if (process.env.XBROWSER_CHAIN_PACE === "human" && results.length > 0) {
|
|
7344
|
+
await new Promise((r) => setTimeout(r, 800 + Math.random() * 1700));
|
|
7345
|
+
}
|
|
7324
7346
|
const parts = splitCommand(cmdStr);
|
|
7325
7347
|
if (parts.length === 0) continue;
|
|
7326
7348
|
const cmdName = parts[0];
|
|
@@ -8857,7 +8879,7 @@ function createRPCHandler() {
|
|
|
8857
8879
|
if (isNewFormat) {
|
|
8858
8880
|
try {
|
|
8859
8881
|
const replayErrors = [];
|
|
8860
|
-
const { SessionReplayer } = await import("./session-replayer-
|
|
8882
|
+
const { SessionReplayer } = await import("./session-replayer-DKHXF3DZ.js");
|
|
8861
8883
|
const replayer = new SessionReplayer({
|
|
8862
8884
|
page: session.page,
|
|
8863
8885
|
stepDelay: slowMo * 500,
|
package/dist/index.d.ts
CHANGED
|
@@ -316,6 +316,8 @@ interface XBKeyboard {
|
|
|
316
316
|
press(key: string, opts?: {
|
|
317
317
|
delay?: number;
|
|
318
318
|
}): Promise<void>;
|
|
319
|
+
/** Navigation key with CDP 'keyDown' type (carries browser default actions) */
|
|
320
|
+
pressNav(key: string): Promise<void>;
|
|
319
321
|
down(key: string): Promise<void>;
|
|
320
322
|
up(key: string): Promise<void>;
|
|
321
323
|
type(text: string, opts?: {
|
package/dist/index.js
CHANGED
|
@@ -92,8 +92,12 @@ import {
|
|
|
92
92
|
resolveLaunchOpts,
|
|
93
93
|
saveSessionDiskMeta,
|
|
94
94
|
setActivePage
|
|
95
|
-
} from "./chunk-
|
|
96
|
-
import
|
|
95
|
+
} from "./chunk-K5E4CWRV.js";
|
|
96
|
+
import {
|
|
97
|
+
rand,
|
|
98
|
+
sleep,
|
|
99
|
+
wheelDelta
|
|
100
|
+
} from "./chunk-NTTFKS6T.js";
|
|
97
101
|
import "./chunk-TNEN6VQ2.js";
|
|
98
102
|
import {
|
|
99
103
|
errMsg
|
|
@@ -385,7 +389,8 @@ var gotoCommand = registerCommand({
|
|
|
385
389
|
parameters: z.object({
|
|
386
390
|
url: z.string(),
|
|
387
391
|
waitUntil: z.enum(["load", "domcontentloaded", "networkidle", "commit"]).optional(),
|
|
388
|
-
timeout: z.number().optional()
|
|
392
|
+
timeout: z.number().optional(),
|
|
393
|
+
referrer: z.string().optional()
|
|
389
394
|
}),
|
|
390
395
|
result: z.object({
|
|
391
396
|
url: z.string(),
|
|
@@ -406,7 +411,9 @@ var gotoCommand = registerCommand({
|
|
|
406
411
|
try {
|
|
407
412
|
response = await ctx.page.goto(url, {
|
|
408
413
|
waitUntil: p.waitUntil || "domcontentloaded",
|
|
409
|
-
...p.timeout ? { timeout: p.timeout } : {}
|
|
414
|
+
...p.timeout ? { timeout: p.timeout } : {},
|
|
415
|
+
// referrer(d54):模拟从源页面点链接进入 —— document.referrer 非空
|
|
416
|
+
...p.referrer ? { referer: p.referrer } : {}
|
|
410
417
|
});
|
|
411
418
|
} catch (err) {
|
|
412
419
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -933,21 +940,34 @@ var scrollCommand = registerCommand({
|
|
|
933
940
|
}),
|
|
934
941
|
handler: async (p, ctx) => {
|
|
935
942
|
const distance = p.distance ?? 500;
|
|
936
|
-
const
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
right: [distance, 0],
|
|
940
|
-
left: [-distance, 0]
|
|
941
|
-
};
|
|
942
|
-
const [dx, dy] = deltas[p.direction];
|
|
943
|
+
const sign = { down: 1, up: -1, right: 1, left: -1 };
|
|
944
|
+
const vertical = p.direction === "down" || p.direction === "up";
|
|
945
|
+
const s = sign[p.direction];
|
|
943
946
|
if (p.selector) {
|
|
944
947
|
const element = ctx.page.locator(p.selector).first();
|
|
945
948
|
await element.evaluate((el, args) => {
|
|
946
949
|
const [dxx, dyy] = args;
|
|
947
950
|
el.scrollBy(dxx, dyy);
|
|
948
|
-
}, [
|
|
951
|
+
}, [vertical ? 0 : distance * s, vertical ? distance * s : 0]);
|
|
952
|
+
} else if (process.env.XBROWSER_STEALTH !== "off") {
|
|
953
|
+
let acc = 0;
|
|
954
|
+
let step = 0;
|
|
955
|
+
while (acc < distance && step < 60) {
|
|
956
|
+
const d = Math.min(wheelDelta(step), distance - acc);
|
|
957
|
+
if (d < 1) break;
|
|
958
|
+
await ctx.page.mouse.wheel(
|
|
959
|
+
vertical ? 0 : d * s,
|
|
960
|
+
vertical ? d * s : 0
|
|
961
|
+
);
|
|
962
|
+
acc += d;
|
|
963
|
+
step++;
|
|
964
|
+
await sleep(rand(16, 28));
|
|
965
|
+
}
|
|
949
966
|
} else {
|
|
950
|
-
await ctx.page.mouse.wheel(
|
|
967
|
+
await ctx.page.mouse.wheel(
|
|
968
|
+
vertical ? 0 : distance * s,
|
|
969
|
+
vertical ? distance * s : 0
|
|
970
|
+
);
|
|
951
971
|
}
|
|
952
972
|
return ok5({ direction: p.direction, distance });
|
|
953
973
|
}
|
|
@@ -7873,7 +7893,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7873
7893
|
}
|
|
7874
7894
|
let targetPageOverride = null;
|
|
7875
7895
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7876
|
-
const { findTargetPage } = await import("./browser-
|
|
7896
|
+
const { findTargetPage } = await import("./browser-4FKHBUBJ.js");
|
|
7877
7897
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7878
7898
|
if (!targetPageOverride) {
|
|
7879
7899
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -8153,6 +8173,9 @@ async function executeChain(input, options) {
|
|
|
8153
8173
|
for (const pipeline of pipelines) {
|
|
8154
8174
|
const { type, pipeline: commands } = pipeline;
|
|
8155
8175
|
for (const cmdStr of commands) {
|
|
8176
|
+
if (process.env.XBROWSER_CHAIN_PACE === "human" && results.length > 0) {
|
|
8177
|
+
await new Promise((r) => setTimeout(r, 800 + Math.random() * 1700));
|
|
8178
|
+
}
|
|
8156
8179
|
const parts = splitCommand(cmdStr);
|
|
8157
8180
|
if (parts.length === 0) continue;
|
|
8158
8181
|
const cmdName = parts[0];
|
|
@@ -10561,12 +10584,33 @@ function normalizeSelector(input) {
|
|
|
10561
10584
|
|
|
10562
10585
|
// src/cli/browser-routes.ts
|
|
10563
10586
|
import { helpGenerator } from "@dyyz1993/xcli-core";
|
|
10587
|
+
function autoCompleteParams(cmdName, params, options) {
|
|
10588
|
+
try {
|
|
10589
|
+
const cmd = getCommand(cmdName);
|
|
10590
|
+
if (!cmd?.parameters) return params;
|
|
10591
|
+
const schema = asZodSchema(cmd.parameters);
|
|
10592
|
+
const shape = schema?.shape ?? schema?._def?.shape;
|
|
10593
|
+
if (!shape) return params;
|
|
10594
|
+
for (const key of Object.keys(shape)) {
|
|
10595
|
+
if (params[key] !== void 0) continue;
|
|
10596
|
+
const v = options[key];
|
|
10597
|
+
if (v === void 0 || v === true && key === "json" || key === "yaml") continue;
|
|
10598
|
+
if (typeof v === "string" && v === "") continue;
|
|
10599
|
+
params[key] = v;
|
|
10600
|
+
}
|
|
10601
|
+
return params;
|
|
10602
|
+
} catch {
|
|
10603
|
+
return params;
|
|
10604
|
+
}
|
|
10605
|
+
}
|
|
10564
10606
|
function parseSelectorFlags(args, options) {
|
|
10565
|
-
const
|
|
10566
|
-
const
|
|
10607
|
+
const rawSelector = options.s ?? options.selector;
|
|
10608
|
+
const rawValue = options.v ?? options.value;
|
|
10609
|
+
const selector = typeof rawSelector === "string" ? normalizeSelector(rawSelector) : void 0;
|
|
10610
|
+
const value = typeof rawValue === "string" ? rawValue : void 0;
|
|
10567
10611
|
const remaining = args.filter((a) => !a.startsWith("-"));
|
|
10568
10612
|
return {
|
|
10569
|
-
selector
|
|
10613
|
+
selector,
|
|
10570
10614
|
value,
|
|
10571
10615
|
remaining
|
|
10572
10616
|
};
|
|
@@ -10654,7 +10698,11 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10654
10698
|
if (!sel || !txt)
|
|
10655
10699
|
outputError("Usage: xbrowser type <selector> <text>\n xbrowser type -s <selector> -v <text>");
|
|
10656
10700
|
cmdName = "type";
|
|
10657
|
-
params = {
|
|
10701
|
+
params = {
|
|
10702
|
+
selector: sel,
|
|
10703
|
+
text: txt,
|
|
10704
|
+
...options.delay !== void 0 ? { delay: Number(options.delay) } : {}
|
|
10705
|
+
};
|
|
10658
10706
|
break;
|
|
10659
10707
|
}
|
|
10660
10708
|
case "press": {
|
|
@@ -10796,7 +10844,13 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10796
10844
|
outputError("Usage: xbrowser mouse <move|click|dblclick> <x> <y>\n xbrowser mouse --action <action> --x <x> --y <y>");
|
|
10797
10845
|
}
|
|
10798
10846
|
cmdName = "mouse";
|
|
10799
|
-
params = {
|
|
10847
|
+
params = {
|
|
10848
|
+
action,
|
|
10849
|
+
x,
|
|
10850
|
+
y,
|
|
10851
|
+
...options.button ? { button: options.button } : {},
|
|
10852
|
+
...options.steps !== void 0 ? { steps: Number(options.steps) } : {}
|
|
10853
|
+
};
|
|
10800
10854
|
break;
|
|
10801
10855
|
}
|
|
10802
10856
|
case "html":
|
|
@@ -10998,6 +11052,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10998
11052
|
if (target) {
|
|
10999
11053
|
params = { ...params, _target: target };
|
|
11000
11054
|
}
|
|
11055
|
+
params = autoCompleteParams(cmdName, params, options);
|
|
11001
11056
|
const tabIndex = options.tab;
|
|
11002
11057
|
if (tabIndex !== void 0) {
|
|
11003
11058
|
params = { ...params, _tabIndex: Number(tabIndex) };
|
|
@@ -14051,7 +14106,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
14051
14106
|
const targetPage = pages[cmdTabIndex];
|
|
14052
14107
|
await targetPage.bringToFront().catch(() => {
|
|
14053
14108
|
});
|
|
14054
|
-
const { setActivePage: setActivePage2 } = await import("./browser-
|
|
14109
|
+
const { setActivePage: setActivePage2 } = await import("./browser-4FKHBUBJ.js");
|
|
14055
14110
|
setActivePage2(session, targetPage);
|
|
14056
14111
|
}
|
|
14057
14112
|
}
|
|
@@ -17224,7 +17279,7 @@ var DataCollector = class {
|
|
|
17224
17279
|
return results;
|
|
17225
17280
|
}
|
|
17226
17281
|
async createBrowserContext() {
|
|
17227
|
-
const { launch } = await import("./cdp-driver-
|
|
17282
|
+
const { launch } = await import("./cdp-driver-QX72T7SU.js");
|
|
17228
17283
|
const { browser } = await launch({
|
|
17229
17284
|
headless: true,
|
|
17230
17285
|
args: ["--no-sandbox", "--disable-setuid-sandbox"]
|
|
@@ -34,7 +34,7 @@ var SessionReplayer = class {
|
|
|
34
34
|
if (this.opts.page) {
|
|
35
35
|
this.page = this.opts.page;
|
|
36
36
|
} else if (this.opts.cdpUrl) {
|
|
37
|
-
const { launch } = await import("./cdp-driver-
|
|
37
|
+
const { launch } = await import("./cdp-driver-7RW4NY2X.js");
|
|
38
38
|
const { browser } = await launch({ cdpEndpoint: this.opts.cdpUrl });
|
|
39
39
|
let contexts = browser.contexts();
|
|
40
40
|
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.
|
|
3
|
+
"version": "1.15.0",
|
|
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": {
|