@xbrowser/cli 1.1.2 → 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-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 +122 -13
- package/dist/daemon-main.js +67 -6
- package/dist/index.js +123 -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,53 @@ 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
|
+
const tableSelectors = [
|
|
2362
|
+
"table",
|
|
2363
|
+
'[role="table"]',
|
|
2364
|
+
'[role="grid"]',
|
|
2365
|
+
'[class*="el-table"]',
|
|
2366
|
+
// Element UI
|
|
2367
|
+
'[class*="ant-table"]',
|
|
2368
|
+
// Ant Design
|
|
2369
|
+
'[class*="MuiTable"]',
|
|
2370
|
+
// Material UI
|
|
2371
|
+
'[class*="table"]'
|
|
2372
|
+
// Generic table-like
|
|
2373
|
+
].join(",");
|
|
2374
|
+
const tables = document.querySelectorAll(tableSelectors);
|
|
2375
|
+
if (tables.length === 0) return "";
|
|
2376
|
+
return Array.from(tables).map((table) => {
|
|
2377
|
+
const rows = table.querySelectorAll('tr, [role="row"], [class*="row"]');
|
|
2378
|
+
if (rows.length === 0) return "";
|
|
2379
|
+
const mdRows = Array.from(rows).map((row) => {
|
|
2380
|
+
const cells = row.querySelectorAll('th, td, [role="columnheader"], [role="cell"], [class*="cell"], [class*="col"]');
|
|
2381
|
+
return "| " + Array.from(cells).map((c) => {
|
|
2382
|
+
const cellText = c.innerText?.trim().replace(/\n/g, " ") || "";
|
|
2383
|
+
return cellText.replace(/\|/g, "\\|") || "";
|
|
2384
|
+
}).join(" | ") + " |";
|
|
2385
|
+
}).join("\n");
|
|
2386
|
+
const headerRow = rows[0];
|
|
2387
|
+
const headerCells = headerRow.querySelectorAll('th, [role="columnheader"], [class*="header"]');
|
|
2388
|
+
const hasHeader = headerCells.length > 0;
|
|
2389
|
+
if (hasHeader && mdRows) {
|
|
2390
|
+
const headerCount = headerCells.length;
|
|
2391
|
+
const sep = "| " + Array(headerCount).fill("---").join(" | ") + " |";
|
|
2392
|
+
return mdRows.split("\n").map((line, i) => {
|
|
2393
|
+
if (i === 0) return line + "\n" + sep;
|
|
2394
|
+
return line;
|
|
2395
|
+
}).join("\n");
|
|
2396
|
+
}
|
|
2397
|
+
return mdRows;
|
|
2398
|
+
}).join("\n\n");
|
|
2399
|
+
});
|
|
2345
2400
|
content = htmlToMarkdown(html, { onlyMainContent: p.onlyMainContent });
|
|
2401
|
+
if (tablesMd) {
|
|
2402
|
+
content = tablesMd + "\n\n" + content;
|
|
2403
|
+
}
|
|
2346
2404
|
break;
|
|
2405
|
+
}
|
|
2347
2406
|
case "html":
|
|
2348
2407
|
content = html;
|
|
2349
2408
|
break;
|
|
@@ -5283,6 +5342,7 @@ function parseCommandChain(input, options) {
|
|
|
5283
5342
|
continue;
|
|
5284
5343
|
}
|
|
5285
5344
|
if (char === ";") {
|
|
5345
|
+
lastOperator = "sequence";
|
|
5286
5346
|
flushPipeline();
|
|
5287
5347
|
continue;
|
|
5288
5348
|
}
|
|
@@ -5337,6 +5397,7 @@ registerCommandDefinition("uncheck", ["selector"]);
|
|
|
5337
5397
|
registerCommandDefinition("hover", ["selector"]);
|
|
5338
5398
|
registerCommandDefinition("dblclick", ["selector"]);
|
|
5339
5399
|
registerCommandDefinition("wait", ["selector"]);
|
|
5400
|
+
registerCommandDefinition("waitForTimeout", ["timeout"]);
|
|
5340
5401
|
registerCommandDefinition("screenshot", []);
|
|
5341
5402
|
registerCommandDefinition("eval", ["expression"]);
|
|
5342
5403
|
registerCommandDefinition("scroll", ["direction"]);
|
|
@@ -6969,7 +7030,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6969
7030
|
}
|
|
6970
7031
|
let targetPageOverride = null;
|
|
6971
7032
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
6972
|
-
const { findTargetPage } = await import("./browser-
|
|
7033
|
+
const { findTargetPage } = await import("./browser-PZX7PO23.js");
|
|
6973
7034
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
6974
7035
|
if (!targetPageOverride) {
|
|
6975
7036
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -9477,7 +9538,17 @@ var SELECTOR_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
9477
9538
|
"dblclick",
|
|
9478
9539
|
"wait"
|
|
9479
9540
|
]);
|
|
9541
|
+
var CAMEL_TO_KEBAB = {
|
|
9542
|
+
getCookies: "get-cookies",
|
|
9543
|
+
setCookie: "set-cookie",
|
|
9544
|
+
clearCookies: "clear-cookies",
|
|
9545
|
+
getLocalStorage: "get-local-storage",
|
|
9546
|
+
setLocalStorage: "set-local-storage",
|
|
9547
|
+
clearLocalStorage: "clear-local-storage",
|
|
9548
|
+
setViewport: "set-viewport"
|
|
9549
|
+
};
|
|
9480
9550
|
async function handleBrowserCommand(command, args, options, sessionName, mode, cdpEndpoint) {
|
|
9551
|
+
command = CAMEL_TO_KEBAB[command] || command;
|
|
9481
9552
|
if (args.includes("--help") || args.includes("-h") || options.help || options.h) {
|
|
9482
9553
|
const cmdDef = getCommand(command);
|
|
9483
9554
|
if (cmdDef) {
|
|
@@ -9607,7 +9678,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
9607
9678
|
type: options.type,
|
|
9608
9679
|
selector: options.selector || options.s,
|
|
9609
9680
|
base64: !!options.base64,
|
|
9610
|
-
output: options.output || options.o
|
|
9681
|
+
output: options.output || options.o || args[0]
|
|
9611
9682
|
};
|
|
9612
9683
|
break;
|
|
9613
9684
|
case "eval":
|
|
@@ -9844,6 +9915,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
9844
9915
|
outputResult(result.data, mode);
|
|
9845
9916
|
console.error(`
|
|
9846
9917
|
\u26A0\uFE0F ${hint}`);
|
|
9918
|
+
process.exit(1);
|
|
9847
9919
|
} else {
|
|
9848
9920
|
outputResult(result.data, mode);
|
|
9849
9921
|
}
|
|
@@ -9938,11 +10010,6 @@ async function handleSession(args, options, mode, _cdpEndpoint) {
|
|
|
9938
10010
|
}
|
|
9939
10011
|
|
|
9940
10012
|
// src/cli/plugin-routes.ts
|
|
9941
|
-
var pluginLoader3 = null;
|
|
9942
|
-
function getPluginLoader2() {
|
|
9943
|
-
if (!pluginLoader3) pluginLoader3 = new XBrowserPluginLoader();
|
|
9944
|
-
return pluginLoader3;
|
|
9945
|
-
}
|
|
9946
10013
|
async function buildRuntimePluginInfo() {
|
|
9947
10014
|
const loader = await getPluginLoader();
|
|
9948
10015
|
const sites = loader.getCore().loader.getSites();
|
|
@@ -10200,6 +10267,10 @@ async function handlePlugin(args, options, mode) {
|
|
|
10200
10267
|
} else {
|
|
10201
10268
|
result = await installer.installWithMarketplaceFallback(source, installOpts);
|
|
10202
10269
|
}
|
|
10270
|
+
try {
|
|
10271
|
+
await (await getPluginLoader()).reloadPlugin(result.name);
|
|
10272
|
+
} catch {
|
|
10273
|
+
}
|
|
10203
10274
|
outputResult(
|
|
10204
10275
|
{ ok: true, name: result.name, source: result.source, path: result.path },
|
|
10205
10276
|
mode
|
|
@@ -10215,6 +10286,11 @@ async function handlePlugin(args, options, mode) {
|
|
|
10215
10286
|
outputError(`Plugin "${name}" is not installed. Use 'xbrowser plugin list' to see installed plugins.`);
|
|
10216
10287
|
}
|
|
10217
10288
|
await installer.uninstall(name);
|
|
10289
|
+
try {
|
|
10290
|
+
const loader = await getPluginLoader();
|
|
10291
|
+
await loader.reloadPlugin(name);
|
|
10292
|
+
} catch {
|
|
10293
|
+
}
|
|
10218
10294
|
outputResult({ ok: true, name }, mode);
|
|
10219
10295
|
break;
|
|
10220
10296
|
}
|
|
@@ -10268,7 +10344,7 @@ Total: ${enrichedPlugins.length} plugins`);
|
|
|
10268
10344
|
case "reload": {
|
|
10269
10345
|
const name = subArgs[0];
|
|
10270
10346
|
if (!name) outputError("Usage: xbrowser plugin reload <name>");
|
|
10271
|
-
await
|
|
10347
|
+
(await getPluginLoader()).reloadPlugin(name);
|
|
10272
10348
|
outputResult({ ok: true, name }, mode);
|
|
10273
10349
|
break;
|
|
10274
10350
|
}
|
|
@@ -12116,10 +12192,18 @@ function extractCdpFromArgv(argv) {
|
|
|
12116
12192
|
async function handleStdinMode(stdinCommands, argv) {
|
|
12117
12193
|
const chain = stdinCommands.join(" && ");
|
|
12118
12194
|
const cdpEndpoint = argv ? extractCdpFromArgv(argv) : void 0;
|
|
12119
|
-
const
|
|
12195
|
+
const sessionName = argv ? extractSessionNameFromArgv(argv) : "default";
|
|
12196
|
+
const chainResult = await executeChain(chain, { fileMode: true, cdpEndpoint, sessionName });
|
|
12120
12197
|
printChainResult(chainResult);
|
|
12121
12198
|
if (!chainResult.success) throw new Error("Command failed");
|
|
12122
12199
|
}
|
|
12200
|
+
function extractSessionNameFromArgv(argv) {
|
|
12201
|
+
for (let i = 0; i < argv.length; i++) {
|
|
12202
|
+
if (argv[i] === "--session" && argv[i + 1]) return argv[i + 1];
|
|
12203
|
+
if (typeof argv[i] === "string" && argv[i].startsWith("--session=")) return argv[i].slice(10);
|
|
12204
|
+
}
|
|
12205
|
+
return process.env.XBROWSER_SESSION || "default";
|
|
12206
|
+
}
|
|
12123
12207
|
async function handleEvalMode(argv) {
|
|
12124
12208
|
const evalCommands = parseEvalFlags(argv);
|
|
12125
12209
|
if (evalCommands.length === 0) return;
|
|
@@ -12259,6 +12343,31 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12259
12343
|
}
|
|
12260
12344
|
return;
|
|
12261
12345
|
}
|
|
12346
|
+
const SUBCOMMAND_HELP = {
|
|
12347
|
+
session: "session open|close|kill|list [--session <name>] [--cdp <endpoint>]",
|
|
12348
|
+
plugin: "plugin install|uninstall|list|reload|schema|search|info <name>",
|
|
12349
|
+
record: "record start|stop|status [--url <url>] [--name <flow>]",
|
|
12350
|
+
daemon: "daemon status [--port <port>]",
|
|
12351
|
+
replay: "replay <file> [--slow-mo <ms>] [--stop-on-error]",
|
|
12352
|
+
create: "create <name> [--template static|dynamic|login|api]",
|
|
12353
|
+
run: "run <file>",
|
|
12354
|
+
serve: "serve [--port <port>] [--token <token>]",
|
|
12355
|
+
remote: "remote <url> [command] [--token <token>]",
|
|
12356
|
+
convert: "convert <file> [--to js|py|sh]",
|
|
12357
|
+
extract: "extract <file> [--format json|yaml]",
|
|
12358
|
+
filter: "filter <file> [--include <type>] [--exclude <type>]",
|
|
12359
|
+
test: "test <name> [--cdp <endpoint>]",
|
|
12360
|
+
viewer: "viewer [--session <name>]",
|
|
12361
|
+
kill: "kill [--all]",
|
|
12362
|
+
net: "net [--cdp <endpoint>]"
|
|
12363
|
+
};
|
|
12364
|
+
const subHelp = SUBCOMMAND_HELP[command];
|
|
12365
|
+
if (subHelp) {
|
|
12366
|
+
console.log(`
|
|
12367
|
+
Usage: xbrowser ${subHelp}
|
|
12368
|
+
`);
|
|
12369
|
+
return;
|
|
12370
|
+
}
|
|
12262
12371
|
showMainHelp();
|
|
12263
12372
|
return;
|
|
12264
12373
|
}
|
|
@@ -12745,7 +12854,7 @@ async function main() {
|
|
|
12745
12854
|
const command = process.argv[2];
|
|
12746
12855
|
const isLongRunning = command === "preview" || command === "serve";
|
|
12747
12856
|
if (!isLongRunning) {
|
|
12748
|
-
const { ensureProcessCanExit } = await import("./browser-
|
|
12857
|
+
const { ensureProcessCanExit } = await import("./browser-PZX7PO23.js");
|
|
12749
12858
|
await ensureProcessCanExit().catch(() => {
|
|
12750
12859
|
});
|
|
12751
12860
|
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,53 @@ 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
|
+
const tableSelectors = [
|
|
2320
|
+
"table",
|
|
2321
|
+
'[role="table"]',
|
|
2322
|
+
'[role="grid"]',
|
|
2323
|
+
'[class*="el-table"]',
|
|
2324
|
+
// Element UI
|
|
2325
|
+
'[class*="ant-table"]',
|
|
2326
|
+
// Ant Design
|
|
2327
|
+
'[class*="MuiTable"]',
|
|
2328
|
+
// Material UI
|
|
2329
|
+
'[class*="table"]'
|
|
2330
|
+
// Generic table-like
|
|
2331
|
+
].join(",");
|
|
2332
|
+
const tables = document.querySelectorAll(tableSelectors);
|
|
2333
|
+
if (tables.length === 0) return "";
|
|
2334
|
+
return Array.from(tables).map((table) => {
|
|
2335
|
+
const rows = table.querySelectorAll('tr, [role="row"], [class*="row"]');
|
|
2336
|
+
if (rows.length === 0) return "";
|
|
2337
|
+
const mdRows = Array.from(rows).map((row) => {
|
|
2338
|
+
const cells = row.querySelectorAll('th, td, [role="columnheader"], [role="cell"], [class*="cell"], [class*="col"]');
|
|
2339
|
+
return "| " + Array.from(cells).map((c) => {
|
|
2340
|
+
const cellText = c.innerText?.trim().replace(/\n/g, " ") || "";
|
|
2341
|
+
return cellText.replace(/\|/g, "\\|") || "";
|
|
2342
|
+
}).join(" | ") + " |";
|
|
2343
|
+
}).join("\n");
|
|
2344
|
+
const headerRow = rows[0];
|
|
2345
|
+
const headerCells = headerRow.querySelectorAll('th, [role="columnheader"], [class*="header"]');
|
|
2346
|
+
const hasHeader = headerCells.length > 0;
|
|
2347
|
+
if (hasHeader && mdRows) {
|
|
2348
|
+
const headerCount = headerCells.length;
|
|
2349
|
+
const sep = "| " + Array(headerCount).fill("---").join(" | ") + " |";
|
|
2350
|
+
return mdRows.split("\n").map((line, i) => {
|
|
2351
|
+
if (i === 0) return line + "\n" + sep;
|
|
2352
|
+
return line;
|
|
2353
|
+
}).join("\n");
|
|
2354
|
+
}
|
|
2355
|
+
return mdRows;
|
|
2356
|
+
}).join("\n\n");
|
|
2357
|
+
});
|
|
2303
2358
|
content = htmlToMarkdown(html, { onlyMainContent: p.onlyMainContent });
|
|
2359
|
+
if (tablesMd) {
|
|
2360
|
+
content = tablesMd + "\n\n" + content;
|
|
2361
|
+
}
|
|
2304
2362
|
break;
|
|
2363
|
+
}
|
|
2305
2364
|
case "html":
|
|
2306
2365
|
content = html;
|
|
2307
2366
|
break;
|
|
@@ -5241,6 +5300,7 @@ function parseCommandChain(input, options) {
|
|
|
5241
5300
|
continue;
|
|
5242
5301
|
}
|
|
5243
5302
|
if (char === ";") {
|
|
5303
|
+
lastOperator = "sequence";
|
|
5244
5304
|
flushPipeline();
|
|
5245
5305
|
continue;
|
|
5246
5306
|
}
|
|
@@ -5295,6 +5355,7 @@ registerCommandDefinition("uncheck", ["selector"]);
|
|
|
5295
5355
|
registerCommandDefinition("hover", ["selector"]);
|
|
5296
5356
|
registerCommandDefinition("dblclick", ["selector"]);
|
|
5297
5357
|
registerCommandDefinition("wait", ["selector"]);
|
|
5358
|
+
registerCommandDefinition("waitForTimeout", ["timeout"]);
|
|
5298
5359
|
registerCommandDefinition("screenshot", []);
|
|
5299
5360
|
registerCommandDefinition("eval", ["expression"]);
|
|
5300
5361
|
registerCommandDefinition("scroll", ["direction"]);
|
|
@@ -6927,7 +6988,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6927
6988
|
}
|
|
6928
6989
|
let targetPageOverride = null;
|
|
6929
6990
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
6930
|
-
const { findTargetPage } = await import("./browser-
|
|
6991
|
+
const { findTargetPage } = await import("./browser-CWI6BXYK.js");
|
|
6931
6992
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
6932
6993
|
if (!targetPageOverride) {
|
|
6933
6994
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -8611,7 +8672,7 @@ function createRPCHandler() {
|
|
|
8611
8672
|
const isNewFormat = Array.isArray(parsed.actions);
|
|
8612
8673
|
if (isNewFormat) {
|
|
8613
8674
|
try {
|
|
8614
|
-
const { SessionReplayer } = await import("./session-replayer-
|
|
8675
|
+
const { SessionReplayer } = await import("./session-replayer-IXLSCF5U.js");
|
|
8615
8676
|
const replayer = new SessionReplayer({
|
|
8616
8677
|
page: session.page,
|
|
8617
8678
|
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,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) {
|
|
@@ -9947,7 +10018,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
9947
10018
|
type: options.type,
|
|
9948
10019
|
selector: options.selector || options.s,
|
|
9949
10020
|
base64: !!options.base64,
|
|
9950
|
-
output: options.output || options.o
|
|
10021
|
+
output: options.output || options.o || args[0]
|
|
9951
10022
|
};
|
|
9952
10023
|
break;
|
|
9953
10024
|
case "eval":
|
|
@@ -10184,6 +10255,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10184
10255
|
outputResult(result.data, mode);
|
|
10185
10256
|
console.error(`
|
|
10186
10257
|
\u26A0\uFE0F ${hint}`);
|
|
10258
|
+
process.exit(1);
|
|
10187
10259
|
} else {
|
|
10188
10260
|
outputResult(result.data, mode);
|
|
10189
10261
|
}
|
|
@@ -10278,11 +10350,6 @@ async function handleSession(args, options, mode, _cdpEndpoint) {
|
|
|
10278
10350
|
}
|
|
10279
10351
|
|
|
10280
10352
|
// src/cli/plugin-routes.ts
|
|
10281
|
-
var pluginLoader3 = null;
|
|
10282
|
-
function getPluginLoader2() {
|
|
10283
|
-
if (!pluginLoader3) pluginLoader3 = new XBrowserPluginLoader();
|
|
10284
|
-
return pluginLoader3;
|
|
10285
|
-
}
|
|
10286
10353
|
async function buildRuntimePluginInfo() {
|
|
10287
10354
|
const loader = await getPluginLoader();
|
|
10288
10355
|
const sites = loader.getCore().loader.getSites();
|
|
@@ -10540,6 +10607,10 @@ async function handlePlugin(args, options, mode) {
|
|
|
10540
10607
|
} else {
|
|
10541
10608
|
result = await installer.installWithMarketplaceFallback(source, installOpts);
|
|
10542
10609
|
}
|
|
10610
|
+
try {
|
|
10611
|
+
await (await getPluginLoader()).reloadPlugin(result.name);
|
|
10612
|
+
} catch {
|
|
10613
|
+
}
|
|
10543
10614
|
outputResult(
|
|
10544
10615
|
{ ok: true, name: result.name, source: result.source, path: result.path },
|
|
10545
10616
|
mode
|
|
@@ -10555,6 +10626,11 @@ async function handlePlugin(args, options, mode) {
|
|
|
10555
10626
|
outputError(`Plugin "${name}" is not installed. Use 'xbrowser plugin list' to see installed plugins.`);
|
|
10556
10627
|
}
|
|
10557
10628
|
await installer.uninstall(name);
|
|
10629
|
+
try {
|
|
10630
|
+
const loader = await getPluginLoader();
|
|
10631
|
+
await loader.reloadPlugin(name);
|
|
10632
|
+
} catch {
|
|
10633
|
+
}
|
|
10558
10634
|
outputResult({ ok: true, name }, mode);
|
|
10559
10635
|
break;
|
|
10560
10636
|
}
|
|
@@ -10608,7 +10684,7 @@ Total: ${enrichedPlugins.length} plugins`);
|
|
|
10608
10684
|
case "reload": {
|
|
10609
10685
|
const name = subArgs[0];
|
|
10610
10686
|
if (!name) outputError("Usage: xbrowser plugin reload <name>");
|
|
10611
|
-
await
|
|
10687
|
+
(await getPluginLoader()).reloadPlugin(name);
|
|
10612
10688
|
outputResult({ ok: true, name }, mode);
|
|
10613
10689
|
break;
|
|
10614
10690
|
}
|
|
@@ -12456,10 +12532,18 @@ function extractCdpFromArgv(argv) {
|
|
|
12456
12532
|
async function handleStdinMode(stdinCommands, argv) {
|
|
12457
12533
|
const chain = stdinCommands.join(" && ");
|
|
12458
12534
|
const cdpEndpoint = argv ? extractCdpFromArgv(argv) : void 0;
|
|
12459
|
-
const
|
|
12535
|
+
const sessionName = argv ? extractSessionNameFromArgv(argv) : "default";
|
|
12536
|
+
const chainResult = await executeChain(chain, { fileMode: true, cdpEndpoint, sessionName });
|
|
12460
12537
|
printChainResult(chainResult);
|
|
12461
12538
|
if (!chainResult.success) throw new Error("Command failed");
|
|
12462
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
|
+
}
|
|
12463
12547
|
async function handleEvalMode(argv) {
|
|
12464
12548
|
const evalCommands = parseEvalFlags(argv);
|
|
12465
12549
|
if (evalCommands.length === 0) return;
|
|
@@ -12599,6 +12683,31 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12599
12683
|
}
|
|
12600
12684
|
return;
|
|
12601
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
|
+
}
|
|
12602
12711
|
showMainHelp();
|
|
12603
12712
|
return;
|
|
12604
12713
|
}
|
|
@@ -15872,7 +15981,7 @@ var DataCollector = class {
|
|
|
15872
15981
|
return results;
|
|
15873
15982
|
}
|
|
15874
15983
|
async createBrowserContext() {
|
|
15875
|
-
const { launch } = await import("./cdp-driver-
|
|
15984
|
+
const { launch } = await import("./cdp-driver-S5STYUZZ.js");
|
|
15876
15985
|
const { browser } = await launch({
|
|
15877
15986
|
headless: true,
|
|
15878
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
|
},
|