@xbrowser/cli 1.4.6 → 1.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/{browser-SOML2EWR.js → browser-4KKT6V5S.js} +1 -1
- package/dist/{browser-ZTTS2SVT.js → browser-FKKMMB44.js} +2 -2
- package/dist/{browser-2OK26HBS.js → browser-MMIFKC36.js} +2 -2
- package/dist/{cdp-driver-UGO45HXR.js → cdp-driver-OY67R2KD.js} +1 -1
- package/dist/{cdp-driver-67B5EI4C.js → cdp-driver-VFPOTO42.js} +28 -0
- package/dist/{chunk-NQSFETTQ.js → chunk-J7LDVYWI.js} +1 -1
- package/dist/{chunk-2RHJEYWU.js → chunk-JGITVXIU.js} +28 -0
- package/dist/{chunk-RGS6ECTH.js → chunk-M4U63K6B.js} +28 -0
- package/dist/{chunk-LYVU6SD3.js → chunk-TP6RHIWG.js} +1 -1
- package/dist/cli.js +10 -8
- package/dist/daemon-main.js +7 -7
- package/dist/index.js +11 -9
- package/dist/{session-replayer-YIGRIIDD.js → session-replayer-JIH6H7WX.js} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
## 特性
|
|
11
11
|
|
|
12
|
-
- **
|
|
12
|
+
- **51 浏览器命令** — 导航、交互、查询、存储、截图,覆盖常见自动化场景
|
|
13
13
|
- **命令链** — 用 `&&`、`,`、`+`、`->`、`;` 串联多个命令,一行搞定复杂流程
|
|
14
14
|
- **管道 & Heredoc** — 支持 stdin 管道和 heredoc 批量执行
|
|
15
15
|
- **录制 / 回放** — 录制浏览器操作为 YAML,随时回放,可转换为 JS/Python/Bash 脚本
|
|
@@ -854,7 +854,7 @@ npm run validate
|
|
|
854
854
|
```
|
|
855
855
|
xbrowser/
|
|
856
856
|
├── src/
|
|
857
|
-
│ ├── commands/ #
|
|
857
|
+
│ ├── commands/ # 51 个浏览器命令定义
|
|
858
858
|
│ ├── builtins/ # CLI 内置命令(config, plugin, session, create)
|
|
859
859
|
│ ├── recorder/ # 录制引擎(录制器 + 回放器)
|
|
860
860
|
│ ├── session/ # 会话管理
|
|
@@ -883,7 +883,7 @@ xbrowser/
|
|
|
883
883
|
| **Setup** | `npm i -g` — 0 config | Install + browser download | Install + browser download | Install + WebDriver + drivers |
|
|
884
884
|
| **Web Scraping** | Built-in (`scrape`, `crawl`, `map`) | Write custom scripts | Write custom scripts | Write custom scripts |
|
|
885
885
|
| **Search** | Built-in multi-engine (`search`) | No | No | No |
|
|
886
|
-
| **Plugin Ecosystem** |
|
|
886
|
+
| **Plugin Ecosystem** | 70+ plugins | Limited | Limited | No |
|
|
887
887
|
| **No Code Required** | ✅ CLI commands | ❌ Must write JS/TS | ❌ Must write JS/TS | ❌ Must write code |
|
|
888
888
|
| **Headless Mode** | ✅ Default | ✅ | ✅ | ✅ |
|
|
889
889
|
| **Record/Replay** | ✅ Built-in (`record`/`replay`) | ✅ Codegen | ❌ | ❌ |
|
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
saveSessionDiskMeta,
|
|
21
21
|
setActivePage,
|
|
22
22
|
touchSession
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-TP6RHIWG.js";
|
|
24
|
+
import "./chunk-JGITVXIU.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-J7LDVYWI.js";
|
|
24
|
+
import "./chunk-JGITVXIU.js";
|
|
25
25
|
import "./chunk-TNEN6VQ2.js";
|
|
26
26
|
import "./chunk-GDKLH7ZY.js";
|
|
27
27
|
import "./chunk-KFQGP6VL.js";
|
|
@@ -303,6 +303,18 @@ function queryAllJS(selector) {
|
|
|
303
303
|
async function waitForActionable(page, selector, opts = {}) {
|
|
304
304
|
const timeout = opts.timeout ?? 3e4;
|
|
305
305
|
if (opts.force) {
|
|
306
|
+
if (selector.startsWith("xpath=")) {
|
|
307
|
+
const rect2 = await page.evaluate(`
|
|
308
|
+
(function() {
|
|
309
|
+
const el = ${queryJS(selector)};
|
|
310
|
+
if (!el) return null;
|
|
311
|
+
const r = el.getBoundingClientRect();
|
|
312
|
+
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
313
|
+
})()
|
|
314
|
+
`);
|
|
315
|
+
if (!rect2) throw new Error(`Element not found: ${selector}`);
|
|
316
|
+
return { nodeId: 0, rect: rect2 };
|
|
317
|
+
}
|
|
306
318
|
const nodeId = await page.querySelector(selector);
|
|
307
319
|
if (!nodeId) throw new Error(`Element not found: ${selector}`);
|
|
308
320
|
const rect = await page.getBoxModel(nodeId);
|
|
@@ -1656,6 +1668,22 @@ Last error: ${lastError.message}` : "";
|
|
|
1656
1668
|
// ── CDP helpers exposed for locator/element ─────────────────
|
|
1657
1669
|
/** Query a single element, returns CDP nodeId or 0 if not found */
|
|
1658
1670
|
async querySelector(selector) {
|
|
1671
|
+
if (selector.startsWith("xpath=")) {
|
|
1672
|
+
const found = await this.evaluate(`
|
|
1673
|
+
(() => { const el = ${queryJS(selector)}; return !!el; })()
|
|
1674
|
+
`).catch(() => false);
|
|
1675
|
+
if (!found) return 0;
|
|
1676
|
+
try {
|
|
1677
|
+
const search = await this.conn.send(
|
|
1678
|
+
"DOM.performSearch",
|
|
1679
|
+
{ query: selector.slice(6) },
|
|
1680
|
+
this.sessionId
|
|
1681
|
+
);
|
|
1682
|
+
if (search.nodeId) return search.nodeId;
|
|
1683
|
+
} catch {
|
|
1684
|
+
}
|
|
1685
|
+
return 1;
|
|
1686
|
+
}
|
|
1659
1687
|
const doc = await this.conn.send(
|
|
1660
1688
|
"DOM.getDocument",
|
|
1661
1689
|
{ depth: 0 },
|
|
@@ -302,6 +302,18 @@ function queryAllJS(selector) {
|
|
|
302
302
|
async function waitForActionable(page, selector, opts = {}) {
|
|
303
303
|
const timeout = opts.timeout ?? 3e4;
|
|
304
304
|
if (opts.force) {
|
|
305
|
+
if (selector.startsWith("xpath=")) {
|
|
306
|
+
const rect2 = await page.evaluate(`
|
|
307
|
+
(function() {
|
|
308
|
+
const el = ${queryJS(selector)};
|
|
309
|
+
if (!el) return null;
|
|
310
|
+
const r = el.getBoundingClientRect();
|
|
311
|
+
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
312
|
+
})()
|
|
313
|
+
`);
|
|
314
|
+
if (!rect2) throw new Error(`Element not found: ${selector}`);
|
|
315
|
+
return { nodeId: 0, rect: rect2 };
|
|
316
|
+
}
|
|
305
317
|
const nodeId = await page.querySelector(selector);
|
|
306
318
|
if (!nodeId) throw new Error(`Element not found: ${selector}`);
|
|
307
319
|
const rect = await page.getBoxModel(nodeId);
|
|
@@ -1655,6 +1667,22 @@ Last error: ${lastError.message}` : "";
|
|
|
1655
1667
|
// ── CDP helpers exposed for locator/element ─────────────────
|
|
1656
1668
|
/** Query a single element, returns CDP nodeId or 0 if not found */
|
|
1657
1669
|
async querySelector(selector) {
|
|
1670
|
+
if (selector.startsWith("xpath=")) {
|
|
1671
|
+
const found = await this.evaluate(`
|
|
1672
|
+
(() => { const el = ${queryJS(selector)}; return !!el; })()
|
|
1673
|
+
`).catch(() => false);
|
|
1674
|
+
if (!found) return 0;
|
|
1675
|
+
try {
|
|
1676
|
+
const search = await this.conn.send(
|
|
1677
|
+
"DOM.performSearch",
|
|
1678
|
+
{ query: selector.slice(6) },
|
|
1679
|
+
this.sessionId
|
|
1680
|
+
);
|
|
1681
|
+
if (search.nodeId) return search.nodeId;
|
|
1682
|
+
} catch {
|
|
1683
|
+
}
|
|
1684
|
+
return 1;
|
|
1685
|
+
}
|
|
1658
1686
|
const doc = await this.conn.send(
|
|
1659
1687
|
"DOM.getDocument",
|
|
1660
1688
|
{ depth: 0 },
|
|
@@ -308,6 +308,18 @@ function queryAllJS(selector) {
|
|
|
308
308
|
async function waitForActionable(page, selector, opts = {}) {
|
|
309
309
|
const timeout = opts.timeout ?? 3e4;
|
|
310
310
|
if (opts.force) {
|
|
311
|
+
if (selector.startsWith("xpath=")) {
|
|
312
|
+
const rect2 = await page.evaluate(`
|
|
313
|
+
(function() {
|
|
314
|
+
const el = ${queryJS(selector)};
|
|
315
|
+
if (!el) return null;
|
|
316
|
+
const r = el.getBoundingClientRect();
|
|
317
|
+
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
318
|
+
})()
|
|
319
|
+
`);
|
|
320
|
+
if (!rect2) throw new Error(`Element not found: ${selector}`);
|
|
321
|
+
return { nodeId: 0, rect: rect2 };
|
|
322
|
+
}
|
|
311
323
|
const nodeId = await page.querySelector(selector);
|
|
312
324
|
if (!nodeId) throw new Error(`Element not found: ${selector}`);
|
|
313
325
|
const rect = await page.getBoxModel(nodeId);
|
|
@@ -1661,6 +1673,22 @@ Last error: ${lastError.message}` : "";
|
|
|
1661
1673
|
// ── CDP helpers exposed for locator/element ─────────────────
|
|
1662
1674
|
/** Query a single element, returns CDP nodeId or 0 if not found */
|
|
1663
1675
|
async querySelector(selector) {
|
|
1676
|
+
if (selector.startsWith("xpath=")) {
|
|
1677
|
+
const found = await this.evaluate(`
|
|
1678
|
+
(() => { const el = ${queryJS(selector)}; return !!el; })()
|
|
1679
|
+
`).catch(() => false);
|
|
1680
|
+
if (!found) return 0;
|
|
1681
|
+
try {
|
|
1682
|
+
const search = await this.conn.send(
|
|
1683
|
+
"DOM.performSearch",
|
|
1684
|
+
{ query: selector.slice(6) },
|
|
1685
|
+
this.sessionId
|
|
1686
|
+
);
|
|
1687
|
+
if (search.nodeId) return search.nodeId;
|
|
1688
|
+
} catch {
|
|
1689
|
+
}
|
|
1690
|
+
return 1;
|
|
1691
|
+
}
|
|
1664
1692
|
const doc = await this.conn.send(
|
|
1665
1693
|
"DOM.getDocument",
|
|
1666
1694
|
{ depth: 0 },
|
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-M4U63K6B.js";
|
|
29
29
|
import "./chunk-TNEN6VQ2.js";
|
|
30
30
|
import {
|
|
31
31
|
forwardCommandLog,
|
|
@@ -1208,9 +1208,9 @@ var setViewportCommand = registerCommand({
|
|
|
1208
1208
|
description: "Set the viewport size and properties",
|
|
1209
1209
|
scope: "browser",
|
|
1210
1210
|
parameters: z11.object({
|
|
1211
|
-
width: z11.number(),
|
|
1212
|
-
height: z11.number(),
|
|
1213
|
-
deviceScaleFactor: z11.number().optional(),
|
|
1211
|
+
width: z11.coerce.number(),
|
|
1212
|
+
height: z11.coerce.number(),
|
|
1213
|
+
deviceScaleFactor: z11.coerce.number().optional(),
|
|
1214
1214
|
isMobile: z11.boolean().optional(),
|
|
1215
1215
|
hasTouch: z11.boolean().optional()
|
|
1216
1216
|
}),
|
|
@@ -7083,7 +7083,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7083
7083
|
}
|
|
7084
7084
|
let targetPageOverride = null;
|
|
7085
7085
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7086
|
-
const { findTargetPage } = await import("./browser-
|
|
7086
|
+
const { findTargetPage } = await import("./browser-4KKT6V5S.js");
|
|
7087
7087
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7088
7088
|
if (!targetPageOverride) {
|
|
7089
7089
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -11859,7 +11859,8 @@ function printChainResult(chainResult) {
|
|
|
11859
11859
|
}
|
|
11860
11860
|
if (step.tips?.length) {
|
|
11861
11861
|
for (const tip of step.tips) {
|
|
11862
|
-
|
|
11862
|
+
const text = typeof tip === "string" ? tip : tip.message;
|
|
11863
|
+
if (text) console.log(` \u{1F4A1} ${text}`);
|
|
11863
11864
|
}
|
|
11864
11865
|
}
|
|
11865
11866
|
} else {
|
|
@@ -12655,7 +12656,8 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12655
12656
|
}
|
|
12656
12657
|
if (step.tips?.length) {
|
|
12657
12658
|
for (const tip of step.tips) {
|
|
12658
|
-
|
|
12659
|
+
const text = typeof tip === "string" ? tip : tip.message;
|
|
12660
|
+
if (text) console.log(` \u{1F4A1} ${text}`);
|
|
12659
12661
|
}
|
|
12660
12662
|
}
|
|
12661
12663
|
} else {
|
|
@@ -13057,7 +13059,7 @@ async function main() {
|
|
|
13057
13059
|
const command = process.argv[2];
|
|
13058
13060
|
const isLongRunning = command === "preview" || command === "serve";
|
|
13059
13061
|
if (!isLongRunning) {
|
|
13060
|
-
const { ensureProcessCanExit } = await import("./browser-
|
|
13062
|
+
const { ensureProcessCanExit } = await import("./browser-4KKT6V5S.js");
|
|
13061
13063
|
await ensureProcessCanExit().catch(() => {
|
|
13062
13064
|
});
|
|
13063
13065
|
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-J7LDVYWI.js";
|
|
25
|
+
import "./chunk-JGITVXIU.js";
|
|
26
26
|
import "./chunk-TNEN6VQ2.js";
|
|
27
27
|
import {
|
|
28
28
|
getPluginLoader
|
|
@@ -1169,9 +1169,9 @@ var setViewportCommand = registerCommand({
|
|
|
1169
1169
|
description: "Set the viewport size and properties",
|
|
1170
1170
|
scope: "browser",
|
|
1171
1171
|
parameters: z11.object({
|
|
1172
|
-
width: z11.number(),
|
|
1173
|
-
height: z11.number(),
|
|
1174
|
-
deviceScaleFactor: z11.number().optional(),
|
|
1172
|
+
width: z11.coerce.number(),
|
|
1173
|
+
height: z11.coerce.number(),
|
|
1174
|
+
deviceScaleFactor: z11.coerce.number().optional(),
|
|
1175
1175
|
isMobile: z11.boolean().optional(),
|
|
1176
1176
|
hasTouch: z11.boolean().optional()
|
|
1177
1177
|
}),
|
|
@@ -6614,7 +6614,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6614
6614
|
}
|
|
6615
6615
|
let targetPageOverride = null;
|
|
6616
6616
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
6617
|
-
const { findTargetPage } = await import("./browser-
|
|
6617
|
+
const { findTargetPage } = await import("./browser-MMIFKC36.js");
|
|
6618
6618
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
6619
6619
|
if (!targetPageOverride) {
|
|
6620
6620
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -8330,7 +8330,7 @@ function createRPCHandler() {
|
|
|
8330
8330
|
const isNewFormat = Array.isArray(parsed.actions);
|
|
8331
8331
|
if (isNewFormat) {
|
|
8332
8332
|
try {
|
|
8333
|
-
const { SessionReplayer } = await import("./session-replayer-
|
|
8333
|
+
const { SessionReplayer } = await import("./session-replayer-JIH6H7WX.js");
|
|
8334
8334
|
const replayer = new SessionReplayer({
|
|
8335
8335
|
page: session.page,
|
|
8336
8336
|
stepDelay: slowMo * 500,
|
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-TP6RHIWG.js";
|
|
85
|
+
import "./chunk-JGITVXIU.js";
|
|
86
86
|
import "./chunk-TNEN6VQ2.js";
|
|
87
87
|
import {
|
|
88
88
|
errMsg
|
|
@@ -1248,9 +1248,9 @@ var setViewportCommand = registerCommand({
|
|
|
1248
1248
|
description: "Set the viewport size and properties",
|
|
1249
1249
|
scope: "browser",
|
|
1250
1250
|
parameters: z11.object({
|
|
1251
|
-
width: z11.number(),
|
|
1252
|
-
height: z11.number(),
|
|
1253
|
-
deviceScaleFactor: z11.number().optional(),
|
|
1251
|
+
width: z11.coerce.number(),
|
|
1252
|
+
height: z11.coerce.number(),
|
|
1253
|
+
deviceScaleFactor: z11.coerce.number().optional(),
|
|
1254
1254
|
isMobile: z11.boolean().optional(),
|
|
1255
1255
|
hasTouch: z11.boolean().optional()
|
|
1256
1256
|
}),
|
|
@@ -7403,7 +7403,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7403
7403
|
}
|
|
7404
7404
|
let targetPageOverride = null;
|
|
7405
7405
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7406
|
-
const { findTargetPage } = await import("./browser-
|
|
7406
|
+
const { findTargetPage } = await import("./browser-FKKMMB44.js");
|
|
7407
7407
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7408
7408
|
if (!targetPageOverride) {
|
|
7409
7409
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -12199,7 +12199,8 @@ function printChainResult(chainResult) {
|
|
|
12199
12199
|
}
|
|
12200
12200
|
if (step.tips?.length) {
|
|
12201
12201
|
for (const tip of step.tips) {
|
|
12202
|
-
|
|
12202
|
+
const text = typeof tip === "string" ? tip : tip.message;
|
|
12203
|
+
if (text) console.log(` \u{1F4A1} ${text}`);
|
|
12203
12204
|
}
|
|
12204
12205
|
}
|
|
12205
12206
|
} else {
|
|
@@ -12995,7 +12996,8 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12995
12996
|
}
|
|
12996
12997
|
if (step.tips?.length) {
|
|
12997
12998
|
for (const tip of step.tips) {
|
|
12998
|
-
|
|
12999
|
+
const text = typeof tip === "string" ? tip : tip.message;
|
|
13000
|
+
if (text) console.log(` \u{1F4A1} ${text}`);
|
|
12999
13001
|
}
|
|
13000
13002
|
}
|
|
13001
13003
|
} else {
|
|
@@ -16184,7 +16186,7 @@ var DataCollector = class {
|
|
|
16184
16186
|
return results;
|
|
16185
16187
|
}
|
|
16186
16188
|
async createBrowserContext() {
|
|
16187
|
-
const { launch } = await import("./cdp-driver-
|
|
16189
|
+
const { launch } = await import("./cdp-driver-OY67R2KD.js");
|
|
16188
16190
|
const { browser } = await launch({
|
|
16189
16191
|
headless: true,
|
|
16190
16192
|
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-OY67R2KD.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.8",
|
|
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": {
|