@xbrowser/cli 1.5.4 → 1.6.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/dist/{browser-IS6DTR5Y.js → browser-4PIVOYCW.js} +2 -2
- package/dist/{browser-2FJ4OO4H.js → browser-KVTJQKBA.js} +1 -1
- package/dist/{browser-ZFW2XADE.js → browser-RJIRI2H5.js} +2 -2
- package/dist/{cdp-driver-QHTXQRQ2.js → cdp-driver-6EVYLHL2.js} +1 -1
- package/dist/{cdp-driver-MR6OG3YF.js → cdp-driver-MI7VCE44.js} +26 -4
- package/dist/{chunk-H7R352A2.js → chunk-35DOHDTA.js} +26 -4
- package/dist/{chunk-WCM4FNUB.js → chunk-6QGZN5U7.js} +1 -1
- package/dist/{chunk-MKEAO3XJ.js → chunk-IGWWPMEQ.js} +1 -1
- package/dist/{chunk-74QM55TC.js → chunk-VJNMAWPZ.js} +26 -4
- package/dist/cli.js +19 -7
- package/dist/daemon-main.js +5 -5
- package/dist/index.js +20 -8
- package/dist/{session-replayer-SD2MWGP5.js → session-replayer-RWFOWH3F.js} +1 -1
- package/package.json +1 -1
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
saveSessionDiskMeta,
|
|
21
21
|
setActivePage,
|
|
22
22
|
touchSession
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-6QGZN5U7.js";
|
|
24
|
+
import "./chunk-VJNMAWPZ.js";
|
|
25
25
|
import "./chunk-TNEN6VQ2.js";
|
|
26
26
|
import "./chunk-GDKLH7ZY.js";
|
|
27
27
|
import "./chunk-KFQGP6VL.js";
|
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
saveSessionDiskMeta,
|
|
21
21
|
setActivePage,
|
|
22
22
|
touchSession
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-IGWWPMEQ.js";
|
|
24
|
+
import "./chunk-VJNMAWPZ.js";
|
|
25
25
|
import "./chunk-TNEN6VQ2.js";
|
|
26
26
|
import "./chunk-GDKLH7ZY.js";
|
|
27
27
|
import "./chunk-ABXMBNQ6.js";
|
|
@@ -315,10 +315,23 @@ async function waitForActionable(page, selector, opts = {}) {
|
|
|
315
315
|
if (!rect2) throw new Error(`Element not found: ${selector}`);
|
|
316
316
|
return { nodeId: 0, rect: rect2 };
|
|
317
317
|
}
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
318
|
+
const deadline2 = Date.now() + timeout;
|
|
319
|
+
let lastError;
|
|
320
|
+
let nodeId = 0;
|
|
321
|
+
let rect = null;
|
|
322
|
+
while (Date.now() < deadline2) {
|
|
323
|
+
nodeId = await page.querySelector(selector);
|
|
324
|
+
if (!nodeId) {
|
|
325
|
+
lastError = `Element not found: ${selector}`;
|
|
326
|
+
await page.waitForTimeout(200);
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
rect = await page.getBoxModel(nodeId);
|
|
330
|
+
if (rect) break;
|
|
331
|
+
lastError = `Element has no box: ${selector}`;
|
|
332
|
+
await page.waitForTimeout(500);
|
|
333
|
+
}
|
|
334
|
+
if (!rect) throw new Error(lastError || `Element has no box: ${selector}`);
|
|
322
335
|
return { nodeId, rect };
|
|
323
336
|
}
|
|
324
337
|
const deadline = Date.now() + timeout;
|
|
@@ -1165,6 +1178,15 @@ var XBPageImpl = class _XBPageImpl {
|
|
|
1165
1178
|
if (result.errorText) {
|
|
1166
1179
|
throw new Error(`Navigation failed: ${result.errorText}`);
|
|
1167
1180
|
}
|
|
1181
|
+
if (waitUntil === "commit") {
|
|
1182
|
+
this._url = url;
|
|
1183
|
+
return {
|
|
1184
|
+
status: () => 0,
|
|
1185
|
+
ok: () => false,
|
|
1186
|
+
url: () => url,
|
|
1187
|
+
headers: () => ({})
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1168
1190
|
await this.waitForLoadState(waitUntil, timeout);
|
|
1169
1191
|
this._url = url;
|
|
1170
1192
|
const statusCode = 200;
|
|
@@ -320,10 +320,23 @@ async function waitForActionable(page, selector, opts = {}) {
|
|
|
320
320
|
if (!rect2) throw new Error(`Element not found: ${selector}`);
|
|
321
321
|
return { nodeId: 0, rect: rect2 };
|
|
322
322
|
}
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
323
|
+
const deadline2 = Date.now() + timeout;
|
|
324
|
+
let lastError;
|
|
325
|
+
let nodeId = 0;
|
|
326
|
+
let rect = null;
|
|
327
|
+
while (Date.now() < deadline2) {
|
|
328
|
+
nodeId = await page.querySelector(selector);
|
|
329
|
+
if (!nodeId) {
|
|
330
|
+
lastError = `Element not found: ${selector}`;
|
|
331
|
+
await page.waitForTimeout(200);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
rect = await page.getBoxModel(nodeId);
|
|
335
|
+
if (rect) break;
|
|
336
|
+
lastError = `Element has no box: ${selector}`;
|
|
337
|
+
await page.waitForTimeout(500);
|
|
338
|
+
}
|
|
339
|
+
if (!rect) throw new Error(lastError || `Element has no box: ${selector}`);
|
|
327
340
|
return { nodeId, rect };
|
|
328
341
|
}
|
|
329
342
|
const deadline = Date.now() + timeout;
|
|
@@ -1170,6 +1183,15 @@ var XBPageImpl = class _XBPageImpl {
|
|
|
1170
1183
|
if (result.errorText) {
|
|
1171
1184
|
throw new Error(`Navigation failed: ${result.errorText}`);
|
|
1172
1185
|
}
|
|
1186
|
+
if (waitUntil === "commit") {
|
|
1187
|
+
this._url = url;
|
|
1188
|
+
return {
|
|
1189
|
+
status: () => 0,
|
|
1190
|
+
ok: () => false,
|
|
1191
|
+
url: () => url,
|
|
1192
|
+
headers: () => ({})
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1173
1195
|
await this.waitForLoadState(waitUntil, timeout);
|
|
1174
1196
|
this._url = url;
|
|
1175
1197
|
const statusCode = 200;
|
|
@@ -314,10 +314,23 @@ async function waitForActionable(page, selector, opts = {}) {
|
|
|
314
314
|
if (!rect2) throw new Error(`Element not found: ${selector}`);
|
|
315
315
|
return { nodeId: 0, rect: rect2 };
|
|
316
316
|
}
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
317
|
+
const deadline2 = Date.now() + timeout;
|
|
318
|
+
let lastError;
|
|
319
|
+
let nodeId = 0;
|
|
320
|
+
let rect = null;
|
|
321
|
+
while (Date.now() < deadline2) {
|
|
322
|
+
nodeId = await page.querySelector(selector);
|
|
323
|
+
if (!nodeId) {
|
|
324
|
+
lastError = `Element not found: ${selector}`;
|
|
325
|
+
await page.waitForTimeout(200);
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
rect = await page.getBoxModel(nodeId);
|
|
329
|
+
if (rect) break;
|
|
330
|
+
lastError = `Element has no box: ${selector}`;
|
|
331
|
+
await page.waitForTimeout(500);
|
|
332
|
+
}
|
|
333
|
+
if (!rect) throw new Error(lastError || `Element has no box: ${selector}`);
|
|
321
334
|
return { nodeId, rect };
|
|
322
335
|
}
|
|
323
336
|
const deadline = Date.now() + timeout;
|
|
@@ -1164,6 +1177,15 @@ var XBPageImpl = class _XBPageImpl {
|
|
|
1164
1177
|
if (result.errorText) {
|
|
1165
1178
|
throw new Error(`Navigation failed: ${result.errorText}`);
|
|
1166
1179
|
}
|
|
1180
|
+
if (waitUntil === "commit") {
|
|
1181
|
+
this._url = url;
|
|
1182
|
+
return {
|
|
1183
|
+
status: () => 0,
|
|
1184
|
+
ok: () => false,
|
|
1185
|
+
url: () => url,
|
|
1186
|
+
headers: () => ({})
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1167
1189
|
await this.waitForLoadState(waitUntil, timeout);
|
|
1168
1190
|
this._url = url;
|
|
1169
1191
|
const statusCode = 200;
|
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-35DOHDTA.js";
|
|
29
29
|
import "./chunk-TNEN6VQ2.js";
|
|
30
30
|
import {
|
|
31
31
|
forwardCommandLog,
|
|
@@ -337,7 +337,7 @@ 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", "commit"]).optional(),
|
|
341
341
|
timeout: z.number().optional()
|
|
342
342
|
}),
|
|
343
343
|
result: z.object({
|
|
@@ -7137,7 +7137,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7137
7137
|
}
|
|
7138
7138
|
let targetPageOverride = null;
|
|
7139
7139
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7140
|
-
const { findTargetPage } = await import("./browser-
|
|
7140
|
+
const { findTargetPage } = await import("./browser-KVTJQKBA.js");
|
|
7141
7141
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7142
7142
|
if (!targetPageOverride) {
|
|
7143
7143
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -12557,6 +12557,11 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12557
12557
|
await handleChainInput(argv[0], argv);
|
|
12558
12558
|
return;
|
|
12559
12559
|
}
|
|
12560
|
+
const jsonBeforeChain = (argv[0] === "--json" || argv[0] === "--yaml") && argv[1] && isChainInput(argv[1]);
|
|
12561
|
+
if (jsonBeforeChain) {
|
|
12562
|
+
await handleChainInput(argv[1], argv);
|
|
12563
|
+
return;
|
|
12564
|
+
}
|
|
12560
12565
|
const globalFlags = /* @__PURE__ */ new Set(["--session", "--cdp", "--json", "--yaml", "--output", "--timeout", "--help", "-h", "--version"]);
|
|
12561
12566
|
let chainArgIdx = -1;
|
|
12562
12567
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -12581,11 +12586,13 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12581
12586
|
outputError(e instanceof Error ? e.message : String(e));
|
|
12582
12587
|
return;
|
|
12583
12588
|
}
|
|
12589
|
+
const hasJsonFlag = argv.some((a) => a === "--json" || a.startsWith("--json="));
|
|
12590
|
+
const hasYamlFlag = argv.some((a) => a === "--yaml" || a.startsWith("--yaml="));
|
|
12584
12591
|
const parsed = parseArgs(argv);
|
|
12585
12592
|
const { positional, options } = parsed;
|
|
12586
12593
|
const command = positional[0];
|
|
12587
12594
|
const cmdArgs = positional.slice(1);
|
|
12588
|
-
const mode = options.json ? "json" : options.yaml ? "yaml" : "text";
|
|
12595
|
+
const mode = options.json || hasJsonFlag ? "json" : options.yaml || hasYamlFlag ? "yaml" : "text";
|
|
12589
12596
|
const sessionName = options.session || process.env.XBROWSER_SESSION || "default";
|
|
12590
12597
|
const cdpEndpoint = options.cdp || process.env.XBROWSER_CDP;
|
|
12591
12598
|
if (options.version || options.v && positional.length === 0) {
|
|
@@ -12593,8 +12600,13 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12593
12600
|
return;
|
|
12594
12601
|
}
|
|
12595
12602
|
if (positional.length === 0) {
|
|
12596
|
-
|
|
12597
|
-
|
|
12603
|
+
const chainHints = [options.json, options.yaml, options.session, options.cdp].filter(Boolean).find((v) => typeof v === "string" && v.includes(" "));
|
|
12604
|
+
if (chainHints) {
|
|
12605
|
+
positional.push(chainHints);
|
|
12606
|
+
} else {
|
|
12607
|
+
showMainHelp();
|
|
12608
|
+
return;
|
|
12609
|
+
}
|
|
12598
12610
|
}
|
|
12599
12611
|
if ((options.help || options.h) && positional.length > 0) {
|
|
12600
12612
|
const loader = await getPluginLoader();
|
|
@@ -13210,7 +13222,7 @@ async function main() {
|
|
|
13210
13222
|
const command = process.argv[2];
|
|
13211
13223
|
const isLongRunning = command === "preview" || command === "serve";
|
|
13212
13224
|
if (!isLongRunning) {
|
|
13213
|
-
const { ensureProcessCanExit } = await import("./browser-
|
|
13225
|
+
const { ensureProcessCanExit } = await import("./browser-KVTJQKBA.js");
|
|
13214
13226
|
await ensureProcessCanExit().catch(() => {
|
|
13215
13227
|
});
|
|
13216
13228
|
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-6QGZN5U7.js";
|
|
25
|
+
import "./chunk-VJNMAWPZ.js";
|
|
26
26
|
import "./chunk-TNEN6VQ2.js";
|
|
27
27
|
import {
|
|
28
28
|
getPluginLoader
|
|
@@ -298,7 +298,7 @@ var gotoCommand = registerCommand({
|
|
|
298
298
|
scope: "page",
|
|
299
299
|
parameters: z.object({
|
|
300
300
|
url: z.string(),
|
|
301
|
-
waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).optional(),
|
|
301
|
+
waitUntil: z.enum(["load", "domcontentloaded", "networkidle", "commit"]).optional(),
|
|
302
302
|
timeout: z.number().optional()
|
|
303
303
|
}),
|
|
304
304
|
result: z.object({
|
|
@@ -6668,7 +6668,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6668
6668
|
}
|
|
6669
6669
|
let targetPageOverride = null;
|
|
6670
6670
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
6671
|
-
const { findTargetPage } = await import("./browser-
|
|
6671
|
+
const { findTargetPage } = await import("./browser-4PIVOYCW.js");
|
|
6672
6672
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
6673
6673
|
if (!targetPageOverride) {
|
|
6674
6674
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -8384,7 +8384,7 @@ function createRPCHandler() {
|
|
|
8384
8384
|
const isNewFormat = Array.isArray(parsed.actions);
|
|
8385
8385
|
if (isNewFormat) {
|
|
8386
8386
|
try {
|
|
8387
|
-
const { SessionReplayer } = await import("./session-replayer-
|
|
8387
|
+
const { SessionReplayer } = await import("./session-replayer-RWFOWH3F.js");
|
|
8388
8388
|
const replayer = new SessionReplayer({
|
|
8389
8389
|
page: session.page,
|
|
8390
8390
|
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-IGWWPMEQ.js";
|
|
85
|
+
import "./chunk-VJNMAWPZ.js";
|
|
86
86
|
import "./chunk-TNEN6VQ2.js";
|
|
87
87
|
import {
|
|
88
88
|
errMsg
|
|
@@ -377,7 +377,7 @@ 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", "commit"]).optional(),
|
|
381
381
|
timeout: z.number().optional()
|
|
382
382
|
}),
|
|
383
383
|
result: z.object({
|
|
@@ -7457,7 +7457,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7457
7457
|
}
|
|
7458
7458
|
let targetPageOverride = null;
|
|
7459
7459
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7460
|
-
const { findTargetPage } = await import("./browser-
|
|
7460
|
+
const { findTargetPage } = await import("./browser-RJIRI2H5.js");
|
|
7461
7461
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7462
7462
|
if (!targetPageOverride) {
|
|
7463
7463
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -12880,6 +12880,11 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12880
12880
|
await handleChainInput(argv[0], argv);
|
|
12881
12881
|
return;
|
|
12882
12882
|
}
|
|
12883
|
+
const jsonBeforeChain = (argv[0] === "--json" || argv[0] === "--yaml") && argv[1] && isChainInput(argv[1]);
|
|
12884
|
+
if (jsonBeforeChain) {
|
|
12885
|
+
await handleChainInput(argv[1], argv);
|
|
12886
|
+
return;
|
|
12887
|
+
}
|
|
12883
12888
|
const globalFlags = /* @__PURE__ */ new Set(["--session", "--cdp", "--json", "--yaml", "--output", "--timeout", "--help", "-h", "--version"]);
|
|
12884
12889
|
let chainArgIdx = -1;
|
|
12885
12890
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -12904,11 +12909,13 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12904
12909
|
outputError(e instanceof Error ? e.message : String(e));
|
|
12905
12910
|
return;
|
|
12906
12911
|
}
|
|
12912
|
+
const hasJsonFlag = argv.some((a) => a === "--json" || a.startsWith("--json="));
|
|
12913
|
+
const hasYamlFlag = argv.some((a) => a === "--yaml" || a.startsWith("--yaml="));
|
|
12907
12914
|
const parsed = parseArgs(argv);
|
|
12908
12915
|
const { positional, options } = parsed;
|
|
12909
12916
|
const command = positional[0];
|
|
12910
12917
|
const cmdArgs = positional.slice(1);
|
|
12911
|
-
const mode = options.json ? "json" : options.yaml ? "yaml" : "text";
|
|
12918
|
+
const mode = options.json || hasJsonFlag ? "json" : options.yaml || hasYamlFlag ? "yaml" : "text";
|
|
12912
12919
|
const sessionName = options.session || process.env.XBROWSER_SESSION || "default";
|
|
12913
12920
|
const cdpEndpoint = options.cdp || process.env.XBROWSER_CDP;
|
|
12914
12921
|
if (options.version || options.v && positional.length === 0) {
|
|
@@ -12916,8 +12923,13 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12916
12923
|
return;
|
|
12917
12924
|
}
|
|
12918
12925
|
if (positional.length === 0) {
|
|
12919
|
-
|
|
12920
|
-
|
|
12926
|
+
const chainHints = [options.json, options.yaml, options.session, options.cdp].filter(Boolean).find((v) => typeof v === "string" && v.includes(" "));
|
|
12927
|
+
if (chainHints) {
|
|
12928
|
+
positional.push(chainHints);
|
|
12929
|
+
} else {
|
|
12930
|
+
showMainHelp();
|
|
12931
|
+
return;
|
|
12932
|
+
}
|
|
12921
12933
|
}
|
|
12922
12934
|
if ((options.help || options.h) && positional.length > 0) {
|
|
12923
12935
|
const loader = await getPluginLoader();
|
|
@@ -16320,7 +16332,7 @@ var DataCollector = class {
|
|
|
16320
16332
|
return results;
|
|
16321
16333
|
}
|
|
16322
16334
|
async createBrowserContext() {
|
|
16323
|
-
const { launch } = await import("./cdp-driver-
|
|
16335
|
+
const { launch } = await import("./cdp-driver-6EVYLHL2.js");
|
|
16324
16336
|
const { browser } = await launch({
|
|
16325
16337
|
headless: true,
|
|
16326
16338
|
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-6EVYLHL2.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.6.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": {
|