@xbrowser/cli 1.1.1 → 1.2.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/README.md +5 -3
- package/dist/{browser-BX4HZOUT.js → browser-CWI6BXYK.js} +2 -2
- package/dist/{browser-5FR3B57B.js → browser-JP2LFPR2.js} +2 -2
- package/dist/{browser-CFHOD5GY.js → browser-PZX7PO23.js} +1 -1
- package/dist/{cdp-driver-E4I3AEJK.js → cdp-driver-RPUNQBGM.js} +124 -54
- package/dist/{cdp-driver-LKNM6OQI.js → cdp-driver-S5STYUZZ.js} +1 -1
- package/dist/{chunk-MWBVZWXA.js → chunk-6V57JME6.js} +7 -7
- package/dist/{chunk-ISOSRTTV.js → chunk-MNFOCOL6.js} +130 -60
- package/dist/{chunk-IX4JY6OO.js → chunk-QFROODUU.js} +124 -54
- package/dist/{chunk-GOKTOYWM.js → chunk-SLQR57XZ.js} +7 -7
- package/dist/cli.js +139 -23
- package/dist/daemon-main.js +67 -6
- package/dist/index.js +140 -24
- package/dist/{session-replayer-YWMSSZWC.js → session-replayer-IXLSCF5U.js} +1 -1
- package/package.json +2 -2
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-6V57JME6.js";
|
|
85
|
+
import "./chunk-QFROODUU.js";
|
|
86
86
|
import "./chunk-TNEN6VQ2.js";
|
|
87
87
|
import {
|
|
88
88
|
errMsg
|
|
@@ -840,6 +840,21 @@ var waitForSelectorDef = {
|
|
|
840
840
|
}
|
|
841
841
|
};
|
|
842
842
|
var waitCommand = registerCommand({ name: "wait", selectorParams: ["selector"], ...waitForSelectorDef });
|
|
843
|
+
var waitForTimeoutCommand = registerCommand({
|
|
844
|
+
name: "waitForTimeout",
|
|
845
|
+
description: "Wait for a specified number of milliseconds",
|
|
846
|
+
scope: "project",
|
|
847
|
+
parameters: z4.object({
|
|
848
|
+
timeout: z4.number().describe("Milliseconds to wait").default(1e3)
|
|
849
|
+
}),
|
|
850
|
+
result: z4.object({
|
|
851
|
+
waited: z4.number()
|
|
852
|
+
}),
|
|
853
|
+
handler: async (p) => {
|
|
854
|
+
await new Promise((r) => setTimeout(r, p.timeout));
|
|
855
|
+
return ok4({ waited: p.timeout });
|
|
856
|
+
}
|
|
857
|
+
});
|
|
843
858
|
|
|
844
859
|
// src/commands/scroll.ts
|
|
845
860
|
import { z as z5 } from "zod";
|
|
@@ -1705,7 +1720,7 @@ var healthCheckCommand = registerCommand({
|
|
|
1705
1720
|
issues.push({
|
|
1706
1721
|
severity: "error",
|
|
1707
1722
|
category: "links",
|
|
1708
|
-
message: `Broken link (fetch error): ${href} \u2014 ${
|
|
1723
|
+
message: `Broken link (fetch error): ${href} \u2014 ${(err instanceof Error ? err.message : String(err)) || "unknown"}`
|
|
1709
1724
|
});
|
|
1710
1725
|
}
|
|
1711
1726
|
}
|
|
@@ -2381,9 +2396,53 @@ var scrapeCommand = registerCommand({
|
|
|
2381
2396
|
}
|
|
2382
2397
|
let content;
|
|
2383
2398
|
switch (p.format) {
|
|
2384
|
-
case "markdown":
|
|
2399
|
+
case "markdown": {
|
|
2400
|
+
const tablesMd = await page.evaluate(() => {
|
|
2401
|
+
const tableSelectors = [
|
|
2402
|
+
"table",
|
|
2403
|
+
'[role="table"]',
|
|
2404
|
+
'[role="grid"]',
|
|
2405
|
+
'[class*="el-table"]',
|
|
2406
|
+
// Element UI
|
|
2407
|
+
'[class*="ant-table"]',
|
|
2408
|
+
// Ant Design
|
|
2409
|
+
'[class*="MuiTable"]',
|
|
2410
|
+
// Material UI
|
|
2411
|
+
'[class*="table"]'
|
|
2412
|
+
// Generic table-like
|
|
2413
|
+
].join(",");
|
|
2414
|
+
const tables = document.querySelectorAll(tableSelectors);
|
|
2415
|
+
if (tables.length === 0) return "";
|
|
2416
|
+
return Array.from(tables).map((table) => {
|
|
2417
|
+
const rows = table.querySelectorAll('tr, [role="row"], [class*="row"]');
|
|
2418
|
+
if (rows.length === 0) return "";
|
|
2419
|
+
const mdRows = Array.from(rows).map((row) => {
|
|
2420
|
+
const cells = row.querySelectorAll('th, td, [role="columnheader"], [role="cell"], [class*="cell"], [class*="col"]');
|
|
2421
|
+
return "| " + Array.from(cells).map((c) => {
|
|
2422
|
+
const cellText = c.innerText?.trim().replace(/\n/g, " ") || "";
|
|
2423
|
+
return cellText.replace(/\|/g, "\\|") || "";
|
|
2424
|
+
}).join(" | ") + " |";
|
|
2425
|
+
}).join("\n");
|
|
2426
|
+
const headerRow = rows[0];
|
|
2427
|
+
const headerCells = headerRow.querySelectorAll('th, [role="columnheader"], [class*="header"]');
|
|
2428
|
+
const hasHeader = headerCells.length > 0;
|
|
2429
|
+
if (hasHeader && mdRows) {
|
|
2430
|
+
const headerCount = headerCells.length;
|
|
2431
|
+
const sep = "| " + Array(headerCount).fill("---").join(" | ") + " |";
|
|
2432
|
+
return mdRows.split("\n").map((line, i) => {
|
|
2433
|
+
if (i === 0) return line + "\n" + sep;
|
|
2434
|
+
return line;
|
|
2435
|
+
}).join("\n");
|
|
2436
|
+
}
|
|
2437
|
+
return mdRows;
|
|
2438
|
+
}).join("\n\n");
|
|
2439
|
+
});
|
|
2385
2440
|
content = htmlToMarkdown(html, { onlyMainContent: p.onlyMainContent });
|
|
2441
|
+
if (tablesMd) {
|
|
2442
|
+
content = tablesMd + "\n\n" + content;
|
|
2443
|
+
}
|
|
2386
2444
|
break;
|
|
2445
|
+
}
|
|
2387
2446
|
case "html":
|
|
2388
2447
|
content = html;
|
|
2389
2448
|
break;
|
|
@@ -5600,6 +5659,7 @@ function parseCommandChain(input, options) {
|
|
|
5600
5659
|
continue;
|
|
5601
5660
|
}
|
|
5602
5661
|
if (char === ";") {
|
|
5662
|
+
lastOperator = "sequence";
|
|
5603
5663
|
flushPipeline();
|
|
5604
5664
|
continue;
|
|
5605
5665
|
}
|
|
@@ -5654,6 +5714,7 @@ registerCommandDefinition("uncheck", ["selector"]);
|
|
|
5654
5714
|
registerCommandDefinition("hover", ["selector"]);
|
|
5655
5715
|
registerCommandDefinition("dblclick", ["selector"]);
|
|
5656
5716
|
registerCommandDefinition("wait", ["selector"]);
|
|
5717
|
+
registerCommandDefinition("waitForTimeout", ["timeout"]);
|
|
5657
5718
|
registerCommandDefinition("screenshot", []);
|
|
5658
5719
|
registerCommandDefinition("eval", ["expression"]);
|
|
5659
5720
|
registerCommandDefinition("scroll", ["direction"]);
|
|
@@ -7289,7 +7350,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7289
7350
|
}
|
|
7290
7351
|
let targetPageOverride = null;
|
|
7291
7352
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7292
|
-
const { findTargetPage } = await import("./browser-
|
|
7353
|
+
const { findTargetPage } = await import("./browser-JP2LFPR2.js");
|
|
7293
7354
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7294
7355
|
if (!targetPageOverride) {
|
|
7295
7356
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -9817,7 +9878,17 @@ var SELECTOR_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
9817
9878
|
"dblclick",
|
|
9818
9879
|
"wait"
|
|
9819
9880
|
]);
|
|
9881
|
+
var CAMEL_TO_KEBAB = {
|
|
9882
|
+
getCookies: "get-cookies",
|
|
9883
|
+
setCookie: "set-cookie",
|
|
9884
|
+
clearCookies: "clear-cookies",
|
|
9885
|
+
getLocalStorage: "get-local-storage",
|
|
9886
|
+
setLocalStorage: "set-local-storage",
|
|
9887
|
+
clearLocalStorage: "clear-local-storage",
|
|
9888
|
+
setViewport: "set-viewport"
|
|
9889
|
+
};
|
|
9820
9890
|
async function handleBrowserCommand(command, args, options, sessionName, mode, cdpEndpoint) {
|
|
9891
|
+
command = CAMEL_TO_KEBAB[command] || command;
|
|
9821
9892
|
if (args.includes("--help") || args.includes("-h") || options.help || options.h) {
|
|
9822
9893
|
const cmdDef = getCommand(command);
|
|
9823
9894
|
if (cmdDef) {
|
|
@@ -9934,7 +10005,8 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
9934
10005
|
if (!args[0]) outputError(`Usage: xbrowser ${command} <url>`);
|
|
9935
10006
|
cmdName = "goto";
|
|
9936
10007
|
params = {
|
|
9937
|
-
|
|
10008
|
+
// Don't prefix if URL already has a scheme (http, file, about, data, etc.)
|
|
10009
|
+
url: /^(https?|wss?|file|about|data|chrome|blob):/i.test(args[0]) ? args[0] : /^[\w-]+(\.[\w-]+)+/.test(args[0]) || args[0].startsWith("localhost") ? "https://" + args[0] : args[0],
|
|
9938
10010
|
waitUntil: options.waitUntil,
|
|
9939
10011
|
...options.timeout ? { timeout: Number(options.timeout) } : {}
|
|
9940
10012
|
};
|
|
@@ -9946,7 +10018,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
9946
10018
|
type: options.type,
|
|
9947
10019
|
selector: options.selector || options.s,
|
|
9948
10020
|
base64: !!options.base64,
|
|
9949
|
-
output: options.output || options.o
|
|
10021
|
+
output: options.output || options.o || args[0]
|
|
9950
10022
|
};
|
|
9951
10023
|
break;
|
|
9952
10024
|
case "eval":
|
|
@@ -9992,11 +10064,12 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
9992
10064
|
break;
|
|
9993
10065
|
}
|
|
9994
10066
|
case "mouse": {
|
|
9995
|
-
const
|
|
9996
|
-
const
|
|
9997
|
-
const
|
|
9998
|
-
const
|
|
9999
|
-
|
|
10067
|
+
const flatArgs = args.flatMap((a) => a.split(/\s+/).filter(Boolean));
|
|
10068
|
+
const action = options.action || flatArgs.find((a) => ["move", "click", "dblclick", "down", "up"].includes(a));
|
|
10069
|
+
const actionIdx = action ? flatArgs.indexOf(action) : -1;
|
|
10070
|
+
const x = options.x !== void 0 ? Number(options.x) : actionIdx >= 0 && flatArgs[actionIdx + 1] ? Number(flatArgs[actionIdx + 1]) : void 0;
|
|
10071
|
+
const y = options.y !== void 0 ? Number(options.y) : actionIdx >= 0 && flatArgs[actionIdx + 2] ? Number(flatArgs[actionIdx + 2]) : void 0;
|
|
10072
|
+
if (!action || x === void 0 || y === void 0 || isNaN(x) || isNaN(y)) {
|
|
10000
10073
|
outputError("Usage: xbrowser mouse <move|click|dblclick> <x> <y>\n xbrowser mouse --action <action> --x <x> --y <y>");
|
|
10001
10074
|
}
|
|
10002
10075
|
cmdName = "mouse";
|
|
@@ -10182,6 +10255,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10182
10255
|
outputResult(result.data, mode);
|
|
10183
10256
|
console.error(`
|
|
10184
10257
|
\u26A0\uFE0F ${hint}`);
|
|
10258
|
+
process.exit(1);
|
|
10185
10259
|
} else {
|
|
10186
10260
|
outputResult(result.data, mode);
|
|
10187
10261
|
}
|
|
@@ -10276,11 +10350,6 @@ async function handleSession(args, options, mode, _cdpEndpoint) {
|
|
|
10276
10350
|
}
|
|
10277
10351
|
|
|
10278
10352
|
// src/cli/plugin-routes.ts
|
|
10279
|
-
var pluginLoader3 = null;
|
|
10280
|
-
function getPluginLoader2() {
|
|
10281
|
-
if (!pluginLoader3) pluginLoader3 = new XBrowserPluginLoader();
|
|
10282
|
-
return pluginLoader3;
|
|
10283
|
-
}
|
|
10284
10353
|
async function buildRuntimePluginInfo() {
|
|
10285
10354
|
const loader = await getPluginLoader();
|
|
10286
10355
|
const sites = loader.getCore().loader.getSites();
|
|
@@ -10538,6 +10607,10 @@ async function handlePlugin(args, options, mode) {
|
|
|
10538
10607
|
} else {
|
|
10539
10608
|
result = await installer.installWithMarketplaceFallback(source, installOpts);
|
|
10540
10609
|
}
|
|
10610
|
+
try {
|
|
10611
|
+
await (await getPluginLoader()).reloadPlugin(result.name);
|
|
10612
|
+
} catch {
|
|
10613
|
+
}
|
|
10541
10614
|
outputResult(
|
|
10542
10615
|
{ ok: true, name: result.name, source: result.source, path: result.path },
|
|
10543
10616
|
mode
|
|
@@ -10553,6 +10626,11 @@ async function handlePlugin(args, options, mode) {
|
|
|
10553
10626
|
outputError(`Plugin "${name}" is not installed. Use 'xbrowser plugin list' to see installed plugins.`);
|
|
10554
10627
|
}
|
|
10555
10628
|
await installer.uninstall(name);
|
|
10629
|
+
try {
|
|
10630
|
+
const loader = await getPluginLoader();
|
|
10631
|
+
await loader.reloadPlugin(name);
|
|
10632
|
+
} catch {
|
|
10633
|
+
}
|
|
10556
10634
|
outputResult({ ok: true, name }, mode);
|
|
10557
10635
|
break;
|
|
10558
10636
|
}
|
|
@@ -10606,7 +10684,7 @@ Total: ${enrichedPlugins.length} plugins`);
|
|
|
10606
10684
|
case "reload": {
|
|
10607
10685
|
const name = subArgs[0];
|
|
10608
10686
|
if (!name) outputError("Usage: xbrowser plugin reload <name>");
|
|
10609
|
-
await
|
|
10687
|
+
(await getPluginLoader()).reloadPlugin(name);
|
|
10610
10688
|
outputResult({ ok: true, name }, mode);
|
|
10611
10689
|
break;
|
|
10612
10690
|
}
|
|
@@ -10990,15 +11068,20 @@ async function handleExtract(args, _mode) {
|
|
|
10990
11068
|
console.log(`
|
|
10991
11069
|
Saved LLM summary: ${outputPath}`);
|
|
10992
11070
|
}
|
|
10993
|
-
async function handleFilter(args, _mode) {
|
|
11071
|
+
async function handleFilter(args, _mode, options) {
|
|
10994
11072
|
const filePath = args[0];
|
|
10995
11073
|
const outputPath = args[1];
|
|
10996
11074
|
if (!filePath || !outputPath) {
|
|
10997
|
-
console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude
|
|
11075
|
+
console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude type1,type2]");
|
|
10998
11076
|
process.exit(1);
|
|
10999
11077
|
}
|
|
11000
11078
|
const { filterRecording: filterRecording2, parseExcludeTypes: parseExcludeTypes2 } = await import("./filter-3JQWBM5F.js");
|
|
11001
|
-
const
|
|
11079
|
+
const excludeArgs = args.slice(2).concat(
|
|
11080
|
+
Object.entries(options || {}).flatMap(
|
|
11081
|
+
([k, v]) => k.startsWith("exclude") ? [`--${k}${typeof v === "string" ? "=" + v : ""}`] : []
|
|
11082
|
+
)
|
|
11083
|
+
);
|
|
11084
|
+
const excludeTypes = parseExcludeTypes2(excludeArgs);
|
|
11002
11085
|
const result = filterRecording2(filePath, outputPath, excludeTypes);
|
|
11003
11086
|
console.log(`Filtered ${filePath} -> ${outputPath}`);
|
|
11004
11087
|
console.log(` Original: ${result.originalCount}, After: ${result.filteredCount}, Removed: ${result.removed} (${result.percentage}%)`);
|
|
@@ -12449,10 +12532,18 @@ function extractCdpFromArgv(argv) {
|
|
|
12449
12532
|
async function handleStdinMode(stdinCommands, argv) {
|
|
12450
12533
|
const chain = stdinCommands.join(" && ");
|
|
12451
12534
|
const cdpEndpoint = argv ? extractCdpFromArgv(argv) : void 0;
|
|
12452
|
-
const
|
|
12535
|
+
const sessionName = argv ? extractSessionNameFromArgv(argv) : "default";
|
|
12536
|
+
const chainResult = await executeChain(chain, { fileMode: true, cdpEndpoint, sessionName });
|
|
12453
12537
|
printChainResult(chainResult);
|
|
12454
12538
|
if (!chainResult.success) throw new Error("Command failed");
|
|
12455
12539
|
}
|
|
12540
|
+
function extractSessionNameFromArgv(argv) {
|
|
12541
|
+
for (let i = 0; i < argv.length; i++) {
|
|
12542
|
+
if (argv[i] === "--session" && argv[i + 1]) return argv[i + 1];
|
|
12543
|
+
if (typeof argv[i] === "string" && argv[i].startsWith("--session=")) return argv[i].slice(10);
|
|
12544
|
+
}
|
|
12545
|
+
return process.env.XBROWSER_SESSION || "default";
|
|
12546
|
+
}
|
|
12456
12547
|
async function handleEvalMode(argv) {
|
|
12457
12548
|
const evalCommands = parseEvalFlags(argv);
|
|
12458
12549
|
if (evalCommands.length === 0) return;
|
|
@@ -12592,6 +12683,31 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12592
12683
|
}
|
|
12593
12684
|
return;
|
|
12594
12685
|
}
|
|
12686
|
+
const SUBCOMMAND_HELP = {
|
|
12687
|
+
session: "session open|close|kill|list [--session <name>] [--cdp <endpoint>]",
|
|
12688
|
+
plugin: "plugin install|uninstall|list|reload|schema|search|info <name>",
|
|
12689
|
+
record: "record start|stop|status [--url <url>] [--name <flow>]",
|
|
12690
|
+
daemon: "daemon status [--port <port>]",
|
|
12691
|
+
replay: "replay <file> [--slow-mo <ms>] [--stop-on-error]",
|
|
12692
|
+
create: "create <name> [--template static|dynamic|login|api]",
|
|
12693
|
+
run: "run <file>",
|
|
12694
|
+
serve: "serve [--port <port>] [--token <token>]",
|
|
12695
|
+
remote: "remote <url> [command] [--token <token>]",
|
|
12696
|
+
convert: "convert <file> [--to js|py|sh]",
|
|
12697
|
+
extract: "extract <file> [--format json|yaml]",
|
|
12698
|
+
filter: "filter <file> [--include <type>] [--exclude <type>]",
|
|
12699
|
+
test: "test <name> [--cdp <endpoint>]",
|
|
12700
|
+
viewer: "viewer [--session <name>]",
|
|
12701
|
+
kill: "kill [--all]",
|
|
12702
|
+
net: "net [--cdp <endpoint>]"
|
|
12703
|
+
};
|
|
12704
|
+
const subHelp = SUBCOMMAND_HELP[command];
|
|
12705
|
+
if (subHelp) {
|
|
12706
|
+
console.log(`
|
|
12707
|
+
Usage: xbrowser ${subHelp}
|
|
12708
|
+
`);
|
|
12709
|
+
return;
|
|
12710
|
+
}
|
|
12595
12711
|
showMainHelp();
|
|
12596
12712
|
return;
|
|
12597
12713
|
}
|
|
@@ -12629,7 +12745,7 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12629
12745
|
await handleExtract(cmdArgs, mode);
|
|
12630
12746
|
break;
|
|
12631
12747
|
case "filter":
|
|
12632
|
-
await handleFilter(cmdArgs, mode);
|
|
12748
|
+
await handleFilter(cmdArgs, mode, options);
|
|
12633
12749
|
break;
|
|
12634
12750
|
case "run":
|
|
12635
12751
|
if (!cmdArgs[0]) {
|
|
@@ -15865,7 +15981,7 @@ var DataCollector = class {
|
|
|
15865
15981
|
return results;
|
|
15866
15982
|
}
|
|
15867
15983
|
async createBrowserContext() {
|
|
15868
|
-
const { launch } = await import("./cdp-driver-
|
|
15984
|
+
const { launch } = await import("./cdp-driver-S5STYUZZ.js");
|
|
15869
15985
|
const { browser } = await launch({
|
|
15870
15986
|
headless: true,
|
|
15871
15987
|
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-S5STYUZZ.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.
|
|
3
|
+
"version": "1.2.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": {
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"sharp": "^0.34.5",
|
|
97
97
|
"turndown": "^7.2.4",
|
|
98
98
|
"turndown-plugin-gfm": "^1.0.2",
|
|
99
|
-
"ws": "^8.
|
|
99
|
+
"ws": "^8.21.0",
|
|
100
100
|
"yaml": "^2.8.4",
|
|
101
101
|
"zod": "^3.24.0"
|
|
102
102
|
},
|