@xbrowser/cli 1.1.2 → 1.2.1
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-ZF4EJ3SK.js → browser-CWI6BXYK.js} +2 -2
- package/dist/{browser-H55TWH2I.js → browser-JP2LFPR2.js} +2 -2
- package/dist/{browser-DZVIVKOA.js → browser-PZX7PO23.js} +1 -1
- package/dist/{cdp-driver-DF5JHA7W.js → cdp-driver-RPUNQBGM.js} +117 -52
- package/dist/{cdp-driver-WWQBRTPF.js → cdp-driver-S5STYUZZ.js} +1 -1
- package/dist/{chunk-OH7CB2P6.js → chunk-6V57JME6.js} +1 -1
- package/dist/{chunk-XVZ6NKRJ.js → chunk-MNFOCOL6.js} +117 -52
- package/dist/{chunk-E5WWMKXB.js → chunk-QFROODUU.js} +117 -52
- package/dist/{chunk-NDAMCPIJ.js → chunk-SLQR57XZ.js} +1 -1
- package/dist/cli.js +123 -13
- package/dist/daemon-main.js +68 -6
- package/dist/index.js +124 -14
- package/dist/{session-replayer-UHITXIOZ.js → session-replayer-IXLSCF5U.js} +1 -1
- package/package.json +2 -2
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-MNFOCOL6.js";
|
|
29
29
|
import "./chunk-TNEN6VQ2.js";
|
|
30
30
|
import {
|
|
31
31
|
forwardCommandLog,
|
|
@@ -800,6 +800,21 @@ var waitForSelectorDef = {
|
|
|
800
800
|
}
|
|
801
801
|
};
|
|
802
802
|
var waitCommand = registerCommand({ name: "wait", selectorParams: ["selector"], ...waitForSelectorDef });
|
|
803
|
+
var waitForTimeoutCommand = registerCommand({
|
|
804
|
+
name: "waitForTimeout",
|
|
805
|
+
description: "Wait for a specified number of milliseconds",
|
|
806
|
+
scope: "project",
|
|
807
|
+
parameters: z4.object({
|
|
808
|
+
timeout: z4.number().describe("Milliseconds to wait").default(1e3)
|
|
809
|
+
}),
|
|
810
|
+
result: z4.object({
|
|
811
|
+
waited: z4.number()
|
|
812
|
+
}),
|
|
813
|
+
handler: async (p) => {
|
|
814
|
+
await new Promise((r) => setTimeout(r, p.timeout));
|
|
815
|
+
return ok4({ waited: p.timeout });
|
|
816
|
+
}
|
|
817
|
+
});
|
|
803
818
|
|
|
804
819
|
// src/commands/scroll.ts
|
|
805
820
|
import { z as z5 } from "zod";
|
|
@@ -1665,7 +1680,7 @@ var healthCheckCommand = registerCommand({
|
|
|
1665
1680
|
issues.push({
|
|
1666
1681
|
severity: "error",
|
|
1667
1682
|
category: "links",
|
|
1668
|
-
message: `Broken link (fetch error): ${href} \u2014 ${
|
|
1683
|
+
message: `Broken link (fetch error): ${href} \u2014 ${(err instanceof Error ? err.message : String(err)) || "unknown"}`
|
|
1669
1684
|
});
|
|
1670
1685
|
}
|
|
1671
1686
|
}
|
|
@@ -2341,9 +2356,54 @@ var scrapeCommand = registerCommand({
|
|
|
2341
2356
|
}
|
|
2342
2357
|
let content;
|
|
2343
2358
|
switch (p.format) {
|
|
2344
|
-
case "markdown":
|
|
2359
|
+
case "markdown": {
|
|
2360
|
+
const tablesMd = await page.evaluate(() => {
|
|
2361
|
+
document.querySelectorAll(
|
|
2362
|
+
'.el-table__fixed, .el-table__fixed-right, [class*="fixed-left"], [class*="fixed-right"], .ant-table-fixed-left, .ant-table-fixed-right'
|
|
2363
|
+
).forEach((el) => el.remove());
|
|
2364
|
+
document.querySelectorAll("table").forEach((t) => {
|
|
2365
|
+
if (t.closest(".el-table__fixed, .el-table__fixed-right")) t.remove();
|
|
2366
|
+
});
|
|
2367
|
+
const tables = document.querySelectorAll("table");
|
|
2368
|
+
if (tables.length === 0) {
|
|
2369
|
+
const altTables = document.querySelectorAll(
|
|
2370
|
+
'[role="table"], [role="grid"], .el-table__body, .ant-table-tbody'
|
|
2371
|
+
);
|
|
2372
|
+
if (altTables.length === 0) return "";
|
|
2373
|
+
return Array.from(altTables).map((table) => {
|
|
2374
|
+
return extractRowsFromContainer(table);
|
|
2375
|
+
}).filter((md) => md).join("\n\n");
|
|
2376
|
+
}
|
|
2377
|
+
return Array.from(tables).map((table) => {
|
|
2378
|
+
return extractRowsFromContainer(table);
|
|
2379
|
+
}).filter((md) => md).join("\n\n");
|
|
2380
|
+
function extractRowsFromContainer(container) {
|
|
2381
|
+
const rows = container.querySelectorAll(':scope > tr, :scope > thead > tr, :scope > tbody > tr, :scope > tfoot > tr, [role="row"]');
|
|
2382
|
+
if (rows.length === 0) return "";
|
|
2383
|
+
const mdRows = Array.from(rows).map((row) => {
|
|
2384
|
+
const cells = row.querySelectorAll(':scope > th, :scope > td, :scope > [role="columnheader"], :scope > [role="cell"]');
|
|
2385
|
+
if (cells.length === 0) return "";
|
|
2386
|
+
return "| " + Array.from(cells).map((c) => {
|
|
2387
|
+
const cellText = c.innerText?.trim().replace(/\n/g, " ") || "";
|
|
2388
|
+
return cellText.replace(/\|/g, "\\|") || "";
|
|
2389
|
+
}).join(" | ") + " |";
|
|
2390
|
+
}).filter((r) => r);
|
|
2391
|
+
if (mdRows.length === 0) return "";
|
|
2392
|
+
const headerRow = rows[0];
|
|
2393
|
+
const headerCells = headerRow.querySelectorAll(':scope > th, :scope > [role="columnheader"]');
|
|
2394
|
+
if (headerCells.length > 0) {
|
|
2395
|
+
const sep = "| " + Array(headerCells.length).fill("---").join(" | ") + " |";
|
|
2396
|
+
return mdRows[0] + "\n" + sep + "\n" + mdRows.slice(1).join("\n");
|
|
2397
|
+
}
|
|
2398
|
+
return mdRows.join("\n");
|
|
2399
|
+
}
|
|
2400
|
+
});
|
|
2345
2401
|
content = htmlToMarkdown(html, { onlyMainContent: p.onlyMainContent });
|
|
2402
|
+
if (tablesMd) {
|
|
2403
|
+
content = tablesMd + "\n\n" + content;
|
|
2404
|
+
}
|
|
2346
2405
|
break;
|
|
2406
|
+
}
|
|
2347
2407
|
case "html":
|
|
2348
2408
|
content = html;
|
|
2349
2409
|
break;
|
|
@@ -5283,6 +5343,7 @@ function parseCommandChain(input, options) {
|
|
|
5283
5343
|
continue;
|
|
5284
5344
|
}
|
|
5285
5345
|
if (char === ";") {
|
|
5346
|
+
lastOperator = "sequence";
|
|
5286
5347
|
flushPipeline();
|
|
5287
5348
|
continue;
|
|
5288
5349
|
}
|
|
@@ -5337,6 +5398,7 @@ registerCommandDefinition("uncheck", ["selector"]);
|
|
|
5337
5398
|
registerCommandDefinition("hover", ["selector"]);
|
|
5338
5399
|
registerCommandDefinition("dblclick", ["selector"]);
|
|
5339
5400
|
registerCommandDefinition("wait", ["selector"]);
|
|
5401
|
+
registerCommandDefinition("waitForTimeout", ["timeout"]);
|
|
5340
5402
|
registerCommandDefinition("screenshot", []);
|
|
5341
5403
|
registerCommandDefinition("eval", ["expression"]);
|
|
5342
5404
|
registerCommandDefinition("scroll", ["direction"]);
|
|
@@ -6969,7 +7031,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6969
7031
|
}
|
|
6970
7032
|
let targetPageOverride = null;
|
|
6971
7033
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
6972
|
-
const { findTargetPage } = await import("./browser-
|
|
7034
|
+
const { findTargetPage } = await import("./browser-PZX7PO23.js");
|
|
6973
7035
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
6974
7036
|
if (!targetPageOverride) {
|
|
6975
7037
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -9477,7 +9539,17 @@ var SELECTOR_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
9477
9539
|
"dblclick",
|
|
9478
9540
|
"wait"
|
|
9479
9541
|
]);
|
|
9542
|
+
var CAMEL_TO_KEBAB = {
|
|
9543
|
+
getCookies: "get-cookies",
|
|
9544
|
+
setCookie: "set-cookie",
|
|
9545
|
+
clearCookies: "clear-cookies",
|
|
9546
|
+
getLocalStorage: "get-local-storage",
|
|
9547
|
+
setLocalStorage: "set-local-storage",
|
|
9548
|
+
clearLocalStorage: "clear-local-storage",
|
|
9549
|
+
setViewport: "set-viewport"
|
|
9550
|
+
};
|
|
9480
9551
|
async function handleBrowserCommand(command, args, options, sessionName, mode, cdpEndpoint) {
|
|
9552
|
+
command = CAMEL_TO_KEBAB[command] || command;
|
|
9481
9553
|
if (args.includes("--help") || args.includes("-h") || options.help || options.h) {
|
|
9482
9554
|
const cmdDef = getCommand(command);
|
|
9483
9555
|
if (cmdDef) {
|
|
@@ -9607,7 +9679,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
9607
9679
|
type: options.type,
|
|
9608
9680
|
selector: options.selector || options.s,
|
|
9609
9681
|
base64: !!options.base64,
|
|
9610
|
-
output: options.output || options.o
|
|
9682
|
+
output: options.output || options.o || args[0]
|
|
9611
9683
|
};
|
|
9612
9684
|
break;
|
|
9613
9685
|
case "eval":
|
|
@@ -9844,6 +9916,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
9844
9916
|
outputResult(result.data, mode);
|
|
9845
9917
|
console.error(`
|
|
9846
9918
|
\u26A0\uFE0F ${hint}`);
|
|
9919
|
+
process.exit(1);
|
|
9847
9920
|
} else {
|
|
9848
9921
|
outputResult(result.data, mode);
|
|
9849
9922
|
}
|
|
@@ -9938,11 +10011,6 @@ async function handleSession(args, options, mode, _cdpEndpoint) {
|
|
|
9938
10011
|
}
|
|
9939
10012
|
|
|
9940
10013
|
// src/cli/plugin-routes.ts
|
|
9941
|
-
var pluginLoader3 = null;
|
|
9942
|
-
function getPluginLoader2() {
|
|
9943
|
-
if (!pluginLoader3) pluginLoader3 = new XBrowserPluginLoader();
|
|
9944
|
-
return pluginLoader3;
|
|
9945
|
-
}
|
|
9946
10014
|
async function buildRuntimePluginInfo() {
|
|
9947
10015
|
const loader = await getPluginLoader();
|
|
9948
10016
|
const sites = loader.getCore().loader.getSites();
|
|
@@ -10200,6 +10268,10 @@ async function handlePlugin(args, options, mode) {
|
|
|
10200
10268
|
} else {
|
|
10201
10269
|
result = await installer.installWithMarketplaceFallback(source, installOpts);
|
|
10202
10270
|
}
|
|
10271
|
+
try {
|
|
10272
|
+
await (await getPluginLoader()).reloadPlugin(result.name);
|
|
10273
|
+
} catch {
|
|
10274
|
+
}
|
|
10203
10275
|
outputResult(
|
|
10204
10276
|
{ ok: true, name: result.name, source: result.source, path: result.path },
|
|
10205
10277
|
mode
|
|
@@ -10215,6 +10287,11 @@ async function handlePlugin(args, options, mode) {
|
|
|
10215
10287
|
outputError(`Plugin "${name}" is not installed. Use 'xbrowser plugin list' to see installed plugins.`);
|
|
10216
10288
|
}
|
|
10217
10289
|
await installer.uninstall(name);
|
|
10290
|
+
try {
|
|
10291
|
+
const loader = await getPluginLoader();
|
|
10292
|
+
await loader.reloadPlugin(name);
|
|
10293
|
+
} catch {
|
|
10294
|
+
}
|
|
10218
10295
|
outputResult({ ok: true, name }, mode);
|
|
10219
10296
|
break;
|
|
10220
10297
|
}
|
|
@@ -10268,7 +10345,7 @@ Total: ${enrichedPlugins.length} plugins`);
|
|
|
10268
10345
|
case "reload": {
|
|
10269
10346
|
const name = subArgs[0];
|
|
10270
10347
|
if (!name) outputError("Usage: xbrowser plugin reload <name>");
|
|
10271
|
-
await
|
|
10348
|
+
(await getPluginLoader()).reloadPlugin(name);
|
|
10272
10349
|
outputResult({ ok: true, name }, mode);
|
|
10273
10350
|
break;
|
|
10274
10351
|
}
|
|
@@ -12116,10 +12193,18 @@ function extractCdpFromArgv(argv) {
|
|
|
12116
12193
|
async function handleStdinMode(stdinCommands, argv) {
|
|
12117
12194
|
const chain = stdinCommands.join(" && ");
|
|
12118
12195
|
const cdpEndpoint = argv ? extractCdpFromArgv(argv) : void 0;
|
|
12119
|
-
const
|
|
12196
|
+
const sessionName = argv ? extractSessionNameFromArgv(argv) : "default";
|
|
12197
|
+
const chainResult = await executeChain(chain, { fileMode: true, cdpEndpoint, sessionName });
|
|
12120
12198
|
printChainResult(chainResult);
|
|
12121
12199
|
if (!chainResult.success) throw new Error("Command failed");
|
|
12122
12200
|
}
|
|
12201
|
+
function extractSessionNameFromArgv(argv) {
|
|
12202
|
+
for (let i = 0; i < argv.length; i++) {
|
|
12203
|
+
if (argv[i] === "--session" && argv[i + 1]) return argv[i + 1];
|
|
12204
|
+
if (typeof argv[i] === "string" && argv[i].startsWith("--session=")) return argv[i].slice(10);
|
|
12205
|
+
}
|
|
12206
|
+
return process.env.XBROWSER_SESSION || "default";
|
|
12207
|
+
}
|
|
12123
12208
|
async function handleEvalMode(argv) {
|
|
12124
12209
|
const evalCommands = parseEvalFlags(argv);
|
|
12125
12210
|
if (evalCommands.length === 0) return;
|
|
@@ -12259,6 +12344,31 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12259
12344
|
}
|
|
12260
12345
|
return;
|
|
12261
12346
|
}
|
|
12347
|
+
const SUBCOMMAND_HELP = {
|
|
12348
|
+
session: "session open|close|kill|list [--session <name>] [--cdp <endpoint>]",
|
|
12349
|
+
plugin: "plugin install|uninstall|list|reload|schema|search|info <name>",
|
|
12350
|
+
record: "record start|stop|status [--url <url>] [--name <flow>]",
|
|
12351
|
+
daemon: "daemon status [--port <port>]",
|
|
12352
|
+
replay: "replay <file> [--slow-mo <ms>] [--stop-on-error]",
|
|
12353
|
+
create: "create <name> [--template static|dynamic|login|api]",
|
|
12354
|
+
run: "run <file>",
|
|
12355
|
+
serve: "serve [--port <port>] [--token <token>]",
|
|
12356
|
+
remote: "remote <url> [command] [--token <token>]",
|
|
12357
|
+
convert: "convert <file> [--to js|py|sh]",
|
|
12358
|
+
extract: "extract <file> [--format json|yaml]",
|
|
12359
|
+
filter: "filter <file> [--include <type>] [--exclude <type>]",
|
|
12360
|
+
test: "test <name> [--cdp <endpoint>]",
|
|
12361
|
+
viewer: "viewer [--session <name>]",
|
|
12362
|
+
kill: "kill [--all]",
|
|
12363
|
+
net: "net [--cdp <endpoint>]"
|
|
12364
|
+
};
|
|
12365
|
+
const subHelp = SUBCOMMAND_HELP[command];
|
|
12366
|
+
if (subHelp) {
|
|
12367
|
+
console.log(`
|
|
12368
|
+
Usage: xbrowser ${subHelp}
|
|
12369
|
+
`);
|
|
12370
|
+
return;
|
|
12371
|
+
}
|
|
12262
12372
|
showMainHelp();
|
|
12263
12373
|
return;
|
|
12264
12374
|
}
|
|
@@ -12745,7 +12855,7 @@ async function main() {
|
|
|
12745
12855
|
const command = process.argv[2];
|
|
12746
12856
|
const isLongRunning = command === "preview" || command === "serve";
|
|
12747
12857
|
if (!isLongRunning) {
|
|
12748
|
-
const { ensureProcessCanExit } = await import("./browser-
|
|
12858
|
+
const { ensureProcessCanExit } = await import("./browser-PZX7PO23.js");
|
|
12749
12859
|
await ensureProcessCanExit().catch(() => {
|
|
12750
12860
|
});
|
|
12751
12861
|
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-SLQR57XZ.js";
|
|
25
|
+
import "./chunk-QFROODUU.js";
|
|
26
26
|
import "./chunk-TNEN6VQ2.js";
|
|
27
27
|
import {
|
|
28
28
|
getDaemonConfig,
|
|
@@ -758,6 +758,21 @@ var waitForSelectorDef = {
|
|
|
758
758
|
}
|
|
759
759
|
};
|
|
760
760
|
var waitCommand = registerCommand({ name: "wait", selectorParams: ["selector"], ...waitForSelectorDef });
|
|
761
|
+
var waitForTimeoutCommand = registerCommand({
|
|
762
|
+
name: "waitForTimeout",
|
|
763
|
+
description: "Wait for a specified number of milliseconds",
|
|
764
|
+
scope: "project",
|
|
765
|
+
parameters: z4.object({
|
|
766
|
+
timeout: z4.number().describe("Milliseconds to wait").default(1e3)
|
|
767
|
+
}),
|
|
768
|
+
result: z4.object({
|
|
769
|
+
waited: z4.number()
|
|
770
|
+
}),
|
|
771
|
+
handler: async (p) => {
|
|
772
|
+
await new Promise((r) => setTimeout(r, p.timeout));
|
|
773
|
+
return ok4({ waited: p.timeout });
|
|
774
|
+
}
|
|
775
|
+
});
|
|
761
776
|
|
|
762
777
|
// src/commands/scroll.ts
|
|
763
778
|
import { z as z5 } from "zod";
|
|
@@ -1623,7 +1638,7 @@ var healthCheckCommand = registerCommand({
|
|
|
1623
1638
|
issues.push({
|
|
1624
1639
|
severity: "error",
|
|
1625
1640
|
category: "links",
|
|
1626
|
-
message: `Broken link (fetch error): ${href} \u2014 ${
|
|
1641
|
+
message: `Broken link (fetch error): ${href} \u2014 ${(err instanceof Error ? err.message : String(err)) || "unknown"}`
|
|
1627
1642
|
});
|
|
1628
1643
|
}
|
|
1629
1644
|
}
|
|
@@ -2299,9 +2314,54 @@ var scrapeCommand = registerCommand({
|
|
|
2299
2314
|
}
|
|
2300
2315
|
let content;
|
|
2301
2316
|
switch (p.format) {
|
|
2302
|
-
case "markdown":
|
|
2317
|
+
case "markdown": {
|
|
2318
|
+
const tablesMd = await page.evaluate(() => {
|
|
2319
|
+
document.querySelectorAll(
|
|
2320
|
+
'.el-table__fixed, .el-table__fixed-right, [class*="fixed-left"], [class*="fixed-right"], .ant-table-fixed-left, .ant-table-fixed-right'
|
|
2321
|
+
).forEach((el) => el.remove());
|
|
2322
|
+
document.querySelectorAll("table").forEach((t) => {
|
|
2323
|
+
if (t.closest(".el-table__fixed, .el-table__fixed-right")) t.remove();
|
|
2324
|
+
});
|
|
2325
|
+
const tables = document.querySelectorAll("table");
|
|
2326
|
+
if (tables.length === 0) {
|
|
2327
|
+
const altTables = document.querySelectorAll(
|
|
2328
|
+
'[role="table"], [role="grid"], .el-table__body, .ant-table-tbody'
|
|
2329
|
+
);
|
|
2330
|
+
if (altTables.length === 0) return "";
|
|
2331
|
+
return Array.from(altTables).map((table) => {
|
|
2332
|
+
return extractRowsFromContainer(table);
|
|
2333
|
+
}).filter((md) => md).join("\n\n");
|
|
2334
|
+
}
|
|
2335
|
+
return Array.from(tables).map((table) => {
|
|
2336
|
+
return extractRowsFromContainer(table);
|
|
2337
|
+
}).filter((md) => md).join("\n\n");
|
|
2338
|
+
function extractRowsFromContainer(container) {
|
|
2339
|
+
const rows = container.querySelectorAll(':scope > tr, :scope > thead > tr, :scope > tbody > tr, :scope > tfoot > tr, [role="row"]');
|
|
2340
|
+
if (rows.length === 0) return "";
|
|
2341
|
+
const mdRows = Array.from(rows).map((row) => {
|
|
2342
|
+
const cells = row.querySelectorAll(':scope > th, :scope > td, :scope > [role="columnheader"], :scope > [role="cell"]');
|
|
2343
|
+
if (cells.length === 0) return "";
|
|
2344
|
+
return "| " + Array.from(cells).map((c) => {
|
|
2345
|
+
const cellText = c.innerText?.trim().replace(/\n/g, " ") || "";
|
|
2346
|
+
return cellText.replace(/\|/g, "\\|") || "";
|
|
2347
|
+
}).join(" | ") + " |";
|
|
2348
|
+
}).filter((r) => r);
|
|
2349
|
+
if (mdRows.length === 0) return "";
|
|
2350
|
+
const headerRow = rows[0];
|
|
2351
|
+
const headerCells = headerRow.querySelectorAll(':scope > th, :scope > [role="columnheader"]');
|
|
2352
|
+
if (headerCells.length > 0) {
|
|
2353
|
+
const sep = "| " + Array(headerCells.length).fill("---").join(" | ") + " |";
|
|
2354
|
+
return mdRows[0] + "\n" + sep + "\n" + mdRows.slice(1).join("\n");
|
|
2355
|
+
}
|
|
2356
|
+
return mdRows.join("\n");
|
|
2357
|
+
}
|
|
2358
|
+
});
|
|
2303
2359
|
content = htmlToMarkdown(html, { onlyMainContent: p.onlyMainContent });
|
|
2360
|
+
if (tablesMd) {
|
|
2361
|
+
content = tablesMd + "\n\n" + content;
|
|
2362
|
+
}
|
|
2304
2363
|
break;
|
|
2364
|
+
}
|
|
2305
2365
|
case "html":
|
|
2306
2366
|
content = html;
|
|
2307
2367
|
break;
|
|
@@ -5241,6 +5301,7 @@ function parseCommandChain(input, options) {
|
|
|
5241
5301
|
continue;
|
|
5242
5302
|
}
|
|
5243
5303
|
if (char === ";") {
|
|
5304
|
+
lastOperator = "sequence";
|
|
5244
5305
|
flushPipeline();
|
|
5245
5306
|
continue;
|
|
5246
5307
|
}
|
|
@@ -5295,6 +5356,7 @@ registerCommandDefinition("uncheck", ["selector"]);
|
|
|
5295
5356
|
registerCommandDefinition("hover", ["selector"]);
|
|
5296
5357
|
registerCommandDefinition("dblclick", ["selector"]);
|
|
5297
5358
|
registerCommandDefinition("wait", ["selector"]);
|
|
5359
|
+
registerCommandDefinition("waitForTimeout", ["timeout"]);
|
|
5298
5360
|
registerCommandDefinition("screenshot", []);
|
|
5299
5361
|
registerCommandDefinition("eval", ["expression"]);
|
|
5300
5362
|
registerCommandDefinition("scroll", ["direction"]);
|
|
@@ -6927,7 +6989,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6927
6989
|
}
|
|
6928
6990
|
let targetPageOverride = null;
|
|
6929
6991
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
6930
|
-
const { findTargetPage } = await import("./browser-
|
|
6992
|
+
const { findTargetPage } = await import("./browser-CWI6BXYK.js");
|
|
6931
6993
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
6932
6994
|
if (!targetPageOverride) {
|
|
6933
6995
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -8611,7 +8673,7 @@ function createRPCHandler() {
|
|
|
8611
8673
|
const isNewFormat = Array.isArray(parsed.actions);
|
|
8612
8674
|
if (isNewFormat) {
|
|
8613
8675
|
try {
|
|
8614
|
-
const { SessionReplayer } = await import("./session-replayer-
|
|
8676
|
+
const { SessionReplayer } = await import("./session-replayer-IXLSCF5U.js");
|
|
8615
8677
|
const replayer = new SessionReplayer({
|
|
8616
8678
|
page: session.page,
|
|
8617
8679
|
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-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,54 @@ 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
|
+
document.querySelectorAll(
|
|
2402
|
+
'.el-table__fixed, .el-table__fixed-right, [class*="fixed-left"], [class*="fixed-right"], .ant-table-fixed-left, .ant-table-fixed-right'
|
|
2403
|
+
).forEach((el) => el.remove());
|
|
2404
|
+
document.querySelectorAll("table").forEach((t) => {
|
|
2405
|
+
if (t.closest(".el-table__fixed, .el-table__fixed-right")) t.remove();
|
|
2406
|
+
});
|
|
2407
|
+
const tables = document.querySelectorAll("table");
|
|
2408
|
+
if (tables.length === 0) {
|
|
2409
|
+
const altTables = document.querySelectorAll(
|
|
2410
|
+
'[role="table"], [role="grid"], .el-table__body, .ant-table-tbody'
|
|
2411
|
+
);
|
|
2412
|
+
if (altTables.length === 0) return "";
|
|
2413
|
+
return Array.from(altTables).map((table) => {
|
|
2414
|
+
return extractRowsFromContainer(table);
|
|
2415
|
+
}).filter((md) => md).join("\n\n");
|
|
2416
|
+
}
|
|
2417
|
+
return Array.from(tables).map((table) => {
|
|
2418
|
+
return extractRowsFromContainer(table);
|
|
2419
|
+
}).filter((md) => md).join("\n\n");
|
|
2420
|
+
function extractRowsFromContainer(container) {
|
|
2421
|
+
const rows = container.querySelectorAll(':scope > tr, :scope > thead > tr, :scope > tbody > tr, :scope > tfoot > tr, [role="row"]');
|
|
2422
|
+
if (rows.length === 0) return "";
|
|
2423
|
+
const mdRows = Array.from(rows).map((row) => {
|
|
2424
|
+
const cells = row.querySelectorAll(':scope > th, :scope > td, :scope > [role="columnheader"], :scope > [role="cell"]');
|
|
2425
|
+
if (cells.length === 0) return "";
|
|
2426
|
+
return "| " + Array.from(cells).map((c) => {
|
|
2427
|
+
const cellText = c.innerText?.trim().replace(/\n/g, " ") || "";
|
|
2428
|
+
return cellText.replace(/\|/g, "\\|") || "";
|
|
2429
|
+
}).join(" | ") + " |";
|
|
2430
|
+
}).filter((r) => r);
|
|
2431
|
+
if (mdRows.length === 0) return "";
|
|
2432
|
+
const headerRow = rows[0];
|
|
2433
|
+
const headerCells = headerRow.querySelectorAll(':scope > th, :scope > [role="columnheader"]');
|
|
2434
|
+
if (headerCells.length > 0) {
|
|
2435
|
+
const sep = "| " + Array(headerCells.length).fill("---").join(" | ") + " |";
|
|
2436
|
+
return mdRows[0] + "\n" + sep + "\n" + mdRows.slice(1).join("\n");
|
|
2437
|
+
}
|
|
2438
|
+
return mdRows.join("\n");
|
|
2439
|
+
}
|
|
2440
|
+
});
|
|
2385
2441
|
content = htmlToMarkdown(html, { onlyMainContent: p.onlyMainContent });
|
|
2442
|
+
if (tablesMd) {
|
|
2443
|
+
content = tablesMd + "\n\n" + content;
|
|
2444
|
+
}
|
|
2386
2445
|
break;
|
|
2446
|
+
}
|
|
2387
2447
|
case "html":
|
|
2388
2448
|
content = html;
|
|
2389
2449
|
break;
|
|
@@ -5600,6 +5660,7 @@ function parseCommandChain(input, options) {
|
|
|
5600
5660
|
continue;
|
|
5601
5661
|
}
|
|
5602
5662
|
if (char === ";") {
|
|
5663
|
+
lastOperator = "sequence";
|
|
5603
5664
|
flushPipeline();
|
|
5604
5665
|
continue;
|
|
5605
5666
|
}
|
|
@@ -5654,6 +5715,7 @@ registerCommandDefinition("uncheck", ["selector"]);
|
|
|
5654
5715
|
registerCommandDefinition("hover", ["selector"]);
|
|
5655
5716
|
registerCommandDefinition("dblclick", ["selector"]);
|
|
5656
5717
|
registerCommandDefinition("wait", ["selector"]);
|
|
5718
|
+
registerCommandDefinition("waitForTimeout", ["timeout"]);
|
|
5657
5719
|
registerCommandDefinition("screenshot", []);
|
|
5658
5720
|
registerCommandDefinition("eval", ["expression"]);
|
|
5659
5721
|
registerCommandDefinition("scroll", ["direction"]);
|
|
@@ -7289,7 +7351,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7289
7351
|
}
|
|
7290
7352
|
let targetPageOverride = null;
|
|
7291
7353
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7292
|
-
const { findTargetPage } = await import("./browser-
|
|
7354
|
+
const { findTargetPage } = await import("./browser-JP2LFPR2.js");
|
|
7293
7355
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7294
7356
|
if (!targetPageOverride) {
|
|
7295
7357
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -9817,7 +9879,17 @@ var SELECTOR_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
9817
9879
|
"dblclick",
|
|
9818
9880
|
"wait"
|
|
9819
9881
|
]);
|
|
9882
|
+
var CAMEL_TO_KEBAB = {
|
|
9883
|
+
getCookies: "get-cookies",
|
|
9884
|
+
setCookie: "set-cookie",
|
|
9885
|
+
clearCookies: "clear-cookies",
|
|
9886
|
+
getLocalStorage: "get-local-storage",
|
|
9887
|
+
setLocalStorage: "set-local-storage",
|
|
9888
|
+
clearLocalStorage: "clear-local-storage",
|
|
9889
|
+
setViewport: "set-viewport"
|
|
9890
|
+
};
|
|
9820
9891
|
async function handleBrowserCommand(command, args, options, sessionName, mode, cdpEndpoint) {
|
|
9892
|
+
command = CAMEL_TO_KEBAB[command] || command;
|
|
9821
9893
|
if (args.includes("--help") || args.includes("-h") || options.help || options.h) {
|
|
9822
9894
|
const cmdDef = getCommand(command);
|
|
9823
9895
|
if (cmdDef) {
|
|
@@ -9947,7 +10019,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
9947
10019
|
type: options.type,
|
|
9948
10020
|
selector: options.selector || options.s,
|
|
9949
10021
|
base64: !!options.base64,
|
|
9950
|
-
output: options.output || options.o
|
|
10022
|
+
output: options.output || options.o || args[0]
|
|
9951
10023
|
};
|
|
9952
10024
|
break;
|
|
9953
10025
|
case "eval":
|
|
@@ -10184,6 +10256,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10184
10256
|
outputResult(result.data, mode);
|
|
10185
10257
|
console.error(`
|
|
10186
10258
|
\u26A0\uFE0F ${hint}`);
|
|
10259
|
+
process.exit(1);
|
|
10187
10260
|
} else {
|
|
10188
10261
|
outputResult(result.data, mode);
|
|
10189
10262
|
}
|
|
@@ -10278,11 +10351,6 @@ async function handleSession(args, options, mode, _cdpEndpoint) {
|
|
|
10278
10351
|
}
|
|
10279
10352
|
|
|
10280
10353
|
// src/cli/plugin-routes.ts
|
|
10281
|
-
var pluginLoader3 = null;
|
|
10282
|
-
function getPluginLoader2() {
|
|
10283
|
-
if (!pluginLoader3) pluginLoader3 = new XBrowserPluginLoader();
|
|
10284
|
-
return pluginLoader3;
|
|
10285
|
-
}
|
|
10286
10354
|
async function buildRuntimePluginInfo() {
|
|
10287
10355
|
const loader = await getPluginLoader();
|
|
10288
10356
|
const sites = loader.getCore().loader.getSites();
|
|
@@ -10540,6 +10608,10 @@ async function handlePlugin(args, options, mode) {
|
|
|
10540
10608
|
} else {
|
|
10541
10609
|
result = await installer.installWithMarketplaceFallback(source, installOpts);
|
|
10542
10610
|
}
|
|
10611
|
+
try {
|
|
10612
|
+
await (await getPluginLoader()).reloadPlugin(result.name);
|
|
10613
|
+
} catch {
|
|
10614
|
+
}
|
|
10543
10615
|
outputResult(
|
|
10544
10616
|
{ ok: true, name: result.name, source: result.source, path: result.path },
|
|
10545
10617
|
mode
|
|
@@ -10555,6 +10627,11 @@ async function handlePlugin(args, options, mode) {
|
|
|
10555
10627
|
outputError(`Plugin "${name}" is not installed. Use 'xbrowser plugin list' to see installed plugins.`);
|
|
10556
10628
|
}
|
|
10557
10629
|
await installer.uninstall(name);
|
|
10630
|
+
try {
|
|
10631
|
+
const loader = await getPluginLoader();
|
|
10632
|
+
await loader.reloadPlugin(name);
|
|
10633
|
+
} catch {
|
|
10634
|
+
}
|
|
10558
10635
|
outputResult({ ok: true, name }, mode);
|
|
10559
10636
|
break;
|
|
10560
10637
|
}
|
|
@@ -10608,7 +10685,7 @@ Total: ${enrichedPlugins.length} plugins`);
|
|
|
10608
10685
|
case "reload": {
|
|
10609
10686
|
const name = subArgs[0];
|
|
10610
10687
|
if (!name) outputError("Usage: xbrowser plugin reload <name>");
|
|
10611
|
-
await
|
|
10688
|
+
(await getPluginLoader()).reloadPlugin(name);
|
|
10612
10689
|
outputResult({ ok: true, name }, mode);
|
|
10613
10690
|
break;
|
|
10614
10691
|
}
|
|
@@ -12456,10 +12533,18 @@ function extractCdpFromArgv(argv) {
|
|
|
12456
12533
|
async function handleStdinMode(stdinCommands, argv) {
|
|
12457
12534
|
const chain = stdinCommands.join(" && ");
|
|
12458
12535
|
const cdpEndpoint = argv ? extractCdpFromArgv(argv) : void 0;
|
|
12459
|
-
const
|
|
12536
|
+
const sessionName = argv ? extractSessionNameFromArgv(argv) : "default";
|
|
12537
|
+
const chainResult = await executeChain(chain, { fileMode: true, cdpEndpoint, sessionName });
|
|
12460
12538
|
printChainResult(chainResult);
|
|
12461
12539
|
if (!chainResult.success) throw new Error("Command failed");
|
|
12462
12540
|
}
|
|
12541
|
+
function extractSessionNameFromArgv(argv) {
|
|
12542
|
+
for (let i = 0; i < argv.length; i++) {
|
|
12543
|
+
if (argv[i] === "--session" && argv[i + 1]) return argv[i + 1];
|
|
12544
|
+
if (typeof argv[i] === "string" && argv[i].startsWith("--session=")) return argv[i].slice(10);
|
|
12545
|
+
}
|
|
12546
|
+
return process.env.XBROWSER_SESSION || "default";
|
|
12547
|
+
}
|
|
12463
12548
|
async function handleEvalMode(argv) {
|
|
12464
12549
|
const evalCommands = parseEvalFlags(argv);
|
|
12465
12550
|
if (evalCommands.length === 0) return;
|
|
@@ -12599,6 +12684,31 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12599
12684
|
}
|
|
12600
12685
|
return;
|
|
12601
12686
|
}
|
|
12687
|
+
const SUBCOMMAND_HELP = {
|
|
12688
|
+
session: "session open|close|kill|list [--session <name>] [--cdp <endpoint>]",
|
|
12689
|
+
plugin: "plugin install|uninstall|list|reload|schema|search|info <name>",
|
|
12690
|
+
record: "record start|stop|status [--url <url>] [--name <flow>]",
|
|
12691
|
+
daemon: "daemon status [--port <port>]",
|
|
12692
|
+
replay: "replay <file> [--slow-mo <ms>] [--stop-on-error]",
|
|
12693
|
+
create: "create <name> [--template static|dynamic|login|api]",
|
|
12694
|
+
run: "run <file>",
|
|
12695
|
+
serve: "serve [--port <port>] [--token <token>]",
|
|
12696
|
+
remote: "remote <url> [command] [--token <token>]",
|
|
12697
|
+
convert: "convert <file> [--to js|py|sh]",
|
|
12698
|
+
extract: "extract <file> [--format json|yaml]",
|
|
12699
|
+
filter: "filter <file> [--include <type>] [--exclude <type>]",
|
|
12700
|
+
test: "test <name> [--cdp <endpoint>]",
|
|
12701
|
+
viewer: "viewer [--session <name>]",
|
|
12702
|
+
kill: "kill [--all]",
|
|
12703
|
+
net: "net [--cdp <endpoint>]"
|
|
12704
|
+
};
|
|
12705
|
+
const subHelp = SUBCOMMAND_HELP[command];
|
|
12706
|
+
if (subHelp) {
|
|
12707
|
+
console.log(`
|
|
12708
|
+
Usage: xbrowser ${subHelp}
|
|
12709
|
+
`);
|
|
12710
|
+
return;
|
|
12711
|
+
}
|
|
12602
12712
|
showMainHelp();
|
|
12603
12713
|
return;
|
|
12604
12714
|
}
|
|
@@ -15872,7 +15982,7 @@ var DataCollector = class {
|
|
|
15872
15982
|
return results;
|
|
15873
15983
|
}
|
|
15874
15984
|
async createBrowserContext() {
|
|
15875
|
-
const { launch } = await import("./cdp-driver-
|
|
15985
|
+
const { launch } = await import("./cdp-driver-S5STYUZZ.js");
|
|
15876
15986
|
const { browser } = await launch({
|
|
15877
15987
|
headless: true,
|
|
15878
15988
|
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.1
|
|
3
|
+
"version": "1.2.1",
|
|
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
|
},
|