@xbrowser/cli 1.8.7 → 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/{browser-XPKM344Y.js → browser-4KRHALJ3.js} +1 -1
- package/dist/{browser-LW2MDJE4.js → browser-6WJHQDPV.js} +1 -1
- package/dist/{browser-TLIDFFEG.js → browser-C63PFGPT.js} +1 -1
- package/dist/{chunk-GJAV3QGG.js → chunk-ANVL2ID2.js} +27 -3
- package/dist/{chunk-CJJ564VK.js → chunk-MSQIPXKE.js} +1 -1
- package/dist/{chunk-HXLMMSU3.js → chunk-OOJ2H7IL.js} +1 -1
- package/dist/{chunk-2QQDTXDL.js → chunk-PIWNMC36.js} +13 -1
- package/dist/{chunk-FL6DOSWV.js → chunk-TTZNR3QP.js} +30 -2
- package/dist/{chunk-MJFYLKGL.js → chunk-X46HDAOT.js} +27 -3
- package/dist/{chunk-RMYEHTLS.js → chunk-YSCY52UJ.js} +30 -2
- package/dist/{chunk-SJLRCE6N.js → chunk-Z7WC4P2M.js} +1 -1
- package/dist/cli.js +252 -138
- package/dist/daemon-main.js +162 -249
- 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.d.ts +11 -1
- package/dist/index.js +252 -138
- package/dist/{plugin-singleton-SYJF6BD6.js → plugin-singleton-FCNDN3FX.js} +1 -1
- package/dist/{session-recorder-33UKWCIR.js → session-recorder-KPU4YQAE.js} +1 -1
- package/dist/{session-recorder-BMIJ3ZJM.js → session-recorder-UTULJFTE.js} +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -50,14 +50,14 @@ import {
|
|
|
50
50
|
extractAndSave,
|
|
51
51
|
extractRecording,
|
|
52
52
|
printExtractSummary
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-X46HDAOT.js";
|
|
54
54
|
import {
|
|
55
55
|
filterRecording,
|
|
56
56
|
parseExcludeTypes
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-ANVL2ID2.js";
|
|
58
58
|
import {
|
|
59
59
|
SessionRecorder
|
|
60
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-YSCY52UJ.js";
|
|
61
61
|
import {
|
|
62
62
|
addKnownIssue,
|
|
63
63
|
getKnowledgePath,
|
|
@@ -82,7 +82,7 @@ import {
|
|
|
82
82
|
resolveLaunchOpts,
|
|
83
83
|
saveSessionDiskMeta,
|
|
84
84
|
setActivePage
|
|
85
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-MSQIPXKE.js";
|
|
86
86
|
import "./chunk-VJNMAWPZ.js";
|
|
87
87
|
import "./chunk-TNEN6VQ2.js";
|
|
88
88
|
import {
|
|
@@ -109,7 +109,7 @@ import {
|
|
|
109
109
|
// src/executor.ts
|
|
110
110
|
import {
|
|
111
111
|
ok as ok25,
|
|
112
|
-
fail as
|
|
112
|
+
fail as fail11,
|
|
113
113
|
isCommandResult,
|
|
114
114
|
CompositeStorage as CompositeStorage2,
|
|
115
115
|
TipCollector as TipCollector2,
|
|
@@ -993,7 +993,7 @@ var evaluateCommand = registerCommand({
|
|
|
993
993
|
|
|
994
994
|
// src/commands/storage.ts
|
|
995
995
|
import { z as z8 } from "zod";
|
|
996
|
-
import { ok as ok8 } from "@dyyz1993/xcli-core";
|
|
996
|
+
import { ok as ok8, fail as fail2 } from "@dyyz1993/xcli-core";
|
|
997
997
|
var getCookiesCommand = registerCommand({
|
|
998
998
|
name: "get-cookies",
|
|
999
999
|
description: "Get all cookies for the current page",
|
|
@@ -1015,6 +1015,7 @@ var setCookieCommand = registerCommand({
|
|
|
1015
1015
|
value: z8.coerce.string(),
|
|
1016
1016
|
domain: z8.coerce.string().optional(),
|
|
1017
1017
|
path: z8.coerce.string().optional(),
|
|
1018
|
+
url: z8.string().optional().describe("Cookie URL (alternative to domain)"),
|
|
1018
1019
|
expires: z8.number().optional(),
|
|
1019
1020
|
httpOnly: z8.boolean().optional(),
|
|
1020
1021
|
secure: z8.boolean().optional(),
|
|
@@ -1034,6 +1035,9 @@ var setCookieCommand = registerCommand({
|
|
|
1034
1035
|
}
|
|
1035
1036
|
}
|
|
1036
1037
|
}
|
|
1038
|
+
if (!cookie.domain && !cookie.url) {
|
|
1039
|
+
return fail2("set-cookie requires --domain or --url, or a non-blank page URL to infer from");
|
|
1040
|
+
}
|
|
1037
1041
|
await ctx.browserContext.addCookies([cookie]);
|
|
1038
1042
|
return ok8({ name: p.name });
|
|
1039
1043
|
}
|
|
@@ -1060,19 +1064,23 @@ var getLocalStorageCommand = registerCommand({
|
|
|
1060
1064
|
z8.object({ data: z8.record(z8.string()) })
|
|
1061
1065
|
]),
|
|
1062
1066
|
handler: async (p, ctx) => {
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1067
|
+
try {
|
|
1068
|
+
if (p.key) {
|
|
1069
|
+
const value = await ctx.page.evaluate((k) => localStorage.getItem(k), p.key);
|
|
1070
|
+
return ok8({ key: p.key, value });
|
|
1071
|
+
}
|
|
1072
|
+
const data = await ctx.page.evaluate(() => {
|
|
1073
|
+
const entries = {};
|
|
1074
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
1075
|
+
const key = localStorage.key(i);
|
|
1076
|
+
if (key) entries[key] = localStorage.getItem(key) ?? "";
|
|
1077
|
+
}
|
|
1078
|
+
return entries;
|
|
1079
|
+
});
|
|
1080
|
+
return ok8({ data });
|
|
1081
|
+
} catch (e) {
|
|
1082
|
+
return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
|
|
1066
1083
|
}
|
|
1067
|
-
const data = await ctx.page.evaluate(() => {
|
|
1068
|
-
const entries = {};
|
|
1069
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
1070
|
-
const key = localStorage.key(i);
|
|
1071
|
-
if (key) entries[key] = localStorage.getItem(key) ?? "";
|
|
1072
|
-
}
|
|
1073
|
-
return entries;
|
|
1074
|
-
});
|
|
1075
|
-
return ok8({ data });
|
|
1076
1084
|
}
|
|
1077
1085
|
});
|
|
1078
1086
|
var setLocalStorageCommand = registerCommand({
|
|
@@ -1085,13 +1093,17 @@ var setLocalStorageCommand = registerCommand({
|
|
|
1085
1093
|
}),
|
|
1086
1094
|
result: z8.object({ key: z8.string() }),
|
|
1087
1095
|
handler: async (p, ctx) => {
|
|
1088
|
-
|
|
1089
|
-
(
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1096
|
+
try {
|
|
1097
|
+
await ctx.page.evaluate(
|
|
1098
|
+
(args) => {
|
|
1099
|
+
localStorage.setItem(args.key, args.value);
|
|
1100
|
+
},
|
|
1101
|
+
{ key: p.key, value: p.value }
|
|
1102
|
+
);
|
|
1103
|
+
return ok8({ key: p.key });
|
|
1104
|
+
} catch (e) {
|
|
1105
|
+
return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
|
|
1106
|
+
}
|
|
1095
1107
|
}
|
|
1096
1108
|
});
|
|
1097
1109
|
var clearLocalStorageCommand = registerCommand({
|
|
@@ -1100,21 +1112,35 @@ var clearLocalStorageCommand = registerCommand({
|
|
|
1100
1112
|
scope: "page",
|
|
1101
1113
|
result: z8.object({ cleared: z8.boolean() }),
|
|
1102
1114
|
handler: async (_p, ctx) => {
|
|
1103
|
-
|
|
1104
|
-
|
|
1115
|
+
try {
|
|
1116
|
+
await ctx.page.evaluate(() => localStorage.clear());
|
|
1117
|
+
return ok8({ cleared: true });
|
|
1118
|
+
} catch (e) {
|
|
1119
|
+
return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
|
|
1120
|
+
}
|
|
1105
1121
|
}
|
|
1106
1122
|
});
|
|
1107
1123
|
|
|
1108
1124
|
// src/commands/screenshot.ts
|
|
1109
1125
|
import { z as z9 } from "zod";
|
|
1110
|
-
import { ok as ok9 } from "@dyyz1993/xcli-core";
|
|
1126
|
+
import { ok as ok9, fail as fail3 } from "@dyyz1993/xcli-core";
|
|
1111
1127
|
import { writeFileSync, mkdirSync } from "fs";
|
|
1112
|
-
import { join as join2 } from "path";
|
|
1128
|
+
import { dirname, join as join2 } from "path";
|
|
1113
1129
|
import { homedir as homedir2 } from "os";
|
|
1114
1130
|
var SCREENSHOTS_DIR = join2(homedir2(), ".xbrowser", "screenshots");
|
|
1115
1131
|
function ensureScreenshotsDir() {
|
|
1116
1132
|
mkdirSync(SCREENSHOTS_DIR, { recursive: true });
|
|
1117
1133
|
}
|
|
1134
|
+
function ensureParentDir(filePath) {
|
|
1135
|
+
const dir = dirname(filePath);
|
|
1136
|
+
if (dir === "." || dir === "/") return null;
|
|
1137
|
+
try {
|
|
1138
|
+
mkdirSync(dir, { recursive: true });
|
|
1139
|
+
return null;
|
|
1140
|
+
} catch (err) {
|
|
1141
|
+
return err instanceof Error ? err.message : String(err);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1118
1144
|
function generateScreenshotPath(format) {
|
|
1119
1145
|
const timestamp = Date.now();
|
|
1120
1146
|
const random = Math.random().toString(36).slice(2, 8);
|
|
@@ -1158,7 +1184,15 @@ var screenshotCommand = registerCommand({
|
|
|
1158
1184
|
buffer = await ctx.page.screenshot(options);
|
|
1159
1185
|
}
|
|
1160
1186
|
if (p.output) {
|
|
1161
|
-
|
|
1187
|
+
const dirErr = ensureParentDir(p.output);
|
|
1188
|
+
if (dirErr) {
|
|
1189
|
+
return fail3(`Cannot create directory for --output "${p.output}": ${dirErr}`);
|
|
1190
|
+
}
|
|
1191
|
+
try {
|
|
1192
|
+
writeFileSync(p.output, buffer, "binary");
|
|
1193
|
+
} catch (err) {
|
|
1194
|
+
return fail3(`Failed to write screenshot to "${p.output}": ${err instanceof Error ? err.message : String(err)}`);
|
|
1195
|
+
}
|
|
1162
1196
|
return ok9({
|
|
1163
1197
|
output: p.output,
|
|
1164
1198
|
format,
|
|
@@ -1276,7 +1310,7 @@ var setViewportCommand = registerCommand({
|
|
|
1276
1310
|
|
|
1277
1311
|
// src/commands/frame.ts
|
|
1278
1312
|
import { z as z12 } from "zod";
|
|
1279
|
-
import { ok as ok12, fail as
|
|
1313
|
+
import { ok as ok12, fail as fail4 } from "@dyyz1993/xcli-core";
|
|
1280
1314
|
var framesCommand = registerCommand({
|
|
1281
1315
|
name: "frames",
|
|
1282
1316
|
description: "List all frames in the current page",
|
|
@@ -1301,10 +1335,10 @@ var framesCommand = registerCommand({
|
|
|
1301
1335
|
});
|
|
1302
1336
|
var frameCommand = registerCommand({
|
|
1303
1337
|
name: "frame",
|
|
1304
|
-
description: "
|
|
1338
|
+
description: "Get frame info by index or name",
|
|
1305
1339
|
scope: "page",
|
|
1306
1340
|
parameters: z12.object({
|
|
1307
|
-
index: z12.number().optional(),
|
|
1341
|
+
index: z12.number().int().min(0).optional(),
|
|
1308
1342
|
name: z12.string().optional()
|
|
1309
1343
|
}),
|
|
1310
1344
|
result: z12.object({
|
|
@@ -1321,10 +1355,10 @@ var frameCommand = registerCommand({
|
|
|
1321
1355
|
} else if (p.name !== void 0) {
|
|
1322
1356
|
targetFrame = rawFrames.find((f) => f.name() === p.name);
|
|
1323
1357
|
} else {
|
|
1324
|
-
return
|
|
1358
|
+
return fail4("Must provide index or name");
|
|
1325
1359
|
}
|
|
1326
1360
|
if (!targetFrame) {
|
|
1327
|
-
return
|
|
1361
|
+
return fail4("Frame not found");
|
|
1328
1362
|
}
|
|
1329
1363
|
return ok12({
|
|
1330
1364
|
name: targetFrame.name(),
|
|
@@ -1970,7 +2004,7 @@ var actionsCommand = registerCommand({
|
|
|
1970
2004
|
|
|
1971
2005
|
// src/commands/scrape.ts
|
|
1972
2006
|
import { z as z15 } from "zod";
|
|
1973
|
-
import { ok as ok15, fail as
|
|
2007
|
+
import { ok as ok15, fail as fail5 } from "@dyyz1993/xcli-core";
|
|
1974
2008
|
|
|
1975
2009
|
// src/lib/html-to-markdown.ts
|
|
1976
2010
|
import * as cheerio from "cheerio";
|
|
@@ -2371,7 +2405,7 @@ var scrapeCommand = registerCommand({
|
|
|
2371
2405
|
try {
|
|
2372
2406
|
const targetUrl = p.url || page.url();
|
|
2373
2407
|
if (!targetUrl || targetUrl === "about:blank") {
|
|
2374
|
-
return
|
|
2408
|
+
return fail5("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
|
|
2375
2409
|
}
|
|
2376
2410
|
let lastError;
|
|
2377
2411
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
@@ -2525,7 +2559,7 @@ var scrapeCommand = registerCommand({
|
|
|
2525
2559
|
}
|
|
2526
2560
|
}
|
|
2527
2561
|
}
|
|
2528
|
-
return
|
|
2562
|
+
return fail5(`Scrape failed after ${maxAttempts} attempt(s): ${lastError?.message ?? "unknown error"}`);
|
|
2529
2563
|
} finally {
|
|
2530
2564
|
await closeEphemeralContext(context);
|
|
2531
2565
|
}
|
|
@@ -2538,7 +2572,7 @@ var scrapeCommand = registerCommand({
|
|
|
2538
2572
|
|
|
2539
2573
|
// src/commands/map.ts
|
|
2540
2574
|
import { z as z16 } from "zod";
|
|
2541
|
-
import { ok as ok16, fail as
|
|
2575
|
+
import { ok as ok16, fail as fail6 } from "@dyyz1993/xcli-core";
|
|
2542
2576
|
|
|
2543
2577
|
// src/utils/url.ts
|
|
2544
2578
|
var SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
@@ -2787,7 +2821,7 @@ var mapCommand = registerCommand({
|
|
|
2787
2821
|
try {
|
|
2788
2822
|
const targetUrl = p.url || page.url();
|
|
2789
2823
|
if (!targetUrl || targetUrl === "about:blank") {
|
|
2790
|
-
return
|
|
2824
|
+
return fail6("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
|
|
2791
2825
|
}
|
|
2792
2826
|
const links = await discoverUrls(page, targetUrl, {
|
|
2793
2827
|
sitemap: p.sitemap,
|
|
@@ -3632,7 +3666,7 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
|
|
|
3632
3666
|
|
|
3633
3667
|
// src/commands/network.ts
|
|
3634
3668
|
import { z as z19 } from "zod";
|
|
3635
|
-
import { ok as ok19, fail as
|
|
3669
|
+
import { ok as ok19, fail as fail7 } from "@dyyz1993/xcli-core";
|
|
3636
3670
|
function extractPath2(url) {
|
|
3637
3671
|
try {
|
|
3638
3672
|
const u = new URL(url);
|
|
@@ -3797,7 +3831,7 @@ var networkCommand = registerCommand({
|
|
|
3797
3831
|
};
|
|
3798
3832
|
if (p.listen) {
|
|
3799
3833
|
const page2 = ctx.page;
|
|
3800
|
-
if (!page2) return
|
|
3834
|
+
if (!page2) return fail7("No active page. Use --cdp to connect first.");
|
|
3801
3835
|
const captures = [];
|
|
3802
3836
|
const consoleMessages = [];
|
|
3803
3837
|
const wsCaptures = [];
|
|
@@ -4454,7 +4488,7 @@ function parseMarkdownResults(rawText) {
|
|
|
4454
4488
|
|
|
4455
4489
|
// src/commands/snapshot.ts
|
|
4456
4490
|
import { z as z21 } from "zod";
|
|
4457
|
-
import { ok as ok20, fail as
|
|
4491
|
+
import { ok as ok20, fail as fail8, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
|
|
4458
4492
|
|
|
4459
4493
|
// src/runtime/ref-store.ts
|
|
4460
4494
|
var sessions = /* @__PURE__ */ new Map();
|
|
@@ -4696,7 +4730,7 @@ async function resolveRefParams(page, params, selectorKeys, cache, sessionId) {
|
|
|
4696
4730
|
|
|
4697
4731
|
// src/utils/site-semantics.ts
|
|
4698
4732
|
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync, readFileSync } from "fs";
|
|
4699
|
-
import { join as join4, dirname } from "path";
|
|
4733
|
+
import { join as join4, dirname as dirname2 } from "path";
|
|
4700
4734
|
import { homedir as homedir4 } from "os";
|
|
4701
4735
|
import { stringify, parse } from "yaml";
|
|
4702
4736
|
import { execFile } from "child_process";
|
|
@@ -4789,7 +4823,7 @@ function extractDomain2(url) {
|
|
|
4789
4823
|
}
|
|
4790
4824
|
function saveSemantics(domain, pagePath, url, elements) {
|
|
4791
4825
|
const filePath = getSemanticsPath(domain);
|
|
4792
|
-
const dir =
|
|
4826
|
+
const dir = dirname2(filePath);
|
|
4793
4827
|
let site;
|
|
4794
4828
|
if (existsSync(filePath)) {
|
|
4795
4829
|
try {
|
|
@@ -5425,7 +5459,7 @@ var snapshotCommand = registerCommand({
|
|
|
5425
5459
|
persistSemantics(url, aria);
|
|
5426
5460
|
return ok20({ url, title, aria, text, dom }, normalizeTips3(tips));
|
|
5427
5461
|
}
|
|
5428
|
-
return
|
|
5462
|
+
return fail8(`Unknown snapshot type: ${p.type}`);
|
|
5429
5463
|
}
|
|
5430
5464
|
});
|
|
5431
5465
|
function persistSemantics(url, aria) {
|
|
@@ -5594,7 +5628,7 @@ var waitForCommand = registerCommand({
|
|
|
5594
5628
|
|
|
5595
5629
|
// src/commands/tab.ts
|
|
5596
5630
|
import { z as z23 } from "zod";
|
|
5597
|
-
import { ok as ok22, fail as
|
|
5631
|
+
import { ok as ok22, fail as fail9 } from "@dyyz1993/xcli-core";
|
|
5598
5632
|
var TabParams = z23.object({
|
|
5599
5633
|
subcommand: z23.enum(["list", "new", "close", "switch"]),
|
|
5600
5634
|
url: z23.string().optional(),
|
|
@@ -5611,7 +5645,7 @@ var tabCommand = registerCommand({
|
|
|
5611
5645
|
}),
|
|
5612
5646
|
handler: async (p, ctx) => {
|
|
5613
5647
|
if (!ctx.browserContext) {
|
|
5614
|
-
return
|
|
5648
|
+
return fail9("No browser context available. Use --cdp to connect to a browser first.");
|
|
5615
5649
|
}
|
|
5616
5650
|
const pages = ctx.browserContext.pages();
|
|
5617
5651
|
switch (p.subcommand) {
|
|
@@ -5620,50 +5654,45 @@ var tabCommand = registerCommand({
|
|
|
5620
5654
|
case "new":
|
|
5621
5655
|
return handleNew(p, pages, ctx);
|
|
5622
5656
|
case "close":
|
|
5623
|
-
return handleClose(p,
|
|
5657
|
+
return handleClose(p, ctx);
|
|
5624
5658
|
case "switch":
|
|
5625
5659
|
return handleSwitch(p, pages, ctx);
|
|
5626
5660
|
default:
|
|
5627
|
-
return
|
|
5661
|
+
return fail9(`Unknown subcommand: ${p.subcommand}`);
|
|
5628
5662
|
}
|
|
5629
5663
|
}
|
|
5630
5664
|
});
|
|
5631
|
-
function handleList(pages, ctx) {
|
|
5632
|
-
const
|
|
5633
|
-
|
|
5665
|
+
async function handleList(pages, ctx) {
|
|
5666
|
+
const tabs = [];
|
|
5667
|
+
let activeIndex = -1;
|
|
5668
|
+
for (let i = 0; i < pages.length; i++) {
|
|
5669
|
+
const page = pages[i];
|
|
5634
5670
|
const url = page.url();
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
});
|
|
5642
|
-
}
|
|
5643
|
-
} catch {
|
|
5644
|
-
title = "";
|
|
5645
|
-
}
|
|
5646
|
-
return {
|
|
5647
|
-
index: i,
|
|
5648
|
-
url,
|
|
5649
|
-
title,
|
|
5650
|
-
active: i === currentIndex
|
|
5651
|
-
};
|
|
5652
|
-
});
|
|
5653
|
-
return ok22({ tabs, total: tabs.length, activeIndex: currentIndex });
|
|
5671
|
+
const title = await page.title().catch(() => "");
|
|
5672
|
+
const isActive = page === ctx.page;
|
|
5673
|
+
if (isActive) activeIndex = i;
|
|
5674
|
+
tabs.push({ index: i, url, title, active: isActive });
|
|
5675
|
+
}
|
|
5676
|
+
return ok22({ tabs, total: tabs.length, activeIndex });
|
|
5654
5677
|
}
|
|
5655
5678
|
async function handleNew(p, _pages, ctx) {
|
|
5656
5679
|
const newPage = await ctx.browserContext.newPage();
|
|
5680
|
+
const warnings = [];
|
|
5657
5681
|
if (p.url) {
|
|
5658
5682
|
let url = p.url;
|
|
5659
|
-
if (!/^
|
|
5683
|
+
if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(url)) {
|
|
5660
5684
|
url = "https://" + url;
|
|
5661
5685
|
}
|
|
5662
|
-
|
|
5663
|
-
|
|
5686
|
+
try {
|
|
5687
|
+
await newPage.goto(url, { waitUntil: "domcontentloaded", timeout: 15e3 });
|
|
5688
|
+
} catch (err) {
|
|
5689
|
+
warnings.push(`Navigation to "${url}" failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5690
|
+
}
|
|
5691
|
+
}
|
|
5692
|
+
try {
|
|
5693
|
+
await newPage.waitForLoadState("domcontentloaded");
|
|
5694
|
+
} catch {
|
|
5664
5695
|
}
|
|
5665
|
-
await newPage.waitForLoadState("domcontentloaded").catch(() => {
|
|
5666
|
-
});
|
|
5667
5696
|
const session = ctx.sessionId ? getSessionById(ctx.sessionId) : void 0;
|
|
5668
5697
|
if (session) {
|
|
5669
5698
|
setActivePage(session, newPage);
|
|
@@ -5676,18 +5705,20 @@ async function handleNew(p, _pages, ctx) {
|
|
|
5676
5705
|
index: newIndex >= 0 ? newIndex : allPages.length - 1,
|
|
5677
5706
|
url: newPage.url(),
|
|
5678
5707
|
title,
|
|
5679
|
-
total: allPages.length
|
|
5708
|
+
total: allPages.length,
|
|
5709
|
+
...warnings.length > 0 ? { warning: warnings.join("; ") } : {}
|
|
5680
5710
|
});
|
|
5681
5711
|
}
|
|
5682
|
-
async function handleClose(p,
|
|
5683
|
-
|
|
5684
|
-
|
|
5712
|
+
async function handleClose(p, ctx) {
|
|
5713
|
+
const currentPages = ctx.browserContext.pages();
|
|
5714
|
+
if (currentPages.length <= 1) {
|
|
5715
|
+
return fail9("Cannot close the last remaining tab");
|
|
5685
5716
|
}
|
|
5686
|
-
const closeIndex = p.index ??
|
|
5687
|
-
if (closeIndex < 0 || closeIndex >=
|
|
5688
|
-
return
|
|
5717
|
+
const closeIndex = p.index ?? currentPages.findIndex((pg) => pg === ctx.page);
|
|
5718
|
+
if (closeIndex < 0 || closeIndex >= currentPages.length) {
|
|
5719
|
+
return fail9(`Invalid tab index: ${closeIndex}. Valid range: 0-${currentPages.length - 1}`);
|
|
5689
5720
|
}
|
|
5690
|
-
const pageToClose =
|
|
5721
|
+
const pageToClose = currentPages[closeIndex];
|
|
5691
5722
|
const isActivePage = pageToClose === ctx.page;
|
|
5692
5723
|
await pageToClose.close();
|
|
5693
5724
|
const remainingPages = ctx.browserContext.pages();
|
|
@@ -5703,15 +5734,15 @@ async function handleClose(p, pages, ctx) {
|
|
|
5703
5734
|
return ok22({
|
|
5704
5735
|
closedIndex: closeIndex,
|
|
5705
5736
|
total: remainingPages.length,
|
|
5706
|
-
activeIndex: isActivePage ? closeIndex < remainingPages.length ? closeIndex : remainingPages.length - 1 :
|
|
5737
|
+
activeIndex: isActivePage ? closeIndex < remainingPages.length ? closeIndex : remainingPages.length - 1 : remainingPages.findIndex((pg) => pg === ctx.page)
|
|
5707
5738
|
});
|
|
5708
5739
|
}
|
|
5709
5740
|
async function handleSwitch(p, pages, ctx) {
|
|
5710
5741
|
if (p.index === void 0) {
|
|
5711
|
-
return
|
|
5742
|
+
return fail9("Parameter --index is required for switch subcommand");
|
|
5712
5743
|
}
|
|
5713
5744
|
if (p.index < 0 || p.index >= pages.length) {
|
|
5714
|
-
return
|
|
5745
|
+
return fail9(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
|
|
5715
5746
|
}
|
|
5716
5747
|
const targetPage = pages[p.index];
|
|
5717
5748
|
await targetPage.bringToFront().catch(() => {
|
|
@@ -5963,7 +5994,7 @@ registerCommandDefinition("addinitscript", ["script"]);
|
|
|
5963
5994
|
|
|
5964
5995
|
// src/commands/find.ts
|
|
5965
5996
|
import { z as z25 } from "zod";
|
|
5966
|
-
import { ok as ok24, fail as
|
|
5997
|
+
import { ok as ok24, fail as fail10, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
|
|
5967
5998
|
var actionSchema2 = z25.enum(["click", "fill", "type", "select", "hover", "check"]);
|
|
5968
5999
|
var findCommand = registerCommand({
|
|
5969
6000
|
name: "find",
|
|
@@ -6004,7 +6035,7 @@ var findCommand = registerCommand({
|
|
|
6004
6035
|
});
|
|
6005
6036
|
const count = await locator.count();
|
|
6006
6037
|
if (count === 0) {
|
|
6007
|
-
return
|
|
6038
|
+
return fail10(`No element found with ${p.strategy}="${p.value}"`);
|
|
6008
6039
|
}
|
|
6009
6040
|
const tips = [];
|
|
6010
6041
|
const target = selectTarget(locator, p.strategy);
|
|
@@ -6016,15 +6047,15 @@ var findCommand = registerCommand({
|
|
|
6016
6047
|
await target.click({ timeout: p.timeout, force: true });
|
|
6017
6048
|
return okWithTips({ matched: count, selector, action: "click" }, tips);
|
|
6018
6049
|
} else if (actionName === "fill") {
|
|
6019
|
-
if (actionValue === void 0) return
|
|
6050
|
+
if (actionValue === void 0) return fail10("find fill requires a value");
|
|
6020
6051
|
await target.fill(actionValue, { timeout: p.timeout, force: true });
|
|
6021
6052
|
return okWithTips({ matched: count, selector, action: `fill("${actionValue}")` }, tips);
|
|
6022
6053
|
} else if (actionName === "type") {
|
|
6023
|
-
if (actionValue === void 0) return
|
|
6054
|
+
if (actionValue === void 0) return fail10("find type requires a value");
|
|
6024
6055
|
await target.type(actionValue, { delay: 10, timeout: p.timeout });
|
|
6025
6056
|
return okWithTips({ matched: count, selector, action: `type("${actionValue}")` }, tips);
|
|
6026
6057
|
} else if (actionName === "select") {
|
|
6027
|
-
if (actionValue === void 0) return
|
|
6058
|
+
if (actionValue === void 0) return fail10("find select requires a value");
|
|
6028
6059
|
await target.selectOption(actionValue);
|
|
6029
6060
|
return okWithTips({ matched: count, selector, action: `select("${actionValue}")` }, tips);
|
|
6030
6061
|
} else if (actionName === "hover") {
|
|
@@ -6797,12 +6828,24 @@ var XBrowserPluginLoader = class {
|
|
|
6797
6828
|
// src/utils/plugin-singleton.ts
|
|
6798
6829
|
var pluginLoader = null;
|
|
6799
6830
|
var pluginsScanned = false;
|
|
6831
|
+
function silenceSchemaWarnings(fn) {
|
|
6832
|
+
if (process.env.XBROWSER_DEBUG || process.env.VITEST_WORKER_ID) return fn();
|
|
6833
|
+
const originalWarn = console.warn;
|
|
6834
|
+
console.warn = (...args) => {
|
|
6835
|
+
const msg = typeof args[0] === "string" ? args[0] : "";
|
|
6836
|
+
if (msg.includes('has no "result" schema')) return;
|
|
6837
|
+
originalWarn(...args);
|
|
6838
|
+
};
|
|
6839
|
+
return fn().finally(() => {
|
|
6840
|
+
console.warn = originalWarn;
|
|
6841
|
+
});
|
|
6842
|
+
}
|
|
6800
6843
|
async function getPluginLoader() {
|
|
6801
6844
|
if (!pluginLoader) {
|
|
6802
6845
|
pluginLoader = new XBrowserPluginLoader();
|
|
6803
6846
|
}
|
|
6804
6847
|
if (!pluginsScanned) {
|
|
6805
|
-
await pluginLoader.scanAndLoad();
|
|
6848
|
+
await silenceSchemaWarnings(() => pluginLoader.scanAndLoad());
|
|
6806
6849
|
pluginsScanned = true;
|
|
6807
6850
|
}
|
|
6808
6851
|
return pluginLoader;
|
|
@@ -7460,7 +7503,7 @@ async function guardCheck(commandName) {
|
|
|
7460
7503
|
}
|
|
7461
7504
|
}
|
|
7462
7505
|
function errorResult(message) {
|
|
7463
|
-
return { ...
|
|
7506
|
+
return { ...fail11(message), duration: 0 };
|
|
7464
7507
|
}
|
|
7465
7508
|
function tipsToMessages(tips) {
|
|
7466
7509
|
if (!tips || tips.length === 0) return [];
|
|
@@ -7540,7 +7583,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7540
7583
|
}
|
|
7541
7584
|
let targetPageOverride = null;
|
|
7542
7585
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7543
|
-
const { findTargetPage } = await import("./browser-
|
|
7586
|
+
const { findTargetPage } = await import("./browser-C63PFGPT.js");
|
|
7544
7587
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7545
7588
|
if (!targetPageOverride) {
|
|
7546
7589
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -7764,7 +7807,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7764
7807
|
duration,
|
|
7765
7808
|
timestamp: start
|
|
7766
7809
|
});
|
|
7767
|
-
return { ...
|
|
7810
|
+
return { ...fail11(errorMessage), duration };
|
|
7768
7811
|
} finally {
|
|
7769
7812
|
}
|
|
7770
7813
|
}
|
|
@@ -7805,7 +7848,7 @@ async function executeChain(input, options) {
|
|
|
7805
7848
|
results.push({
|
|
7806
7849
|
command: cmdName,
|
|
7807
7850
|
raw: cmdStr,
|
|
7808
|
-
...
|
|
7851
|
+
...fail11(`Plugin "${cmdName}" requires a sub-command`),
|
|
7809
7852
|
duration: 0
|
|
7810
7853
|
});
|
|
7811
7854
|
if (type === "and") {
|
|
@@ -7824,7 +7867,7 @@ async function executeChain(input, options) {
|
|
|
7824
7867
|
results.push({
|
|
7825
7868
|
command: cmdName,
|
|
7826
7869
|
raw: cmdStr,
|
|
7827
|
-
...
|
|
7870
|
+
...fail11(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
|
|
7828
7871
|
duration: 0
|
|
7829
7872
|
});
|
|
7830
7873
|
if (type === "and") {
|
|
@@ -7956,7 +7999,7 @@ async function executeChain(input, options) {
|
|
|
7956
7999
|
results.push({
|
|
7957
8000
|
command: `${cmdName} ${subCommand}`,
|
|
7958
8001
|
raw: cmdStr,
|
|
7959
|
-
...
|
|
8002
|
+
...fail11(errorMessage),
|
|
7960
8003
|
duration: duration2
|
|
7961
8004
|
});
|
|
7962
8005
|
if (type === "and") {
|
|
@@ -8288,8 +8331,14 @@ var configBuiltin = {
|
|
|
8288
8331
|
"preview.quality",
|
|
8289
8332
|
"preview.fps"
|
|
8290
8333
|
]);
|
|
8291
|
-
|
|
8292
|
-
|
|
8334
|
+
const isKnownNamespace = key.startsWith("browser.") || key.startsWith("captcha.") || key.startsWith("preview.");
|
|
8335
|
+
if (!knownKeys.has(key) && !isKnownNamespace) {
|
|
8336
|
+
console.error(
|
|
8337
|
+
`Unknown config key: "${key}".
|
|
8338
|
+
Known namespaces: browser.*, captcha.*, preview.*
|
|
8339
|
+
Run "xbrowser config list" to see current keys.`
|
|
8340
|
+
);
|
|
8341
|
+
process.exit(1);
|
|
8293
8342
|
}
|
|
8294
8343
|
setConfigValue(key, value);
|
|
8295
8344
|
console.log(`Set ${key} = ${value}`);
|
|
@@ -8312,7 +8361,7 @@ import {
|
|
|
8312
8361
|
readFileSync as readFileSync8,
|
|
8313
8362
|
writeFileSync as writeFileSync10
|
|
8314
8363
|
} from "fs";
|
|
8315
|
-
import { resolve as resolve8, basename as basename2, dirname as
|
|
8364
|
+
import { resolve as resolve8, basename as basename2, dirname as dirname4 } from "path";
|
|
8316
8365
|
import { homedir as homedir7 } from "os";
|
|
8317
8366
|
|
|
8318
8367
|
// src/plugin/install-sources/local.ts
|
|
@@ -8570,7 +8619,7 @@ import {
|
|
|
8570
8619
|
rmSync as rmSync5,
|
|
8571
8620
|
cpSync as cpSync5
|
|
8572
8621
|
} from "fs";
|
|
8573
|
-
import { resolve as resolve7, join as join10, dirname as
|
|
8622
|
+
import { resolve as resolve7, join as join10, dirname as dirname3 } from "path";
|
|
8574
8623
|
import { tmpdir as tmpdir4 } from "os";
|
|
8575
8624
|
import { gunzipSync } from "zlib";
|
|
8576
8625
|
import {
|
|
@@ -8636,7 +8685,7 @@ function extractManifestToDir(manifest, targetDir) {
|
|
|
8636
8685
|
mkdirSync7(targetDir, { recursive: true });
|
|
8637
8686
|
for (const file of manifest) {
|
|
8638
8687
|
const filePath = resolve7(targetDir, file.path);
|
|
8639
|
-
mkdirSync7(
|
|
8688
|
+
mkdirSync7(dirname3(filePath), { recursive: true });
|
|
8640
8689
|
writeFileSync9(filePath, Buffer.from(file.content, "base64"));
|
|
8641
8690
|
}
|
|
8642
8691
|
}
|
|
@@ -8885,7 +8934,7 @@ var PluginInstaller = class {
|
|
|
8885
8934
|
if (resp.ok) {
|
|
8886
8935
|
const content2 = await resp.text();
|
|
8887
8936
|
const dst = resolve8(sharedDir, file);
|
|
8888
|
-
const dstDir =
|
|
8937
|
+
const dstDir = dirname4(dst);
|
|
8889
8938
|
if (!existsSync10(dstDir)) mkdirSync8(dstDir, { recursive: true });
|
|
8890
8939
|
writeFileSync10(dst, content2, "utf-8");
|
|
8891
8940
|
console.log(`\u2705 Downloaded shared/${file} from GitHub`);
|
|
@@ -8910,7 +8959,7 @@ var PluginInstaller = class {
|
|
|
8910
8959
|
const dst = resolve8(sharedDir, file);
|
|
8911
8960
|
if (existsSync10(src)) {
|
|
8912
8961
|
try {
|
|
8913
|
-
cpSync6(
|
|
8962
|
+
cpSync6(dirname4(src), dirname4(dst), { recursive: true });
|
|
8914
8963
|
console.log(`\u2705 Copied shared/${file} for plugin "${basename2(pluginDir)}"`);
|
|
8915
8964
|
} catch {
|
|
8916
8965
|
try {
|
|
@@ -9053,7 +9102,7 @@ function outputResult(result, mode = "text") {
|
|
|
9053
9102
|
function outputError(message) {
|
|
9054
9103
|
const formatted = formatter.formatError(message, { color: true, emoji: false });
|
|
9055
9104
|
console.error(formatted);
|
|
9056
|
-
process.
|
|
9105
|
+
process.exitCode = 1;
|
|
9057
9106
|
}
|
|
9058
9107
|
function outputEnvelope(result, meta, mode) {
|
|
9059
9108
|
if (mode !== "json" && mode !== "yaml") {
|
|
@@ -10528,7 +10577,6 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10528
10577
|
break;
|
|
10529
10578
|
}
|
|
10530
10579
|
case "scrape":
|
|
10531
|
-
if (!args[0]) outputError("Usage: xbrowser scrape <url> [--format markdown|html|text] [--mode raw|clean|compact] [--selector <sel>] [--timeout <ms>]");
|
|
10532
10580
|
cmdName = "scrape";
|
|
10533
10581
|
params = {
|
|
10534
10582
|
url: args[0],
|
|
@@ -10540,7 +10588,6 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10540
10588
|
};
|
|
10541
10589
|
break;
|
|
10542
10590
|
case "map":
|
|
10543
|
-
if (!args[0]) outputError("Usage: xbrowser map <url> [--search <query>] [--sitemap include|only] [--include-subdomains] [--limit <n>]");
|
|
10544
10591
|
cmdName = "map";
|
|
10545
10592
|
params = {
|
|
10546
10593
|
url: args[0],
|
|
@@ -10675,12 +10722,20 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10675
10722
|
}
|
|
10676
10723
|
}
|
|
10677
10724
|
const outputFile = options.output;
|
|
10678
|
-
|
|
10679
|
-
|
|
10680
|
-
|
|
10681
|
-
writeFileSync13
|
|
10682
|
-
|
|
10725
|
+
const dataObj = result.data;
|
|
10726
|
+
const handlerAlreadyWrote = !!dataObj && typeof dataObj.output === "string";
|
|
10727
|
+
if (outputFile && result.success && result.data && !handlerAlreadyWrote) {
|
|
10728
|
+
const { writeFileSync: writeFileSync13, mkdirSync: mkdirSync11 } = await import("fs");
|
|
10729
|
+
const { dirname: dirname7 } = await import("path");
|
|
10730
|
+
const content = typeof result.data === "string" ? result.data : dataObj.content || dataObj.text || JSON.stringify(result.data, null, 2);
|
|
10731
|
+
try {
|
|
10732
|
+
mkdirSync11(dirname7(outputFile), { recursive: true });
|
|
10733
|
+
writeFileSync13(outputFile, content, "utf-8");
|
|
10734
|
+
console.log(`
|
|
10683
10735
|
\u{1F4C4} Written to ${outputFile}`);
|
|
10736
|
+
} catch (err) {
|
|
10737
|
+
outputError(`Failed to write --output "${outputFile}": ${err instanceof Error ? err.message : String(err)}`);
|
|
10738
|
+
}
|
|
10684
10739
|
}
|
|
10685
10740
|
}
|
|
10686
10741
|
|
|
@@ -11146,7 +11201,11 @@ Total: ${enrichedPlugins.length} plugins`);
|
|
|
11146
11201
|
case "login":
|
|
11147
11202
|
case "whoami":
|
|
11148
11203
|
case "logout":
|
|
11149
|
-
outputError(
|
|
11204
|
+
outputError(
|
|
11205
|
+
`"plugin ${sub}" is no longer available: the built-in marketplace publisher was removed.
|
|
11206
|
+
To publish a plugin, use npm directly: \`npm publish\` from the plugin directory.
|
|
11207
|
+
See docs/plugin-guide.md for the publishing workflow.`
|
|
11208
|
+
);
|
|
11150
11209
|
break;
|
|
11151
11210
|
default:
|
|
11152
11211
|
console.log(handlePluginHelp());
|
|
@@ -11507,23 +11566,51 @@ async function handleConvert(args, _mode) {
|
|
|
11507
11566
|
const path5 = await import("path");
|
|
11508
11567
|
const { default: yaml3 } = await import("yaml");
|
|
11509
11568
|
const { generateJSScript: generateJSScript2, generatePythonScript: generatePythonScript2, generateBashScript: generateBashScript2 } = await import("./convert-R3XXYKC6.js");
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
|
|
11569
|
+
let recording;
|
|
11570
|
+
try {
|
|
11571
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
11572
|
+
recording = yaml3.parse(content);
|
|
11573
|
+
} catch (e) {
|
|
11574
|
+
console.error(`Error: Failed to read "${filePath}": ${e instanceof Error ? e.message.split("\n")[0] : String(e)}`);
|
|
11575
|
+
process.exit(1);
|
|
11576
|
+
}
|
|
11577
|
+
if (recording === null || typeof recording !== "object" || Array.isArray(recording)) {
|
|
11578
|
+
console.error(`Error: "${filePath}" does not contain a valid recording (expected a YAML/JSON object with events or actions).`);
|
|
11579
|
+
process.exit(1);
|
|
11580
|
+
}
|
|
11581
|
+
const rawActions = recording.actions;
|
|
11582
|
+
if (Array.isArray(rawActions) && !Array.isArray(recording.events)) {
|
|
11583
|
+
recording.events = rawActions.map((a) => {
|
|
11584
|
+
const action = a;
|
|
11585
|
+
const element = action.element ?? {};
|
|
11586
|
+
return {
|
|
11587
|
+
type: action.type,
|
|
11588
|
+
selector: element.selector ?? action.selector,
|
|
11589
|
+
data: {
|
|
11590
|
+
...action.data,
|
|
11591
|
+
value: action.value ?? action.data?.value,
|
|
11592
|
+
key: action.key ?? action.data?.key,
|
|
11593
|
+
x: action.scrollX ?? action.data?.x,
|
|
11594
|
+
y: action.scrollY ?? action.data?.y
|
|
11595
|
+
}
|
|
11596
|
+
};
|
|
11597
|
+
});
|
|
11598
|
+
}
|
|
11513
11599
|
const ext = path5.extname(outputPath).toLowerCase();
|
|
11600
|
+
const recordingTyped = recording;
|
|
11514
11601
|
let script;
|
|
11515
11602
|
if (ext === ".py") {
|
|
11516
|
-
script = generatePythonScript2(
|
|
11603
|
+
script = generatePythonScript2(recordingTyped);
|
|
11517
11604
|
} else if (ext === ".sh") {
|
|
11518
|
-
script = generateBashScript2(
|
|
11605
|
+
script = generateBashScript2(recordingTyped);
|
|
11519
11606
|
} else {
|
|
11520
|
-
script = generateJSScript2(
|
|
11607
|
+
script = generateJSScript2(recordingTyped);
|
|
11521
11608
|
}
|
|
11522
11609
|
fs6.writeFileSync(outputPath, script);
|
|
11523
11610
|
fs6.chmodSync(outputPath, 493);
|
|
11524
|
-
const eventCount = (
|
|
11611
|
+
const eventCount = (recordingTyped.events || []).length;
|
|
11525
11612
|
console.log(`Converted ${filePath} -> ${outputPath}`);
|
|
11526
|
-
console.log(` Events: ${eventCount}, Start URL: ${
|
|
11613
|
+
console.log(` Events: ${eventCount}, Start URL: ${recordingTyped.startUrl}`);
|
|
11527
11614
|
console.log(` Run: ${ext === ".py" ? "python" : ext === ".sh" ? "./" : "node"} ${outputPath}`);
|
|
11528
11615
|
}
|
|
11529
11616
|
async function handleExtract(args, _mode) {
|
|
@@ -11532,11 +11619,16 @@ async function handleExtract(args, _mode) {
|
|
|
11532
11619
|
console.error("Usage: xbrowser extract <recording.yaml>");
|
|
11533
11620
|
process.exit(1);
|
|
11534
11621
|
}
|
|
11535
|
-
const { extractAndSave: extractAndSave2, printExtractSummary: printExtractSummary2 } = await import("./extract-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11622
|
+
const { extractAndSave: extractAndSave2, printExtractSummary: printExtractSummary2 } = await import("./extract-DSU2RVMN.js");
|
|
11623
|
+
try {
|
|
11624
|
+
const { summary, outputPath } = extractAndSave2(filePath);
|
|
11625
|
+
printExtractSummary2(summary);
|
|
11626
|
+
console.log(`
|
|
11539
11627
|
Saved LLM summary: ${outputPath}`);
|
|
11628
|
+
} catch (e) {
|
|
11629
|
+
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
11630
|
+
process.exit(1);
|
|
11631
|
+
}
|
|
11540
11632
|
}
|
|
11541
11633
|
async function handleFilter(args, _mode, options) {
|
|
11542
11634
|
const filePath = args[0];
|
|
@@ -11545,19 +11637,24 @@ async function handleFilter(args, _mode, options) {
|
|
|
11545
11637
|
console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude type1,type2]");
|
|
11546
11638
|
process.exit(1);
|
|
11547
11639
|
}
|
|
11548
|
-
const { filterRecording: filterRecording2, parseExcludeTypes: parseExcludeTypes2 } = await import("./filter-
|
|
11640
|
+
const { filterRecording: filterRecording2, parseExcludeTypes: parseExcludeTypes2 } = await import("./filter-3DXC6432.js");
|
|
11549
11641
|
const excludeArgs = args.slice(2).concat(
|
|
11550
11642
|
Object.entries(options || {}).flatMap(
|
|
11551
11643
|
([k, v]) => k.startsWith("exclude") ? [`--${k}${typeof v === "string" ? "=" + v : ""}`] : []
|
|
11552
11644
|
)
|
|
11553
11645
|
);
|
|
11554
11646
|
const excludeTypes = parseExcludeTypes2(excludeArgs);
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11647
|
+
try {
|
|
11648
|
+
const result = filterRecording2(filePath, outputPath, excludeTypes);
|
|
11649
|
+
console.log(`Filtered ${filePath} -> ${outputPath}`);
|
|
11650
|
+
console.log(` Original: ${result.originalCount}, After: ${result.filteredCount}, Removed: ${result.removed} (${result.percentage}%)`);
|
|
11651
|
+
} catch (e) {
|
|
11652
|
+
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
11653
|
+
process.exit(1);
|
|
11654
|
+
}
|
|
11558
11655
|
}
|
|
11559
11656
|
async function handleGeneratePlugin(sessionName, pluginName, outputDir) {
|
|
11560
|
-
const { SessionRecorder: SessionRecorder2 } = await import("./session-recorder-
|
|
11657
|
+
const { SessionRecorder: SessionRecorder2 } = await import("./session-recorder-KPU4YQAE.js");
|
|
11561
11658
|
const { readSiteKnowledge: readSiteKnowledge2, toMarkdown } = await import("./site-knowledge-SYC6VCDB.js");
|
|
11562
11659
|
const { mkdirSync: mkdirSync11, writeFileSync: writeFileSync13 } = await import("fs");
|
|
11563
11660
|
const { join: join14 } = await import("path");
|
|
@@ -11725,8 +11822,21 @@ export default createSite({
|
|
|
11725
11822
|
url: '${data.startUrl}',
|
|
11726
11823
|
detect: async (ctx: CommandContext) => {
|
|
11727
11824
|
const page = ensurePage(ctx);
|
|
11728
|
-
//
|
|
11729
|
-
|
|
11825
|
+
// Best-effort login detection: check for common logged-in indicators.
|
|
11826
|
+
// Customize these selectors for your site's specific login state.
|
|
11827
|
+
try {
|
|
11828
|
+
const loggedIn = await page.evaluate(() => {
|
|
11829
|
+
const el = document.querySelector(
|
|
11830
|
+
'[class*="avatar"], [class*="user-info"], [class*="logged-in"], ' +
|
|
11831
|
+
'[data-testid*="user"], [data-testid*="avatar"], ' +
|
|
11832
|
+
'a[href*="logout"], button[class*="logout"]'
|
|
11833
|
+
);
|
|
11834
|
+
return !!el;
|
|
11835
|
+
});
|
|
11836
|
+
return loggedIn;
|
|
11837
|
+
} catch {
|
|
11838
|
+
return false;
|
|
11839
|
+
}
|
|
11730
11840
|
},
|
|
11731
11841
|
},
|
|
11732
11842
|
|
|
@@ -11753,7 +11863,7 @@ async function handleRun(filePath, options) {
|
|
|
11753
11863
|
outputError("No commands found in file");
|
|
11754
11864
|
return;
|
|
11755
11865
|
}
|
|
11756
|
-
const chain = commands.join("
|
|
11866
|
+
const chain = commands.join(" ; ");
|
|
11757
11867
|
const chainResult = await executeChain(chain, {
|
|
11758
11868
|
cdpEndpoint: options?.cdpEndpoint,
|
|
11759
11869
|
sessionName: options?.sessionName,
|
|
@@ -13088,6 +13198,7 @@ async function handleChainInput(input, argv) {
|
|
|
13088
13198
|
}
|
|
13089
13199
|
async function routeCommand(argvIn, stdinCommands) {
|
|
13090
13200
|
let argv = argvIn;
|
|
13201
|
+
process.exitCode = 0;
|
|
13091
13202
|
try {
|
|
13092
13203
|
if (stdinCommands && stdinCommands.length > 0) {
|
|
13093
13204
|
await handleStdinMode(stdinCommands, argv);
|
|
@@ -13485,6 +13596,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13485
13596
|
const result = await forwardExec(`${command}.${subCommand}`, params, sessionName, cdpEndpoint, userTimeout);
|
|
13486
13597
|
const resultData = result && typeof result === "object" && "data" in result ? result.data : void 0;
|
|
13487
13598
|
if (result && result.success === false && resultData?.code === "LOGIN_REQUIRED") {
|
|
13599
|
+
process.exitCode = 1;
|
|
13488
13600
|
outputLoginRequired(result, mode);
|
|
13489
13601
|
return;
|
|
13490
13602
|
}
|
|
@@ -13519,7 +13631,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13519
13631
|
const targetPage = pages[cmdTabIndex];
|
|
13520
13632
|
await targetPage.bringToFront().catch(() => {
|
|
13521
13633
|
});
|
|
13522
|
-
const { setActivePage: setActivePage2 } = await import("./browser-
|
|
13634
|
+
const { setActivePage: setActivePage2 } = await import("./browser-C63PFGPT.js");
|
|
13523
13635
|
setActivePage2(session, targetPage);
|
|
13524
13636
|
}
|
|
13525
13637
|
}
|
|
@@ -13556,6 +13668,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13556
13668
|
sessionName
|
|
13557
13669
|
});
|
|
13558
13670
|
if (!loginGuard.ok) {
|
|
13671
|
+
process.exitCode = 1;
|
|
13559
13672
|
const result2 = {
|
|
13560
13673
|
success: false,
|
|
13561
13674
|
data: loginGuard.data ?? null,
|
|
@@ -13599,8 +13712,9 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13599
13712
|
}
|
|
13600
13713
|
const outputData = isCommandResult2(result) ? result.data : result && typeof result === "object" ? result.data ?? result : result;
|
|
13601
13714
|
const tips = isCommandResult2(result) ? result.tips : result && typeof result === "object" ? result.tips : void 0;
|
|
13715
|
+
const resultSuccess = isCommandResult2(result) ? result.success !== false : true;
|
|
13716
|
+
if (!resultSuccess) process.exitCode = 1;
|
|
13602
13717
|
if (mode === "json" || mode === "yaml") {
|
|
13603
|
-
const resultSuccess = isCommandResult2(result) ? result.success !== false : true;
|
|
13604
13718
|
const resultMsg = isCommandResult2(result) ? result.message : void 0;
|
|
13605
13719
|
const duration = Date.now() - cmdStart;
|
|
13606
13720
|
const envelopeMeta = { command: `${command} ${subCommand}` };
|