@xbrowser/cli 1.9.0 → 1.9.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/{chunk-GJAV3QGG.js → chunk-ANVL2ID2.js} +27 -3
- package/dist/{chunk-2QQDTXDL.js → chunk-PIWNMC36.js} +13 -1
- package/dist/{chunk-MJFYLKGL.js → chunk-X46HDAOT.js} +27 -3
- package/dist/cli.js +246 -132
- package/dist/daemon-main.js +142 -107
- package/dist/{extract-RM62AJXW.js → extract-DSU2RVMN.js} +1 -1
- package/dist/{extract-O46CC533.js → extract-EUWPRSKH.js} +27 -3
- package/dist/{filter-K6FGRJQU.js → filter-3DXC6432.js} +1 -1
- package/dist/{filter-TAAYMSYI.js → filter-7YOPVPVC.js} +27 -3
- package/dist/index.js +247 -133
- package/dist/{plugin-singleton-SYJF6BD6.js → plugin-singleton-FCNDN3FX.js} +1 -1
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -182,7 +182,7 @@ function asZodSchema(value) {
|
|
|
182
182
|
// src/executor.ts
|
|
183
183
|
import {
|
|
184
184
|
ok as ok25,
|
|
185
|
-
fail as
|
|
185
|
+
fail as fail11,
|
|
186
186
|
isCommandResult,
|
|
187
187
|
CompositeStorage as CompositeStorage2,
|
|
188
188
|
TipCollector as TipCollector2,
|
|
@@ -953,7 +953,7 @@ var evaluateCommand = registerCommand({
|
|
|
953
953
|
|
|
954
954
|
// src/commands/storage.ts
|
|
955
955
|
import { z as z8 } from "zod";
|
|
956
|
-
import { ok as ok8 } from "@dyyz1993/xcli-core";
|
|
956
|
+
import { ok as ok8, fail as fail2 } from "@dyyz1993/xcli-core";
|
|
957
957
|
var getCookiesCommand = registerCommand({
|
|
958
958
|
name: "get-cookies",
|
|
959
959
|
description: "Get all cookies for the current page",
|
|
@@ -975,6 +975,7 @@ var setCookieCommand = registerCommand({
|
|
|
975
975
|
value: z8.coerce.string(),
|
|
976
976
|
domain: z8.coerce.string().optional(),
|
|
977
977
|
path: z8.coerce.string().optional(),
|
|
978
|
+
url: z8.string().optional().describe("Cookie URL (alternative to domain)"),
|
|
978
979
|
expires: z8.number().optional(),
|
|
979
980
|
httpOnly: z8.boolean().optional(),
|
|
980
981
|
secure: z8.boolean().optional(),
|
|
@@ -994,6 +995,9 @@ var setCookieCommand = registerCommand({
|
|
|
994
995
|
}
|
|
995
996
|
}
|
|
996
997
|
}
|
|
998
|
+
if (!cookie.domain && !cookie.url) {
|
|
999
|
+
return fail2("set-cookie requires --domain or --url, or a non-blank page URL to infer from");
|
|
1000
|
+
}
|
|
997
1001
|
await ctx.browserContext.addCookies([cookie]);
|
|
998
1002
|
return ok8({ name: p.name });
|
|
999
1003
|
}
|
|
@@ -1020,19 +1024,23 @@ var getLocalStorageCommand = registerCommand({
|
|
|
1020
1024
|
z8.object({ data: z8.record(z8.string()) })
|
|
1021
1025
|
]),
|
|
1022
1026
|
handler: async (p, ctx) => {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
const data = await ctx.page.evaluate(() => {
|
|
1028
|
-
const entries = {};
|
|
1029
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
1030
|
-
const key = localStorage.key(i);
|
|
1031
|
-
if (key) entries[key] = localStorage.getItem(key) ?? "";
|
|
1027
|
+
try {
|
|
1028
|
+
if (p.key) {
|
|
1029
|
+
const value = await ctx.page.evaluate((k) => localStorage.getItem(k), p.key);
|
|
1030
|
+
return ok8({ key: p.key, value });
|
|
1032
1031
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1032
|
+
const data = await ctx.page.evaluate(() => {
|
|
1033
|
+
const entries = {};
|
|
1034
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
1035
|
+
const key = localStorage.key(i);
|
|
1036
|
+
if (key) entries[key] = localStorage.getItem(key) ?? "";
|
|
1037
|
+
}
|
|
1038
|
+
return entries;
|
|
1039
|
+
});
|
|
1040
|
+
return ok8({ data });
|
|
1041
|
+
} catch (e) {
|
|
1042
|
+
return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
|
|
1043
|
+
}
|
|
1036
1044
|
}
|
|
1037
1045
|
});
|
|
1038
1046
|
var setLocalStorageCommand = registerCommand({
|
|
@@ -1045,13 +1053,17 @@ var setLocalStorageCommand = registerCommand({
|
|
|
1045
1053
|
}),
|
|
1046
1054
|
result: z8.object({ key: z8.string() }),
|
|
1047
1055
|
handler: async (p, ctx) => {
|
|
1048
|
-
|
|
1049
|
-
(
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1056
|
+
try {
|
|
1057
|
+
await ctx.page.evaluate(
|
|
1058
|
+
(args) => {
|
|
1059
|
+
localStorage.setItem(args.key, args.value);
|
|
1060
|
+
},
|
|
1061
|
+
{ key: p.key, value: p.value }
|
|
1062
|
+
);
|
|
1063
|
+
return ok8({ key: p.key });
|
|
1064
|
+
} catch (e) {
|
|
1065
|
+
return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
|
|
1066
|
+
}
|
|
1055
1067
|
}
|
|
1056
1068
|
});
|
|
1057
1069
|
var clearLocalStorageCommand = registerCommand({
|
|
@@ -1060,21 +1072,35 @@ var clearLocalStorageCommand = registerCommand({
|
|
|
1060
1072
|
scope: "page",
|
|
1061
1073
|
result: z8.object({ cleared: z8.boolean() }),
|
|
1062
1074
|
handler: async (_p, ctx) => {
|
|
1063
|
-
|
|
1064
|
-
|
|
1075
|
+
try {
|
|
1076
|
+
await ctx.page.evaluate(() => localStorage.clear());
|
|
1077
|
+
return ok8({ cleared: true });
|
|
1078
|
+
} catch (e) {
|
|
1079
|
+
return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
|
|
1080
|
+
}
|
|
1065
1081
|
}
|
|
1066
1082
|
});
|
|
1067
1083
|
|
|
1068
1084
|
// src/commands/screenshot.ts
|
|
1069
1085
|
import { z as z9 } from "zod";
|
|
1070
|
-
import { ok as ok9 } from "@dyyz1993/xcli-core";
|
|
1086
|
+
import { ok as ok9, fail as fail3 } from "@dyyz1993/xcli-core";
|
|
1071
1087
|
import { writeFileSync, mkdirSync } from "fs";
|
|
1072
|
-
import { join as join2 } from "path";
|
|
1088
|
+
import { dirname, join as join2 } from "path";
|
|
1073
1089
|
import { homedir as homedir2 } from "os";
|
|
1074
1090
|
var SCREENSHOTS_DIR = join2(homedir2(), ".xbrowser", "screenshots");
|
|
1075
1091
|
function ensureScreenshotsDir() {
|
|
1076
1092
|
mkdirSync(SCREENSHOTS_DIR, { recursive: true });
|
|
1077
1093
|
}
|
|
1094
|
+
function ensureParentDir(filePath) {
|
|
1095
|
+
const dir = dirname(filePath);
|
|
1096
|
+
if (dir === "." || dir === "/") return null;
|
|
1097
|
+
try {
|
|
1098
|
+
mkdirSync(dir, { recursive: true });
|
|
1099
|
+
return null;
|
|
1100
|
+
} catch (err) {
|
|
1101
|
+
return err instanceof Error ? err.message : String(err);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1078
1104
|
function generateScreenshotPath(format) {
|
|
1079
1105
|
const timestamp = Date.now();
|
|
1080
1106
|
const random = Math.random().toString(36).slice(2, 8);
|
|
@@ -1118,7 +1144,15 @@ var screenshotCommand = registerCommand({
|
|
|
1118
1144
|
buffer = await ctx.page.screenshot(options);
|
|
1119
1145
|
}
|
|
1120
1146
|
if (p.output) {
|
|
1121
|
-
|
|
1147
|
+
const dirErr = ensureParentDir(p.output);
|
|
1148
|
+
if (dirErr) {
|
|
1149
|
+
return fail3(`Cannot create directory for --output "${p.output}": ${dirErr}`);
|
|
1150
|
+
}
|
|
1151
|
+
try {
|
|
1152
|
+
writeFileSync(p.output, buffer, "binary");
|
|
1153
|
+
} catch (err) {
|
|
1154
|
+
return fail3(`Failed to write screenshot to "${p.output}": ${err instanceof Error ? err.message : String(err)}`);
|
|
1155
|
+
}
|
|
1122
1156
|
return ok9({
|
|
1123
1157
|
output: p.output,
|
|
1124
1158
|
format,
|
|
@@ -1236,7 +1270,7 @@ var setViewportCommand = registerCommand({
|
|
|
1236
1270
|
|
|
1237
1271
|
// src/commands/frame.ts
|
|
1238
1272
|
import { z as z12 } from "zod";
|
|
1239
|
-
import { ok as ok12, fail as
|
|
1273
|
+
import { ok as ok12, fail as fail4 } from "@dyyz1993/xcli-core";
|
|
1240
1274
|
var framesCommand = registerCommand({
|
|
1241
1275
|
name: "frames",
|
|
1242
1276
|
description: "List all frames in the current page",
|
|
@@ -1261,10 +1295,10 @@ var framesCommand = registerCommand({
|
|
|
1261
1295
|
});
|
|
1262
1296
|
var frameCommand = registerCommand({
|
|
1263
1297
|
name: "frame",
|
|
1264
|
-
description: "
|
|
1298
|
+
description: "Get frame info by index or name",
|
|
1265
1299
|
scope: "page",
|
|
1266
1300
|
parameters: z12.object({
|
|
1267
|
-
index: z12.number().optional(),
|
|
1301
|
+
index: z12.number().int().min(0).optional(),
|
|
1268
1302
|
name: z12.string().optional()
|
|
1269
1303
|
}),
|
|
1270
1304
|
result: z12.object({
|
|
@@ -1281,10 +1315,10 @@ var frameCommand = registerCommand({
|
|
|
1281
1315
|
} else if (p.name !== void 0) {
|
|
1282
1316
|
targetFrame = rawFrames.find((f) => f.name() === p.name);
|
|
1283
1317
|
} else {
|
|
1284
|
-
return
|
|
1318
|
+
return fail4("Must provide index or name");
|
|
1285
1319
|
}
|
|
1286
1320
|
if (!targetFrame) {
|
|
1287
|
-
return
|
|
1321
|
+
return fail4("Frame not found");
|
|
1288
1322
|
}
|
|
1289
1323
|
return ok12({
|
|
1290
1324
|
name: targetFrame.name(),
|
|
@@ -1930,7 +1964,7 @@ var actionsCommand = registerCommand({
|
|
|
1930
1964
|
|
|
1931
1965
|
// src/commands/scrape.ts
|
|
1932
1966
|
import { z as z15 } from "zod";
|
|
1933
|
-
import { ok as ok15, fail as
|
|
1967
|
+
import { ok as ok15, fail as fail5 } from "@dyyz1993/xcli-core";
|
|
1934
1968
|
|
|
1935
1969
|
// src/lib/html-to-markdown.ts
|
|
1936
1970
|
import * as cheerio from "cheerio";
|
|
@@ -2331,7 +2365,7 @@ var scrapeCommand = registerCommand({
|
|
|
2331
2365
|
try {
|
|
2332
2366
|
const targetUrl = p.url || page.url();
|
|
2333
2367
|
if (!targetUrl || targetUrl === "about:blank") {
|
|
2334
|
-
return
|
|
2368
|
+
return fail5("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
|
|
2335
2369
|
}
|
|
2336
2370
|
let lastError;
|
|
2337
2371
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
@@ -2485,7 +2519,7 @@ var scrapeCommand = registerCommand({
|
|
|
2485
2519
|
}
|
|
2486
2520
|
}
|
|
2487
2521
|
}
|
|
2488
|
-
return
|
|
2522
|
+
return fail5(`Scrape failed after ${maxAttempts} attempt(s): ${lastError?.message ?? "unknown error"}`);
|
|
2489
2523
|
} finally {
|
|
2490
2524
|
await closeEphemeralContext(context);
|
|
2491
2525
|
}
|
|
@@ -2498,7 +2532,7 @@ var scrapeCommand = registerCommand({
|
|
|
2498
2532
|
|
|
2499
2533
|
// src/commands/map.ts
|
|
2500
2534
|
import { z as z16 } from "zod";
|
|
2501
|
-
import { ok as ok16, fail as
|
|
2535
|
+
import { ok as ok16, fail as fail6 } from "@dyyz1993/xcli-core";
|
|
2502
2536
|
|
|
2503
2537
|
// src/utils/url.ts
|
|
2504
2538
|
var SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
@@ -2747,7 +2781,7 @@ var mapCommand = registerCommand({
|
|
|
2747
2781
|
try {
|
|
2748
2782
|
const targetUrl = p.url || page.url();
|
|
2749
2783
|
if (!targetUrl || targetUrl === "about:blank") {
|
|
2750
|
-
return
|
|
2784
|
+
return fail6("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
|
|
2751
2785
|
}
|
|
2752
2786
|
const links = await discoverUrls(page, targetUrl, {
|
|
2753
2787
|
sitemap: p.sitemap,
|
|
@@ -3592,7 +3626,7 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
|
|
|
3592
3626
|
|
|
3593
3627
|
// src/commands/network.ts
|
|
3594
3628
|
import { z as z19 } from "zod";
|
|
3595
|
-
import { ok as ok19, fail as
|
|
3629
|
+
import { ok as ok19, fail as fail7 } from "@dyyz1993/xcli-core";
|
|
3596
3630
|
function extractPath2(url) {
|
|
3597
3631
|
try {
|
|
3598
3632
|
const u = new URL(url);
|
|
@@ -3757,7 +3791,7 @@ var networkCommand = registerCommand({
|
|
|
3757
3791
|
};
|
|
3758
3792
|
if (p.listen) {
|
|
3759
3793
|
const page2 = ctx.page;
|
|
3760
|
-
if (!page2) return
|
|
3794
|
+
if (!page2) return fail7("No active page. Use --cdp to connect first.");
|
|
3761
3795
|
const captures = [];
|
|
3762
3796
|
const consoleMessages = [];
|
|
3763
3797
|
const wsCaptures = [];
|
|
@@ -4137,7 +4171,7 @@ var ENGINE_KEY_ENUM = z20.enum(ALL_ENGINE_KEYS);
|
|
|
4137
4171
|
|
|
4138
4172
|
// src/commands/snapshot.ts
|
|
4139
4173
|
import { z as z21 } from "zod";
|
|
4140
|
-
import { ok as ok20, fail as
|
|
4174
|
+
import { ok as ok20, fail as fail8, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
|
|
4141
4175
|
|
|
4142
4176
|
// src/runtime/ref-store.ts
|
|
4143
4177
|
var sessions = /* @__PURE__ */ new Map();
|
|
@@ -4379,7 +4413,7 @@ async function resolveRefParams(page, params, selectorKeys, cache, sessionId) {
|
|
|
4379
4413
|
|
|
4380
4414
|
// src/utils/site-semantics.ts
|
|
4381
4415
|
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync, readFileSync } from "fs";
|
|
4382
|
-
import { join as join4, dirname } from "path";
|
|
4416
|
+
import { join as join4, dirname as dirname2 } from "path";
|
|
4383
4417
|
import { homedir as homedir4 } from "os";
|
|
4384
4418
|
import { stringify, parse } from "yaml";
|
|
4385
4419
|
import { execFile } from "child_process";
|
|
@@ -4472,7 +4506,7 @@ function extractDomain2(url) {
|
|
|
4472
4506
|
}
|
|
4473
4507
|
function saveSemantics(domain, pagePath, url, elements) {
|
|
4474
4508
|
const filePath = getSemanticsPath(domain);
|
|
4475
|
-
const dir =
|
|
4509
|
+
const dir = dirname2(filePath);
|
|
4476
4510
|
let site;
|
|
4477
4511
|
if (existsSync(filePath)) {
|
|
4478
4512
|
try {
|
|
@@ -5108,7 +5142,7 @@ var snapshotCommand = registerCommand({
|
|
|
5108
5142
|
persistSemantics(url, aria);
|
|
5109
5143
|
return ok20({ url, title, aria, text, dom }, normalizeTips3(tips));
|
|
5110
5144
|
}
|
|
5111
|
-
return
|
|
5145
|
+
return fail8(`Unknown snapshot type: ${p.type}`);
|
|
5112
5146
|
}
|
|
5113
5147
|
});
|
|
5114
5148
|
function persistSemantics(url, aria) {
|
|
@@ -5277,7 +5311,7 @@ var waitForCommand = registerCommand({
|
|
|
5277
5311
|
|
|
5278
5312
|
// src/commands/tab.ts
|
|
5279
5313
|
import { z as z23 } from "zod";
|
|
5280
|
-
import { ok as ok22, fail as
|
|
5314
|
+
import { ok as ok22, fail as fail9 } from "@dyyz1993/xcli-core";
|
|
5281
5315
|
var TabParams = z23.object({
|
|
5282
5316
|
subcommand: z23.enum(["list", "new", "close", "switch"]),
|
|
5283
5317
|
url: z23.string().optional(),
|
|
@@ -5294,7 +5328,7 @@ var tabCommand = registerCommand({
|
|
|
5294
5328
|
}),
|
|
5295
5329
|
handler: async (p, ctx) => {
|
|
5296
5330
|
if (!ctx.browserContext) {
|
|
5297
|
-
return
|
|
5331
|
+
return fail9("No browser context available. Use --cdp to connect to a browser first.");
|
|
5298
5332
|
}
|
|
5299
5333
|
const pages = ctx.browserContext.pages();
|
|
5300
5334
|
switch (p.subcommand) {
|
|
@@ -5303,50 +5337,45 @@ var tabCommand = registerCommand({
|
|
|
5303
5337
|
case "new":
|
|
5304
5338
|
return handleNew(p, pages, ctx);
|
|
5305
5339
|
case "close":
|
|
5306
|
-
return handleClose(p,
|
|
5340
|
+
return handleClose(p, ctx);
|
|
5307
5341
|
case "switch":
|
|
5308
5342
|
return handleSwitch(p, pages, ctx);
|
|
5309
5343
|
default:
|
|
5310
|
-
return
|
|
5344
|
+
return fail9(`Unknown subcommand: ${p.subcommand}`);
|
|
5311
5345
|
}
|
|
5312
5346
|
}
|
|
5313
5347
|
});
|
|
5314
|
-
function handleList(pages, ctx) {
|
|
5315
|
-
const
|
|
5316
|
-
|
|
5348
|
+
async function handleList(pages, ctx) {
|
|
5349
|
+
const tabs = [];
|
|
5350
|
+
let activeIndex = -1;
|
|
5351
|
+
for (let i = 0; i < pages.length; i++) {
|
|
5352
|
+
const page = pages[i];
|
|
5317
5353
|
const url = page.url();
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
});
|
|
5325
|
-
}
|
|
5326
|
-
} catch {
|
|
5327
|
-
title = "";
|
|
5328
|
-
}
|
|
5329
|
-
return {
|
|
5330
|
-
index: i,
|
|
5331
|
-
url,
|
|
5332
|
-
title,
|
|
5333
|
-
active: i === currentIndex
|
|
5334
|
-
};
|
|
5335
|
-
});
|
|
5336
|
-
return ok22({ tabs, total: tabs.length, activeIndex: currentIndex });
|
|
5354
|
+
const title = await page.title().catch(() => "");
|
|
5355
|
+
const isActive = page === ctx.page;
|
|
5356
|
+
if (isActive) activeIndex = i;
|
|
5357
|
+
tabs.push({ index: i, url, title, active: isActive });
|
|
5358
|
+
}
|
|
5359
|
+
return ok22({ tabs, total: tabs.length, activeIndex });
|
|
5337
5360
|
}
|
|
5338
5361
|
async function handleNew(p, _pages, ctx) {
|
|
5339
5362
|
const newPage = await ctx.browserContext.newPage();
|
|
5363
|
+
const warnings = [];
|
|
5340
5364
|
if (p.url) {
|
|
5341
5365
|
let url = p.url;
|
|
5342
|
-
if (!/^
|
|
5366
|
+
if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(url)) {
|
|
5343
5367
|
url = "https://" + url;
|
|
5344
5368
|
}
|
|
5345
|
-
|
|
5346
|
-
|
|
5369
|
+
try {
|
|
5370
|
+
await newPage.goto(url, { waitUntil: "domcontentloaded", timeout: 15e3 });
|
|
5371
|
+
} catch (err) {
|
|
5372
|
+
warnings.push(`Navigation to "${url}" failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5373
|
+
}
|
|
5374
|
+
}
|
|
5375
|
+
try {
|
|
5376
|
+
await newPage.waitForLoadState("domcontentloaded");
|
|
5377
|
+
} catch {
|
|
5347
5378
|
}
|
|
5348
|
-
await newPage.waitForLoadState("domcontentloaded").catch(() => {
|
|
5349
|
-
});
|
|
5350
5379
|
const session = ctx.sessionId ? getSessionById(ctx.sessionId) : void 0;
|
|
5351
5380
|
if (session) {
|
|
5352
5381
|
setActivePage(session, newPage);
|
|
@@ -5359,18 +5388,20 @@ async function handleNew(p, _pages, ctx) {
|
|
|
5359
5388
|
index: newIndex >= 0 ? newIndex : allPages.length - 1,
|
|
5360
5389
|
url: newPage.url(),
|
|
5361
5390
|
title,
|
|
5362
|
-
total: allPages.length
|
|
5391
|
+
total: allPages.length,
|
|
5392
|
+
...warnings.length > 0 ? { warning: warnings.join("; ") } : {}
|
|
5363
5393
|
});
|
|
5364
5394
|
}
|
|
5365
|
-
async function handleClose(p,
|
|
5366
|
-
|
|
5367
|
-
|
|
5395
|
+
async function handleClose(p, ctx) {
|
|
5396
|
+
const currentPages = ctx.browserContext.pages();
|
|
5397
|
+
if (currentPages.length <= 1) {
|
|
5398
|
+
return fail9("Cannot close the last remaining tab");
|
|
5368
5399
|
}
|
|
5369
|
-
const closeIndex = p.index ??
|
|
5370
|
-
if (closeIndex < 0 || closeIndex >=
|
|
5371
|
-
return
|
|
5400
|
+
const closeIndex = p.index ?? currentPages.findIndex((pg) => pg === ctx.page);
|
|
5401
|
+
if (closeIndex < 0 || closeIndex >= currentPages.length) {
|
|
5402
|
+
return fail9(`Invalid tab index: ${closeIndex}. Valid range: 0-${currentPages.length - 1}`);
|
|
5372
5403
|
}
|
|
5373
|
-
const pageToClose =
|
|
5404
|
+
const pageToClose = currentPages[closeIndex];
|
|
5374
5405
|
const isActivePage = pageToClose === ctx.page;
|
|
5375
5406
|
await pageToClose.close();
|
|
5376
5407
|
const remainingPages = ctx.browserContext.pages();
|
|
@@ -5386,15 +5417,15 @@ async function handleClose(p, pages, ctx) {
|
|
|
5386
5417
|
return ok22({
|
|
5387
5418
|
closedIndex: closeIndex,
|
|
5388
5419
|
total: remainingPages.length,
|
|
5389
|
-
activeIndex: isActivePage ? closeIndex < remainingPages.length ? closeIndex : remainingPages.length - 1 :
|
|
5420
|
+
activeIndex: isActivePage ? closeIndex < remainingPages.length ? closeIndex : remainingPages.length - 1 : remainingPages.findIndex((pg) => pg === ctx.page)
|
|
5390
5421
|
});
|
|
5391
5422
|
}
|
|
5392
5423
|
async function handleSwitch(p, pages, ctx) {
|
|
5393
5424
|
if (p.index === void 0) {
|
|
5394
|
-
return
|
|
5425
|
+
return fail9("Parameter --index is required for switch subcommand");
|
|
5395
5426
|
}
|
|
5396
5427
|
if (p.index < 0 || p.index >= pages.length) {
|
|
5397
|
-
return
|
|
5428
|
+
return fail9(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
|
|
5398
5429
|
}
|
|
5399
5430
|
const targetPage = pages[p.index];
|
|
5400
5431
|
await targetPage.bringToFront().catch(() => {
|
|
@@ -5646,7 +5677,7 @@ registerCommandDefinition("addinitscript", ["script"]);
|
|
|
5646
5677
|
|
|
5647
5678
|
// src/commands/find.ts
|
|
5648
5679
|
import { z as z25 } from "zod";
|
|
5649
|
-
import { ok as ok24, fail as
|
|
5680
|
+
import { ok as ok24, fail as fail10, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
|
|
5650
5681
|
var actionSchema2 = z25.enum(["click", "fill", "type", "select", "hover", "check"]);
|
|
5651
5682
|
var findCommand = registerCommand({
|
|
5652
5683
|
name: "find",
|
|
@@ -5687,7 +5718,7 @@ var findCommand = registerCommand({
|
|
|
5687
5718
|
});
|
|
5688
5719
|
const count = await locator.count();
|
|
5689
5720
|
if (count === 0) {
|
|
5690
|
-
return
|
|
5721
|
+
return fail10(`No element found with ${p.strategy}="${p.value}"`);
|
|
5691
5722
|
}
|
|
5692
5723
|
const tips = [];
|
|
5693
5724
|
const target = selectTarget(locator, p.strategy);
|
|
@@ -5699,15 +5730,15 @@ var findCommand = registerCommand({
|
|
|
5699
5730
|
await target.click({ timeout: p.timeout, force: true });
|
|
5700
5731
|
return okWithTips({ matched: count, selector, action: "click" }, tips);
|
|
5701
5732
|
} else if (actionName === "fill") {
|
|
5702
|
-
if (actionValue === void 0) return
|
|
5733
|
+
if (actionValue === void 0) return fail10("find fill requires a value");
|
|
5703
5734
|
await target.fill(actionValue, { timeout: p.timeout, force: true });
|
|
5704
5735
|
return okWithTips({ matched: count, selector, action: `fill("${actionValue}")` }, tips);
|
|
5705
5736
|
} else if (actionName === "type") {
|
|
5706
|
-
if (actionValue === void 0) return
|
|
5737
|
+
if (actionValue === void 0) return fail10("find type requires a value");
|
|
5707
5738
|
await target.type(actionValue, { delay: 10, timeout: p.timeout });
|
|
5708
5739
|
return okWithTips({ matched: count, selector, action: `type("${actionValue}")` }, tips);
|
|
5709
5740
|
} else if (actionName === "select") {
|
|
5710
|
-
if (actionValue === void 0) return
|
|
5741
|
+
if (actionValue === void 0) return fail10("find select requires a value");
|
|
5711
5742
|
await target.selectOption(actionValue);
|
|
5712
5743
|
return okWithTips({ matched: count, selector, action: `select("${actionValue}")` }, tips);
|
|
5713
5744
|
} else if (actionName === "hover") {
|
|
@@ -6480,12 +6511,24 @@ var XBrowserPluginLoader = class {
|
|
|
6480
6511
|
// src/utils/plugin-singleton.ts
|
|
6481
6512
|
var pluginLoader = null;
|
|
6482
6513
|
var pluginsScanned = false;
|
|
6514
|
+
function silenceSchemaWarnings(fn) {
|
|
6515
|
+
if (process.env.XBROWSER_DEBUG || process.env.VITEST_WORKER_ID) return fn();
|
|
6516
|
+
const originalWarn = console.warn;
|
|
6517
|
+
console.warn = (...args) => {
|
|
6518
|
+
const msg = typeof args[0] === "string" ? args[0] : "";
|
|
6519
|
+
if (msg.includes('has no "result" schema')) return;
|
|
6520
|
+
originalWarn(...args);
|
|
6521
|
+
};
|
|
6522
|
+
return fn().finally(() => {
|
|
6523
|
+
console.warn = originalWarn;
|
|
6524
|
+
});
|
|
6525
|
+
}
|
|
6483
6526
|
async function getPluginLoader() {
|
|
6484
6527
|
if (!pluginLoader) {
|
|
6485
6528
|
pluginLoader = new XBrowserPluginLoader();
|
|
6486
6529
|
}
|
|
6487
6530
|
if (!pluginsScanned) {
|
|
6488
|
-
await pluginLoader.scanAndLoad();
|
|
6531
|
+
await silenceSchemaWarnings(() => pluginLoader.scanAndLoad());
|
|
6489
6532
|
pluginsScanned = true;
|
|
6490
6533
|
}
|
|
6491
6534
|
return pluginLoader;
|
|
@@ -7143,7 +7186,7 @@ async function guardCheck(commandName) {
|
|
|
7143
7186
|
}
|
|
7144
7187
|
}
|
|
7145
7188
|
function errorResult(message) {
|
|
7146
|
-
return { ...
|
|
7189
|
+
return { ...fail11(message), duration: 0 };
|
|
7147
7190
|
}
|
|
7148
7191
|
function tipsToMessages(tips) {
|
|
7149
7192
|
if (!tips || tips.length === 0) return [];
|
|
@@ -7444,7 +7487,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7444
7487
|
duration,
|
|
7445
7488
|
timestamp: start
|
|
7446
7489
|
});
|
|
7447
|
-
return { ...
|
|
7490
|
+
return { ...fail11(errorMessage), duration };
|
|
7448
7491
|
} finally {
|
|
7449
7492
|
}
|
|
7450
7493
|
}
|
|
@@ -7485,7 +7528,7 @@ async function executeChain(input, options) {
|
|
|
7485
7528
|
results.push({
|
|
7486
7529
|
command: cmdName,
|
|
7487
7530
|
raw: cmdStr,
|
|
7488
|
-
...
|
|
7531
|
+
...fail11(`Plugin "${cmdName}" requires a sub-command`),
|
|
7489
7532
|
duration: 0
|
|
7490
7533
|
});
|
|
7491
7534
|
if (type === "and") {
|
|
@@ -7504,7 +7547,7 @@ async function executeChain(input, options) {
|
|
|
7504
7547
|
results.push({
|
|
7505
7548
|
command: cmdName,
|
|
7506
7549
|
raw: cmdStr,
|
|
7507
|
-
...
|
|
7550
|
+
...fail11(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
|
|
7508
7551
|
duration: 0
|
|
7509
7552
|
});
|
|
7510
7553
|
if (type === "and") {
|
|
@@ -7636,7 +7679,7 @@ async function executeChain(input, options) {
|
|
|
7636
7679
|
results.push({
|
|
7637
7680
|
command: `${cmdName} ${subCommand}`,
|
|
7638
7681
|
raw: cmdStr,
|
|
7639
|
-
...
|
|
7682
|
+
...fail11(errorMessage),
|
|
7640
7683
|
duration: duration2
|
|
7641
7684
|
});
|
|
7642
7685
|
if (type === "and") {
|
|
@@ -7970,8 +8013,14 @@ var configBuiltin = {
|
|
|
7970
8013
|
"preview.quality",
|
|
7971
8014
|
"preview.fps"
|
|
7972
8015
|
]);
|
|
7973
|
-
|
|
7974
|
-
|
|
8016
|
+
const isKnownNamespace = key.startsWith("browser.") || key.startsWith("captcha.") || key.startsWith("preview.");
|
|
8017
|
+
if (!knownKeys.has(key) && !isKnownNamespace) {
|
|
8018
|
+
console.error(
|
|
8019
|
+
`Unknown config key: "${key}".
|
|
8020
|
+
Known namespaces: browser.*, captcha.*, preview.*
|
|
8021
|
+
Run "xbrowser config list" to see current keys.`
|
|
8022
|
+
);
|
|
8023
|
+
process.exit(1);
|
|
7975
8024
|
}
|
|
7976
8025
|
setConfigValue(key, value);
|
|
7977
8026
|
console.log(`Set ${key} = ${value}`);
|
|
@@ -7994,7 +8043,7 @@ import {
|
|
|
7994
8043
|
readFileSync as readFileSync8,
|
|
7995
8044
|
writeFileSync as writeFileSync10
|
|
7996
8045
|
} from "fs";
|
|
7997
|
-
import { resolve as resolve8, basename as basename2, dirname as
|
|
8046
|
+
import { resolve as resolve8, basename as basename2, dirname as dirname4 } from "path";
|
|
7998
8047
|
import { homedir as homedir8 } from "os";
|
|
7999
8048
|
|
|
8000
8049
|
// src/plugin/install-sources/local.ts
|
|
@@ -8252,7 +8301,7 @@ import {
|
|
|
8252
8301
|
rmSync as rmSync5,
|
|
8253
8302
|
cpSync as cpSync5
|
|
8254
8303
|
} from "fs";
|
|
8255
|
-
import { resolve as resolve7, join as join11, dirname as
|
|
8304
|
+
import { resolve as resolve7, join as join11, dirname as dirname3 } from "path";
|
|
8256
8305
|
import { tmpdir as tmpdir5 } from "os";
|
|
8257
8306
|
import { gunzipSync } from "zlib";
|
|
8258
8307
|
import {
|
|
@@ -8318,7 +8367,7 @@ function extractManifestToDir(manifest, targetDir) {
|
|
|
8318
8367
|
mkdirSync7(targetDir, { recursive: true });
|
|
8319
8368
|
for (const file of manifest) {
|
|
8320
8369
|
const filePath = resolve7(targetDir, file.path);
|
|
8321
|
-
mkdirSync7(
|
|
8370
|
+
mkdirSync7(dirname3(filePath), { recursive: true });
|
|
8322
8371
|
writeFileSync9(filePath, Buffer.from(file.content, "base64"));
|
|
8323
8372
|
}
|
|
8324
8373
|
}
|
|
@@ -8567,7 +8616,7 @@ var PluginInstaller = class {
|
|
|
8567
8616
|
if (resp.ok) {
|
|
8568
8617
|
const content2 = await resp.text();
|
|
8569
8618
|
const dst = resolve8(sharedDir, file);
|
|
8570
|
-
const dstDir =
|
|
8619
|
+
const dstDir = dirname4(dst);
|
|
8571
8620
|
if (!existsSync10(dstDir)) mkdirSync8(dstDir, { recursive: true });
|
|
8572
8621
|
writeFileSync10(dst, content2, "utf-8");
|
|
8573
8622
|
console.log(`\u2705 Downloaded shared/${file} from GitHub`);
|
|
@@ -8592,7 +8641,7 @@ var PluginInstaller = class {
|
|
|
8592
8641
|
const dst = resolve8(sharedDir, file);
|
|
8593
8642
|
if (existsSync10(src)) {
|
|
8594
8643
|
try {
|
|
8595
|
-
cpSync6(
|
|
8644
|
+
cpSync6(dirname4(src), dirname4(dst), { recursive: true });
|
|
8596
8645
|
console.log(`\u2705 Copied shared/${file} for plugin "${basename2(pluginDir)}"`);
|
|
8597
8646
|
} catch {
|
|
8598
8647
|
try {
|
|
@@ -8735,7 +8784,7 @@ function outputResult(result, mode = "text") {
|
|
|
8735
8784
|
function outputError(message) {
|
|
8736
8785
|
const formatted = formatter.formatError(message, { color: true, emoji: false });
|
|
8737
8786
|
console.error(formatted);
|
|
8738
|
-
process.
|
|
8787
|
+
process.exitCode = 1;
|
|
8739
8788
|
}
|
|
8740
8789
|
function outputEnvelope(result, meta, mode) {
|
|
8741
8790
|
if (mode !== "json" && mode !== "yaml") {
|
|
@@ -10205,7 +10254,6 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10205
10254
|
break;
|
|
10206
10255
|
}
|
|
10207
10256
|
case "scrape":
|
|
10208
|
-
if (!args[0]) outputError("Usage: xbrowser scrape <url> [--format markdown|html|text] [--mode raw|clean|compact] [--selector <sel>] [--timeout <ms>]");
|
|
10209
10257
|
cmdName = "scrape";
|
|
10210
10258
|
params = {
|
|
10211
10259
|
url: args[0],
|
|
@@ -10217,7 +10265,6 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10217
10265
|
};
|
|
10218
10266
|
break;
|
|
10219
10267
|
case "map":
|
|
10220
|
-
if (!args[0]) outputError("Usage: xbrowser map <url> [--search <query>] [--sitemap include|only] [--include-subdomains] [--limit <n>]");
|
|
10221
10268
|
cmdName = "map";
|
|
10222
10269
|
params = {
|
|
10223
10270
|
url: args[0],
|
|
@@ -10352,12 +10399,20 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10352
10399
|
}
|
|
10353
10400
|
}
|
|
10354
10401
|
const outputFile = options.output;
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
writeFileSync12
|
|
10359
|
-
|
|
10402
|
+
const dataObj = result.data;
|
|
10403
|
+
const handlerAlreadyWrote = !!dataObj && typeof dataObj.output === "string";
|
|
10404
|
+
if (outputFile && result.success && result.data && !handlerAlreadyWrote) {
|
|
10405
|
+
const { writeFileSync: writeFileSync12, mkdirSync: mkdirSync10 } = await import("fs");
|
|
10406
|
+
const { dirname: dirname6 } = await import("path");
|
|
10407
|
+
const content = typeof result.data === "string" ? result.data : dataObj.content || dataObj.text || JSON.stringify(result.data, null, 2);
|
|
10408
|
+
try {
|
|
10409
|
+
mkdirSync10(dirname6(outputFile), { recursive: true });
|
|
10410
|
+
writeFileSync12(outputFile, content, "utf-8");
|
|
10411
|
+
console.log(`
|
|
10360
10412
|
\u{1F4C4} Written to ${outputFile}`);
|
|
10413
|
+
} catch (err) {
|
|
10414
|
+
outputError(`Failed to write --output "${outputFile}": ${err instanceof Error ? err.message : String(err)}`);
|
|
10415
|
+
}
|
|
10361
10416
|
}
|
|
10362
10417
|
}
|
|
10363
10418
|
|
|
@@ -10823,7 +10878,11 @@ Total: ${enrichedPlugins.length} plugins`);
|
|
|
10823
10878
|
case "login":
|
|
10824
10879
|
case "whoami":
|
|
10825
10880
|
case "logout":
|
|
10826
|
-
outputError(
|
|
10881
|
+
outputError(
|
|
10882
|
+
`"plugin ${sub}" is no longer available: the built-in marketplace publisher was removed.
|
|
10883
|
+
To publish a plugin, use npm directly: \`npm publish\` from the plugin directory.
|
|
10884
|
+
See docs/plugin-guide.md for the publishing workflow.`
|
|
10885
|
+
);
|
|
10827
10886
|
break;
|
|
10828
10887
|
default:
|
|
10829
10888
|
console.log(handlePluginHelp());
|
|
@@ -11184,23 +11243,51 @@ async function handleConvert(args, _mode) {
|
|
|
11184
11243
|
const path3 = await import("path");
|
|
11185
11244
|
const { default: yaml } = await import("yaml");
|
|
11186
11245
|
const { generateJSScript, generatePythonScript, generateBashScript } = await import("./convert-LB3GJTLR.js");
|
|
11187
|
-
|
|
11188
|
-
|
|
11189
|
-
|
|
11246
|
+
let recording;
|
|
11247
|
+
try {
|
|
11248
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
11249
|
+
recording = yaml.parse(content);
|
|
11250
|
+
} catch (e) {
|
|
11251
|
+
console.error(`Error: Failed to read "${filePath}": ${e instanceof Error ? e.message.split("\n")[0] : String(e)}`);
|
|
11252
|
+
process.exit(1);
|
|
11253
|
+
}
|
|
11254
|
+
if (recording === null || typeof recording !== "object" || Array.isArray(recording)) {
|
|
11255
|
+
console.error(`Error: "${filePath}" does not contain a valid recording (expected a YAML/JSON object with events or actions).`);
|
|
11256
|
+
process.exit(1);
|
|
11257
|
+
}
|
|
11258
|
+
const rawActions = recording.actions;
|
|
11259
|
+
if (Array.isArray(rawActions) && !Array.isArray(recording.events)) {
|
|
11260
|
+
recording.events = rawActions.map((a) => {
|
|
11261
|
+
const action = a;
|
|
11262
|
+
const element = action.element ?? {};
|
|
11263
|
+
return {
|
|
11264
|
+
type: action.type,
|
|
11265
|
+
selector: element.selector ?? action.selector,
|
|
11266
|
+
data: {
|
|
11267
|
+
...action.data,
|
|
11268
|
+
value: action.value ?? action.data?.value,
|
|
11269
|
+
key: action.key ?? action.data?.key,
|
|
11270
|
+
x: action.scrollX ?? action.data?.x,
|
|
11271
|
+
y: action.scrollY ?? action.data?.y
|
|
11272
|
+
}
|
|
11273
|
+
};
|
|
11274
|
+
});
|
|
11275
|
+
}
|
|
11190
11276
|
const ext = path3.extname(outputPath).toLowerCase();
|
|
11277
|
+
const recordingTyped = recording;
|
|
11191
11278
|
let script;
|
|
11192
11279
|
if (ext === ".py") {
|
|
11193
|
-
script = generatePythonScript(
|
|
11280
|
+
script = generatePythonScript(recordingTyped);
|
|
11194
11281
|
} else if (ext === ".sh") {
|
|
11195
|
-
script = generateBashScript(
|
|
11282
|
+
script = generateBashScript(recordingTyped);
|
|
11196
11283
|
} else {
|
|
11197
|
-
script = generateJSScript(
|
|
11284
|
+
script = generateJSScript(recordingTyped);
|
|
11198
11285
|
}
|
|
11199
11286
|
fs3.writeFileSync(outputPath, script);
|
|
11200
11287
|
fs3.chmodSync(outputPath, 493);
|
|
11201
|
-
const eventCount = (
|
|
11288
|
+
const eventCount = (recordingTyped.events || []).length;
|
|
11202
11289
|
console.log(`Converted ${filePath} -> ${outputPath}`);
|
|
11203
|
-
console.log(` Events: ${eventCount}, Start URL: ${
|
|
11290
|
+
console.log(` Events: ${eventCount}, Start URL: ${recordingTyped.startUrl}`);
|
|
11204
11291
|
console.log(` Run: ${ext === ".py" ? "python" : ext === ".sh" ? "./" : "node"} ${outputPath}`);
|
|
11205
11292
|
}
|
|
11206
11293
|
async function handleExtract(args, _mode) {
|
|
@@ -11209,11 +11296,16 @@ async function handleExtract(args, _mode) {
|
|
|
11209
11296
|
console.error("Usage: xbrowser extract <recording.yaml>");
|
|
11210
11297
|
process.exit(1);
|
|
11211
11298
|
}
|
|
11212
|
-
const { extractAndSave, printExtractSummary } = await import("./extract-
|
|
11213
|
-
|
|
11214
|
-
|
|
11215
|
-
|
|
11299
|
+
const { extractAndSave, printExtractSummary } = await import("./extract-EUWPRSKH.js");
|
|
11300
|
+
try {
|
|
11301
|
+
const { summary, outputPath } = extractAndSave(filePath);
|
|
11302
|
+
printExtractSummary(summary);
|
|
11303
|
+
console.log(`
|
|
11216
11304
|
Saved LLM summary: ${outputPath}`);
|
|
11305
|
+
} catch (e) {
|
|
11306
|
+
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
11307
|
+
process.exit(1);
|
|
11308
|
+
}
|
|
11217
11309
|
}
|
|
11218
11310
|
async function handleFilter(args, _mode, options) {
|
|
11219
11311
|
const filePath = args[0];
|
|
@@ -11222,16 +11314,21 @@ async function handleFilter(args, _mode, options) {
|
|
|
11222
11314
|
console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude type1,type2]");
|
|
11223
11315
|
process.exit(1);
|
|
11224
11316
|
}
|
|
11225
|
-
const { filterRecording, parseExcludeTypes } = await import("./filter-
|
|
11317
|
+
const { filterRecording, parseExcludeTypes } = await import("./filter-7YOPVPVC.js");
|
|
11226
11318
|
const excludeArgs = args.slice(2).concat(
|
|
11227
11319
|
Object.entries(options || {}).flatMap(
|
|
11228
11320
|
([k, v]) => k.startsWith("exclude") ? [`--${k}${typeof v === "string" ? "=" + v : ""}`] : []
|
|
11229
11321
|
)
|
|
11230
11322
|
);
|
|
11231
11323
|
const excludeTypes = parseExcludeTypes(excludeArgs);
|
|
11232
|
-
|
|
11233
|
-
|
|
11234
|
-
|
|
11324
|
+
try {
|
|
11325
|
+
const result = filterRecording(filePath, outputPath, excludeTypes);
|
|
11326
|
+
console.log(`Filtered ${filePath} -> ${outputPath}`);
|
|
11327
|
+
console.log(` Original: ${result.originalCount}, After: ${result.filteredCount}, Removed: ${result.removed} (${result.percentage}%)`);
|
|
11328
|
+
} catch (e) {
|
|
11329
|
+
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
11330
|
+
process.exit(1);
|
|
11331
|
+
}
|
|
11235
11332
|
}
|
|
11236
11333
|
async function handleGeneratePlugin(sessionName, pluginName, outputDir) {
|
|
11237
11334
|
const { SessionRecorder: SessionRecorder2 } = await import("./session-recorder-KPU4YQAE.js");
|
|
@@ -11402,8 +11499,21 @@ export default createSite({
|
|
|
11402
11499
|
url: '${data.startUrl}',
|
|
11403
11500
|
detect: async (ctx: CommandContext) => {
|
|
11404
11501
|
const page = ensurePage(ctx);
|
|
11405
|
-
//
|
|
11406
|
-
|
|
11502
|
+
// Best-effort login detection: check for common logged-in indicators.
|
|
11503
|
+
// Customize these selectors for your site's specific login state.
|
|
11504
|
+
try {
|
|
11505
|
+
const loggedIn = await page.evaluate(() => {
|
|
11506
|
+
const el = document.querySelector(
|
|
11507
|
+
'[class*="avatar"], [class*="user-info"], [class*="logged-in"], ' +
|
|
11508
|
+
'[data-testid*="user"], [data-testid*="avatar"], ' +
|
|
11509
|
+
'a[href*="logout"], button[class*="logout"]'
|
|
11510
|
+
);
|
|
11511
|
+
return !!el;
|
|
11512
|
+
});
|
|
11513
|
+
return loggedIn;
|
|
11514
|
+
} catch {
|
|
11515
|
+
return false;
|
|
11516
|
+
}
|
|
11407
11517
|
},
|
|
11408
11518
|
},
|
|
11409
11519
|
|
|
@@ -11430,7 +11540,7 @@ async function handleRun(filePath, options) {
|
|
|
11430
11540
|
outputError("No commands found in file");
|
|
11431
11541
|
return;
|
|
11432
11542
|
}
|
|
11433
|
-
const chain = commands.join("
|
|
11543
|
+
const chain = commands.join(" ; ");
|
|
11434
11544
|
const chainResult = await executeChain(chain, {
|
|
11435
11545
|
cdpEndpoint: options?.cdpEndpoint,
|
|
11436
11546
|
sessionName: options?.sessionName,
|
|
@@ -12765,6 +12875,7 @@ async function handleChainInput(input, argv) {
|
|
|
12765
12875
|
}
|
|
12766
12876
|
async function routeCommand(argvIn, stdinCommands) {
|
|
12767
12877
|
let argv = argvIn;
|
|
12878
|
+
process.exitCode = 0;
|
|
12768
12879
|
try {
|
|
12769
12880
|
if (stdinCommands && stdinCommands.length > 0) {
|
|
12770
12881
|
await handleStdinMode(stdinCommands, argv);
|
|
@@ -13162,6 +13273,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13162
13273
|
const result = await forwardExec(`${command}.${subCommand}`, params, sessionName, cdpEndpoint, userTimeout);
|
|
13163
13274
|
const resultData = result && typeof result === "object" && "data" in result ? result.data : void 0;
|
|
13164
13275
|
if (result && result.success === false && resultData?.code === "LOGIN_REQUIRED") {
|
|
13276
|
+
process.exitCode = 1;
|
|
13165
13277
|
outputLoginRequired(result, mode);
|
|
13166
13278
|
return;
|
|
13167
13279
|
}
|
|
@@ -13233,6 +13345,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13233
13345
|
sessionName
|
|
13234
13346
|
});
|
|
13235
13347
|
if (!loginGuard.ok) {
|
|
13348
|
+
process.exitCode = 1;
|
|
13236
13349
|
const result2 = {
|
|
13237
13350
|
success: false,
|
|
13238
13351
|
data: loginGuard.data ?? null,
|
|
@@ -13276,8 +13389,9 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13276
13389
|
}
|
|
13277
13390
|
const outputData = isCommandResult2(result) ? result.data : result && typeof result === "object" ? result.data ?? result : result;
|
|
13278
13391
|
const tips = isCommandResult2(result) ? result.tips : result && typeof result === "object" ? result.tips : void 0;
|
|
13392
|
+
const resultSuccess = isCommandResult2(result) ? result.success !== false : true;
|
|
13393
|
+
if (!resultSuccess) process.exitCode = 1;
|
|
13279
13394
|
if (mode === "json" || mode === "yaml") {
|
|
13280
|
-
const resultSuccess = isCommandResult2(result) ? result.success !== false : true;
|
|
13281
13395
|
const resultMsg = isCommandResult2(result) ? result.message : void 0;
|
|
13282
13396
|
const duration = Date.now() - cmdStart;
|
|
13283
13397
|
const envelopeMeta = { command: `${command} ${subCommand}` };
|
|
@@ -13472,7 +13586,7 @@ async function main() {
|
|
|
13472
13586
|
const { ensureProcessCanExit } = await import("./browser-4KRHALJ3.js");
|
|
13473
13587
|
await ensureProcessCanExit().catch(() => {
|
|
13474
13588
|
});
|
|
13475
|
-
process.exit(exitCode);
|
|
13589
|
+
process.exit(process.exitCode || exitCode);
|
|
13476
13590
|
}
|
|
13477
13591
|
}
|
|
13478
13592
|
}
|