@xbrowser/cli 1.0.9 → 1.1.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/dist/{browser-6QN42A4K.js → browser-5FR3B57B.js} +1 -1
- package/dist/{browser-MUPES4UY.js → browser-BX4HZOUT.js} +1 -1
- package/dist/{browser-AN6MKGOD.js → browser-CFHOD5GY.js} +1 -1
- package/dist/{chunk-OPRXFZVE.js → chunk-AT4PHAJY.js} +16 -2
- package/dist/{chunk-JUNEBEGF.js → chunk-GOKTOYWM.js} +1 -1
- package/dist/{chunk-HRTXMFW4.js → chunk-ISOSRTTV.js} +1 -1
- package/dist/{chunk-EUHVZVVL.js → chunk-MWBVZWXA.js} +1 -1
- package/dist/cli.js +22 -11
- package/dist/daemon-main.js +6 -4
- package/dist/{filter-T7DSZ2X7.js → filter-3JQWBM5F.js} +1 -1
- package/dist/{filter-KCFO4RSV.js → filter-EDTFGLS5.js} +16 -2
- package/dist/index.js +22 -11
- package/package.json +1 -1
|
@@ -38,9 +38,23 @@ function filterRecording(inputPath, outputPath, excludeTypes) {
|
|
|
38
38
|
return { originalCount, filteredCount, removed, percentage };
|
|
39
39
|
}
|
|
40
40
|
function parseExcludeTypes(args) {
|
|
41
|
-
for (
|
|
41
|
+
for (let i = 0; i < args.length; i++) {
|
|
42
|
+
const arg = args[i];
|
|
42
43
|
if (arg.startsWith("--exclude-types=")) {
|
|
43
|
-
return arg.replace("--exclude-types=", "").split(",");
|
|
44
|
+
return arg.replace("--exclude-types=", "").split(",").map((s) => s.trim());
|
|
45
|
+
}
|
|
46
|
+
if (arg === "--exclude-types" && args[i + 1]) {
|
|
47
|
+
return args[i + 1].split(",").map((s) => s.trim());
|
|
48
|
+
}
|
|
49
|
+
if (arg.startsWith("--exclude=")) {
|
|
50
|
+
return arg.replace("--exclude=", "").split(",").map((s) => s.trim());
|
|
51
|
+
}
|
|
52
|
+
if (arg === "--exclude" && args[i + 1]) {
|
|
53
|
+
const types = [];
|
|
54
|
+
for (let j = i + 1; j < args.length && !args[j].startsWith("-"); j++) {
|
|
55
|
+
types.push(...args[j].split(",").map((s) => s.trim()));
|
|
56
|
+
}
|
|
57
|
+
if (types.length > 0) return types;
|
|
44
58
|
}
|
|
45
59
|
}
|
|
46
60
|
return void 0;
|
|
@@ -1538,7 +1538,7 @@ async function getCDPTargets(cdpEndpoint) {
|
|
|
1538
1538
|
}
|
|
1539
1539
|
async function findTargetPage(cdpEndpoint, target) {
|
|
1540
1540
|
const targets = await getCDPTargets(cdpEndpoint);
|
|
1541
|
-
const pages = targets.filter((t) => t.url && !t.url.startsWith("about:blank") && !t.url.startsWith("chrome://"));
|
|
1541
|
+
const pages = targets.filter((t) => t.url && !t.url.startsWith("about:blank") && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://"));
|
|
1542
1542
|
const byId = pages.find((t) => t.id === target);
|
|
1543
1543
|
if (byId) return { pageId: byId.id, wsUrl: byId.webSocketDebuggerUrl, title: byId.title, url: byId.url };
|
|
1544
1544
|
const lowerTarget = target.toLowerCase();
|
|
@@ -4284,7 +4284,7 @@ async function getCDPTargets2(cdpEndpoint) {
|
|
|
4284
4284
|
}
|
|
4285
4285
|
async function findTargetPage(cdpEndpoint, target) {
|
|
4286
4286
|
const targets = await getCDPTargets2(cdpEndpoint);
|
|
4287
|
-
const pages = targets.filter((t) => t.url && !t.url.startsWith("about:blank") && !t.url.startsWith("chrome://"));
|
|
4287
|
+
const pages = targets.filter((t) => t.url && !t.url.startsWith("about:blank") && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://"));
|
|
4288
4288
|
const byId = pages.find((t) => t.id === target);
|
|
4289
4289
|
if (byId) return { pageId: byId.id, wsUrl: byId.webSocketDebuggerUrl, title: byId.title, url: byId.url };
|
|
4290
4290
|
const lowerTarget = target.toLowerCase();
|
|
@@ -173,7 +173,7 @@ async function getCDPTargets(cdpEndpoint) {
|
|
|
173
173
|
}
|
|
174
174
|
async function findTargetPage(cdpEndpoint, target) {
|
|
175
175
|
const targets = await getCDPTargets(cdpEndpoint);
|
|
176
|
-
const pages = targets.filter((t) => t.url && !t.url.startsWith("about:blank") && !t.url.startsWith("chrome://"));
|
|
176
|
+
const pages = targets.filter((t) => t.url && !t.url.startsWith("about:blank") && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://"));
|
|
177
177
|
const byId = pages.find((t) => t.id === target);
|
|
178
178
|
if (byId) return { pageId: byId.id, wsUrl: byId.webSocketDebuggerUrl, title: byId.title, url: byId.url };
|
|
179
179
|
const lowerTarget = target.toLowerCase();
|
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-ISOSRTTV.js";
|
|
29
29
|
import "./chunk-TNEN6VQ2.js";
|
|
30
30
|
import {
|
|
31
31
|
forwardCommandLog,
|
|
@@ -337,7 +337,8 @@ var gotoCommand = registerCommand({
|
|
|
337
337
|
scope: "page",
|
|
338
338
|
parameters: z.object({
|
|
339
339
|
url: z.string(),
|
|
340
|
-
waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).optional()
|
|
340
|
+
waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).optional(),
|
|
341
|
+
timeout: z.number().optional()
|
|
341
342
|
}),
|
|
342
343
|
result: z.object({
|
|
343
344
|
url: z.string(),
|
|
@@ -355,7 +356,8 @@ var gotoCommand = registerCommand({
|
|
|
355
356
|
}
|
|
356
357
|
}
|
|
357
358
|
const response = await ctx.page.goto(url, {
|
|
358
|
-
waitUntil: p.waitUntil || "domcontentloaded"
|
|
359
|
+
waitUntil: p.waitUntil || "domcontentloaded",
|
|
360
|
+
...p.timeout ? { timeout: p.timeout } : {}
|
|
359
361
|
});
|
|
360
362
|
const ssr = await detectSsr(ctx.page);
|
|
361
363
|
return ok({ url, status: response?.status(), ...ssr ? { ssr } : {} });
|
|
@@ -6967,7 +6969,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6967
6969
|
}
|
|
6968
6970
|
let targetPageOverride = null;
|
|
6969
6971
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
6970
|
-
const { findTargetPage } = await import("./browser-
|
|
6972
|
+
const { findTargetPage } = await import("./browser-CFHOD5GY.js");
|
|
6971
6973
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
6972
6974
|
if (!targetPageOverride) {
|
|
6973
6975
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -9070,7 +9072,7 @@ var createBuiltin = {
|
|
|
9070
9072
|
);
|
|
9071
9073
|
process.exit(1);
|
|
9072
9074
|
}
|
|
9073
|
-
const targetDir = path2.join(ctx.cwd, projectName);
|
|
9075
|
+
const targetDir = path2.isAbsolute(projectName) ? projectName : path2.join(ctx.cwd, projectName);
|
|
9074
9076
|
if (fs2.existsSync(targetDir) && !options["force"]) {
|
|
9075
9077
|
console.error(`Directory "${projectName}" already exists. Use --force to overwrite.`);
|
|
9076
9078
|
process.exit(1);
|
|
@@ -9593,7 +9595,8 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
9593
9595
|
cmdName = "goto";
|
|
9594
9596
|
params = {
|
|
9595
9597
|
url: /^https?:\/\//i.test(args[0]) || /^wss?:\/\//i.test(args[0]) ? args[0] : "https://" + args[0],
|
|
9596
|
-
waitUntil: options.waitUntil
|
|
9598
|
+
waitUntil: options.waitUntil,
|
|
9599
|
+
...options.timeout ? { timeout: Number(options.timeout) } : {}
|
|
9597
9600
|
};
|
|
9598
9601
|
break;
|
|
9599
9602
|
case "screenshot":
|
|
@@ -9843,6 +9846,14 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
9843
9846
|
outputResult(result.data, mode);
|
|
9844
9847
|
}
|
|
9845
9848
|
}
|
|
9849
|
+
const outputFile = options.output;
|
|
9850
|
+
if (outputFile && result.success && result.data) {
|
|
9851
|
+
const { writeFileSync: writeFileSync11 } = await import("fs");
|
|
9852
|
+
const content = typeof result.data === "string" ? result.data : result.data.content || result.data.text || JSON.stringify(result.data, null, 2);
|
|
9853
|
+
writeFileSync11(outputFile, content, "utf-8");
|
|
9854
|
+
console.log(`
|
|
9855
|
+
\u{1F4C4} Written to ${outputFile}`);
|
|
9856
|
+
}
|
|
9846
9857
|
}
|
|
9847
9858
|
|
|
9848
9859
|
// src/cli/session-routes.ts
|
|
@@ -10646,7 +10657,7 @@ async function handleFilter(args, _mode) {
|
|
|
10646
10657
|
console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude-types=type1,type2]");
|
|
10647
10658
|
process.exit(1);
|
|
10648
10659
|
}
|
|
10649
|
-
const { filterRecording, parseExcludeTypes } = await import("./filter-
|
|
10660
|
+
const { filterRecording, parseExcludeTypes } = await import("./filter-EDTFGLS5.js");
|
|
10650
10661
|
const excludeTypes = parseExcludeTypes(args.slice(2));
|
|
10651
10662
|
const result = filterRecording(filePath, outputPath, excludeTypes);
|
|
10652
10663
|
console.log(`Filtered ${filePath} -> ${outputPath}`);
|
|
@@ -12272,13 +12283,13 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12272
12283
|
handleConfig(cmdArgs, options);
|
|
12273
12284
|
break;
|
|
12274
12285
|
case "convert":
|
|
12275
|
-
handleConvert(cmdArgs, mode);
|
|
12286
|
+
await handleConvert(cmdArgs, mode);
|
|
12276
12287
|
break;
|
|
12277
12288
|
case "extract":
|
|
12278
|
-
handleExtract(cmdArgs, mode);
|
|
12289
|
+
await handleExtract(cmdArgs, mode);
|
|
12279
12290
|
break;
|
|
12280
12291
|
case "filter":
|
|
12281
|
-
handleFilter(cmdArgs, mode);
|
|
12292
|
+
await handleFilter(cmdArgs, mode);
|
|
12282
12293
|
break;
|
|
12283
12294
|
case "run":
|
|
12284
12295
|
if (!cmdArgs[0]) {
|
|
@@ -12727,7 +12738,7 @@ async function main() {
|
|
|
12727
12738
|
const command = process.argv[2];
|
|
12728
12739
|
const isLongRunning = command === "preview" || command === "serve";
|
|
12729
12740
|
if (!isLongRunning) {
|
|
12730
|
-
const { ensureProcessCanExit } = await import("./browser-
|
|
12741
|
+
const { ensureProcessCanExit } = await import("./browser-CFHOD5GY.js");
|
|
12731
12742
|
await ensureProcessCanExit().catch(() => {
|
|
12732
12743
|
});
|
|
12733
12744
|
process.exit(exitCode);
|
package/dist/daemon-main.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
resolveLaunchOpts,
|
|
22
22
|
saveSessionDiskMeta,
|
|
23
23
|
setActivePage
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-GOKTOYWM.js";
|
|
25
25
|
import "./chunk-IX4JY6OO.js";
|
|
26
26
|
import "./chunk-TNEN6VQ2.js";
|
|
27
27
|
import {
|
|
@@ -295,7 +295,8 @@ var gotoCommand = registerCommand({
|
|
|
295
295
|
scope: "page",
|
|
296
296
|
parameters: z.object({
|
|
297
297
|
url: z.string(),
|
|
298
|
-
waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).optional()
|
|
298
|
+
waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).optional(),
|
|
299
|
+
timeout: z.number().optional()
|
|
299
300
|
}),
|
|
300
301
|
result: z.object({
|
|
301
302
|
url: z.string(),
|
|
@@ -313,7 +314,8 @@ var gotoCommand = registerCommand({
|
|
|
313
314
|
}
|
|
314
315
|
}
|
|
315
316
|
const response = await ctx.page.goto(url, {
|
|
316
|
-
waitUntil: p.waitUntil || "domcontentloaded"
|
|
317
|
+
waitUntil: p.waitUntil || "domcontentloaded",
|
|
318
|
+
...p.timeout ? { timeout: p.timeout } : {}
|
|
317
319
|
});
|
|
318
320
|
const ssr = await detectSsr(ctx.page);
|
|
319
321
|
return ok({ url, status: response?.status(), ...ssr ? { ssr } : {} });
|
|
@@ -6925,7 +6927,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6925
6927
|
}
|
|
6926
6928
|
let targetPageOverride = null;
|
|
6927
6929
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
6928
|
-
const { findTargetPage } = await import("./browser-
|
|
6930
|
+
const { findTargetPage } = await import("./browser-BX4HZOUT.js");
|
|
6929
6931
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
6930
6932
|
if (!targetPageOverride) {
|
|
6931
6933
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -40,9 +40,23 @@ function filterRecording(inputPath, outputPath, excludeTypes) {
|
|
|
40
40
|
return { originalCount, filteredCount, removed, percentage };
|
|
41
41
|
}
|
|
42
42
|
function parseExcludeTypes(args) {
|
|
43
|
-
for (
|
|
43
|
+
for (let i = 0; i < args.length; i++) {
|
|
44
|
+
const arg = args[i];
|
|
44
45
|
if (arg.startsWith("--exclude-types=")) {
|
|
45
|
-
return arg.replace("--exclude-types=", "").split(",");
|
|
46
|
+
return arg.replace("--exclude-types=", "").split(",").map((s) => s.trim());
|
|
47
|
+
}
|
|
48
|
+
if (arg === "--exclude-types" && args[i + 1]) {
|
|
49
|
+
return args[i + 1].split(",").map((s) => s.trim());
|
|
50
|
+
}
|
|
51
|
+
if (arg.startsWith("--exclude=")) {
|
|
52
|
+
return arg.replace("--exclude=", "").split(",").map((s) => s.trim());
|
|
53
|
+
}
|
|
54
|
+
if (arg === "--exclude" && args[i + 1]) {
|
|
55
|
+
const types = [];
|
|
56
|
+
for (let j = i + 1; j < args.length && !args[j].startsWith("-"); j++) {
|
|
57
|
+
types.push(...args[j].split(",").map((s) => s.trim()));
|
|
58
|
+
}
|
|
59
|
+
if (types.length > 0) return types;
|
|
46
60
|
}
|
|
47
61
|
}
|
|
48
62
|
return void 0;
|
package/dist/index.js
CHANGED
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
import {
|
|
54
54
|
filterRecording,
|
|
55
55
|
parseExcludeTypes
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-AT4PHAJY.js";
|
|
57
57
|
import {
|
|
58
58
|
SessionRecorder
|
|
59
59
|
} from "./chunk-ACFE6PKF.js";
|
|
@@ -81,7 +81,7 @@ import {
|
|
|
81
81
|
resolveLaunchOpts,
|
|
82
82
|
saveSessionDiskMeta,
|
|
83
83
|
setActivePage
|
|
84
|
-
} from "./chunk-
|
|
84
|
+
} from "./chunk-MWBVZWXA.js";
|
|
85
85
|
import "./chunk-IX4JY6OO.js";
|
|
86
86
|
import "./chunk-TNEN6VQ2.js";
|
|
87
87
|
import {
|
|
@@ -377,7 +377,8 @@ var gotoCommand = registerCommand({
|
|
|
377
377
|
scope: "page",
|
|
378
378
|
parameters: z.object({
|
|
379
379
|
url: z.string(),
|
|
380
|
-
waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).optional()
|
|
380
|
+
waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).optional(),
|
|
381
|
+
timeout: z.number().optional()
|
|
381
382
|
}),
|
|
382
383
|
result: z.object({
|
|
383
384
|
url: z.string(),
|
|
@@ -395,7 +396,8 @@ var gotoCommand = registerCommand({
|
|
|
395
396
|
}
|
|
396
397
|
}
|
|
397
398
|
const response = await ctx.page.goto(url, {
|
|
398
|
-
waitUntil: p.waitUntil || "domcontentloaded"
|
|
399
|
+
waitUntil: p.waitUntil || "domcontentloaded",
|
|
400
|
+
...p.timeout ? { timeout: p.timeout } : {}
|
|
399
401
|
});
|
|
400
402
|
const ssr = await detectSsr(ctx.page);
|
|
401
403
|
return ok({ url, status: response?.status(), ...ssr ? { ssr } : {} });
|
|
@@ -7287,7 +7289,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7287
7289
|
}
|
|
7288
7290
|
let targetPageOverride = null;
|
|
7289
7291
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7290
|
-
const { findTargetPage } = await import("./browser-
|
|
7292
|
+
const { findTargetPage } = await import("./browser-5FR3B57B.js");
|
|
7291
7293
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7292
7294
|
if (!targetPageOverride) {
|
|
7293
7295
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -9405,7 +9407,7 @@ var createBuiltin = {
|
|
|
9405
9407
|
);
|
|
9406
9408
|
process.exit(1);
|
|
9407
9409
|
}
|
|
9408
|
-
const targetDir = path2.join(ctx.cwd, projectName);
|
|
9410
|
+
const targetDir = path2.isAbsolute(projectName) ? projectName : path2.join(ctx.cwd, projectName);
|
|
9409
9411
|
if (fs2.existsSync(targetDir) && !options["force"]) {
|
|
9410
9412
|
console.error(`Directory "${projectName}" already exists. Use --force to overwrite.`);
|
|
9411
9413
|
process.exit(1);
|
|
@@ -9933,7 +9935,8 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
9933
9935
|
cmdName = "goto";
|
|
9934
9936
|
params = {
|
|
9935
9937
|
url: /^https?:\/\//i.test(args[0]) || /^wss?:\/\//i.test(args[0]) ? args[0] : "https://" + args[0],
|
|
9936
|
-
waitUntil: options.waitUntil
|
|
9938
|
+
waitUntil: options.waitUntil,
|
|
9939
|
+
...options.timeout ? { timeout: Number(options.timeout) } : {}
|
|
9937
9940
|
};
|
|
9938
9941
|
break;
|
|
9939
9942
|
case "screenshot":
|
|
@@ -10183,6 +10186,14 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10183
10186
|
outputResult(result.data, mode);
|
|
10184
10187
|
}
|
|
10185
10188
|
}
|
|
10189
|
+
const outputFile = options.output;
|
|
10190
|
+
if (outputFile && result.success && result.data) {
|
|
10191
|
+
const { writeFileSync: writeFileSync12 } = await import("fs");
|
|
10192
|
+
const content = typeof result.data === "string" ? result.data : result.data.content || result.data.text || JSON.stringify(result.data, null, 2);
|
|
10193
|
+
writeFileSync12(outputFile, content, "utf-8");
|
|
10194
|
+
console.log(`
|
|
10195
|
+
\u{1F4C4} Written to ${outputFile}`);
|
|
10196
|
+
}
|
|
10186
10197
|
}
|
|
10187
10198
|
|
|
10188
10199
|
// src/cli/session-routes.ts
|
|
@@ -10986,7 +10997,7 @@ async function handleFilter(args, _mode) {
|
|
|
10986
10997
|
console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude-types=type1,type2]");
|
|
10987
10998
|
process.exit(1);
|
|
10988
10999
|
}
|
|
10989
|
-
const { filterRecording: filterRecording2, parseExcludeTypes: parseExcludeTypes2 } = await import("./filter-
|
|
11000
|
+
const { filterRecording: filterRecording2, parseExcludeTypes: parseExcludeTypes2 } = await import("./filter-3JQWBM5F.js");
|
|
10990
11001
|
const excludeTypes = parseExcludeTypes2(args.slice(2));
|
|
10991
11002
|
const result = filterRecording2(filePath, outputPath, excludeTypes);
|
|
10992
11003
|
console.log(`Filtered ${filePath} -> ${outputPath}`);
|
|
@@ -12612,13 +12623,13 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12612
12623
|
handleConfig(cmdArgs, options);
|
|
12613
12624
|
break;
|
|
12614
12625
|
case "convert":
|
|
12615
|
-
handleConvert(cmdArgs, mode);
|
|
12626
|
+
await handleConvert(cmdArgs, mode);
|
|
12616
12627
|
break;
|
|
12617
12628
|
case "extract":
|
|
12618
|
-
handleExtract(cmdArgs, mode);
|
|
12629
|
+
await handleExtract(cmdArgs, mode);
|
|
12619
12630
|
break;
|
|
12620
12631
|
case "filter":
|
|
12621
|
-
handleFilter(cmdArgs, mode);
|
|
12632
|
+
await handleFilter(cmdArgs, mode);
|
|
12622
12633
|
break;
|
|
12623
12634
|
case "run":
|
|
12624
12635
|
if (!cmdArgs[0]) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xbrowser/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.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": {
|