@xbrowser/cli 1.8.6 → 1.9.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-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-CJJ564VK.js → chunk-MSQIPXKE.js} +1 -1
- package/dist/{chunk-HXLMMSU3.js → chunk-OOJ2H7IL.js} +1 -1
- package/dist/{chunk-FL6DOSWV.js → chunk-TTZNR3QP.js} +30 -2
- package/dist/{chunk-RMYEHTLS.js → chunk-YSCY52UJ.js} +30 -2
- package/dist/{chunk-SJLRCE6N.js → chunk-Z7WC4P2M.js} +1 -1
- package/dist/cli.js +42 -34
- package/dist/daemon-main.js +57 -171
- package/dist/index.d.ts +11 -1
- package/dist/index.js +41 -33
- 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 +1 -1
package/dist/daemon-main.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-VEDJ5XSQ.js";
|
|
6
6
|
import {
|
|
7
7
|
SessionRecorder
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-TTZNR3QP.js";
|
|
9
9
|
import {
|
|
10
10
|
closeEphemeralContext,
|
|
11
11
|
closeSessionByName,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
resolveLaunchOpts,
|
|
22
22
|
saveSessionDiskMeta,
|
|
23
23
|
setActivePage
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-OOJ2H7IL.js";
|
|
25
25
|
import "./chunk-VJNMAWPZ.js";
|
|
26
26
|
import "./chunk-TNEN6VQ2.js";
|
|
27
27
|
import {
|
|
@@ -38,15 +38,13 @@ import {
|
|
|
38
38
|
import "./chunk-KFQGP6VL.js";
|
|
39
39
|
|
|
40
40
|
// src/daemon/daemon-main.ts
|
|
41
|
-
import { writeFileSync as
|
|
42
|
-
import { join as
|
|
43
|
-
import { homedir as
|
|
41
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, appendFileSync, unlinkSync } from "fs";
|
|
42
|
+
import { join as join7 } from "path";
|
|
43
|
+
import { homedir as homedir7 } from "os";
|
|
44
44
|
import { startHttpServer } from "@dyyz1993/xcli-core";
|
|
45
45
|
|
|
46
46
|
// src/daemon/rpc-handlers.ts
|
|
47
|
-
import {
|
|
48
|
-
import { join as join7 } from "path";
|
|
49
|
-
import { homedir as homedir7 } from "os";
|
|
47
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
50
48
|
import {
|
|
51
49
|
createSessionMeta,
|
|
52
50
|
removeSession
|
|
@@ -55,7 +53,7 @@ import {
|
|
|
55
53
|
// src/executor.ts
|
|
56
54
|
import {
|
|
57
55
|
ok as ok25,
|
|
58
|
-
fail as
|
|
56
|
+
fail as fail9,
|
|
59
57
|
isCommandResult,
|
|
60
58
|
CompositeStorage as CompositeStorage2,
|
|
61
59
|
TipCollector as TipCollector2,
|
|
@@ -2283,7 +2281,7 @@ var scrapeCommand = registerCommand({
|
|
|
2283
2281
|
scope: "project",
|
|
2284
2282
|
selectorParams: ["selector"],
|
|
2285
2283
|
parameters: z15.object({
|
|
2286
|
-
url: z15.string(),
|
|
2284
|
+
url: z15.string().optional(),
|
|
2287
2285
|
selector: z15.string().optional(),
|
|
2288
2286
|
timeout: z15.number().default(3e4),
|
|
2289
2287
|
format: z15.enum(["markdown", "html", "text"]).default("markdown"),
|
|
@@ -2296,10 +2294,14 @@ var scrapeCommand = registerCommand({
|
|
|
2296
2294
|
const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
|
|
2297
2295
|
const maxAttempts = p.retries + 1;
|
|
2298
2296
|
try {
|
|
2297
|
+
const targetUrl = p.url || page.url();
|
|
2298
|
+
if (!targetUrl || targetUrl === "about:blank") {
|
|
2299
|
+
return fail3("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
|
|
2300
|
+
}
|
|
2299
2301
|
let lastError;
|
|
2300
2302
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
2301
2303
|
try {
|
|
2302
|
-
await page.goto(
|
|
2304
|
+
await page.goto(targetUrl, { waitUntil: "commit", timeout: p.timeout });
|
|
2303
2305
|
await page.waitForSelector("body", { timeout: p.timeout }).catch(() => {
|
|
2304
2306
|
});
|
|
2305
2307
|
await page.waitForLoadState("networkidle", Math.min(p.timeout, 8e3)).catch(() => {
|
|
@@ -2360,7 +2362,7 @@ var scrapeCommand = registerCommand({
|
|
|
2360
2362
|
return { url: location.href, title: document.title, navigation, tables, forms: forms.slice(0, 20), links: links.slice(0, 30), mainText };
|
|
2361
2363
|
});
|
|
2362
2364
|
try {
|
|
2363
|
-
persistFromScrape(
|
|
2365
|
+
persistFromScrape(targetUrl, structured);
|
|
2364
2366
|
} catch {
|
|
2365
2367
|
}
|
|
2366
2368
|
if (p.mode === "smart") {
|
|
@@ -2461,7 +2463,7 @@ var scrapeCommand = registerCommand({
|
|
|
2461
2463
|
|
|
2462
2464
|
// src/commands/map.ts
|
|
2463
2465
|
import { z as z16 } from "zod";
|
|
2464
|
-
import { ok as ok16 } from "@dyyz1993/xcli-core";
|
|
2466
|
+
import { ok as ok16, fail as fail4 } from "@dyyz1993/xcli-core";
|
|
2465
2467
|
|
|
2466
2468
|
// src/utils/url.ts
|
|
2467
2469
|
var SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
@@ -2697,7 +2699,7 @@ var mapCommand = registerCommand({
|
|
|
2697
2699
|
description: "Discover all URLs on a website via sitemap and page link extraction",
|
|
2698
2700
|
scope: "project",
|
|
2699
2701
|
parameters: z16.object({
|
|
2700
|
-
url: z16.string(),
|
|
2702
|
+
url: z16.string().optional(),
|
|
2701
2703
|
search: z16.string().optional(),
|
|
2702
2704
|
sitemap: z16.enum(["include", "only"]).optional(),
|
|
2703
2705
|
includeSubdomains: z16.boolean().optional(),
|
|
@@ -2708,7 +2710,11 @@ var mapCommand = registerCommand({
|
|
|
2708
2710
|
handler: async (p, ctx) => {
|
|
2709
2711
|
const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
|
|
2710
2712
|
try {
|
|
2711
|
-
const
|
|
2713
|
+
const targetUrl = p.url || page.url();
|
|
2714
|
+
if (!targetUrl || targetUrl === "about:blank") {
|
|
2715
|
+
return fail4("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
|
|
2716
|
+
}
|
|
2717
|
+
const links = await discoverUrls(page, targetUrl, {
|
|
2712
2718
|
sitemap: p.sitemap,
|
|
2713
2719
|
includeSubdomains: p.includeSubdomains,
|
|
2714
2720
|
allowExternalLinks: p.allowExternalLinks,
|
|
@@ -3551,7 +3557,7 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
|
|
|
3551
3557
|
|
|
3552
3558
|
// src/commands/network.ts
|
|
3553
3559
|
import { z as z19 } from "zod";
|
|
3554
|
-
import { ok as ok19, fail as
|
|
3560
|
+
import { ok as ok19, fail as fail5 } from "@dyyz1993/xcli-core";
|
|
3555
3561
|
function extractPath2(url) {
|
|
3556
3562
|
try {
|
|
3557
3563
|
const u = new URL(url);
|
|
@@ -3716,7 +3722,7 @@ var networkCommand = registerCommand({
|
|
|
3716
3722
|
};
|
|
3717
3723
|
if (p.listen) {
|
|
3718
3724
|
const page2 = ctx.page;
|
|
3719
|
-
if (!page2) return
|
|
3725
|
+
if (!page2) return fail5("No active page. Use --cdp to connect first.");
|
|
3720
3726
|
const captures = [];
|
|
3721
3727
|
const consoleMessages = [];
|
|
3722
3728
|
const wsCaptures = [];
|
|
@@ -4096,7 +4102,7 @@ var ENGINE_KEY_ENUM = z20.enum(ALL_ENGINE_KEYS);
|
|
|
4096
4102
|
|
|
4097
4103
|
// src/commands/snapshot.ts
|
|
4098
4104
|
import { z as z21 } from "zod";
|
|
4099
|
-
import { ok as ok20, fail as
|
|
4105
|
+
import { ok as ok20, fail as fail6, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
|
|
4100
4106
|
|
|
4101
4107
|
// src/runtime/ref-store.ts
|
|
4102
4108
|
var sessions = /* @__PURE__ */ new Map();
|
|
@@ -5067,7 +5073,7 @@ var snapshotCommand = registerCommand({
|
|
|
5067
5073
|
persistSemantics(url, aria);
|
|
5068
5074
|
return ok20({ url, title, aria, text, dom }, normalizeTips3(tips));
|
|
5069
5075
|
}
|
|
5070
|
-
return
|
|
5076
|
+
return fail6(`Unknown snapshot type: ${p.type}`);
|
|
5071
5077
|
}
|
|
5072
5078
|
});
|
|
5073
5079
|
function persistSemantics(url, aria) {
|
|
@@ -5236,7 +5242,7 @@ var waitForCommand = registerCommand({
|
|
|
5236
5242
|
|
|
5237
5243
|
// src/commands/tab.ts
|
|
5238
5244
|
import { z as z23 } from "zod";
|
|
5239
|
-
import { ok as ok22, fail as
|
|
5245
|
+
import { ok as ok22, fail as fail7 } from "@dyyz1993/xcli-core";
|
|
5240
5246
|
var TabParams = z23.object({
|
|
5241
5247
|
subcommand: z23.enum(["list", "new", "close", "switch"]),
|
|
5242
5248
|
url: z23.string().optional(),
|
|
@@ -5253,7 +5259,7 @@ var tabCommand = registerCommand({
|
|
|
5253
5259
|
}),
|
|
5254
5260
|
handler: async (p, ctx) => {
|
|
5255
5261
|
if (!ctx.browserContext) {
|
|
5256
|
-
return
|
|
5262
|
+
return fail7("No browser context available. Use --cdp to connect to a browser first.");
|
|
5257
5263
|
}
|
|
5258
5264
|
const pages = ctx.browserContext.pages();
|
|
5259
5265
|
switch (p.subcommand) {
|
|
@@ -5266,7 +5272,7 @@ var tabCommand = registerCommand({
|
|
|
5266
5272
|
case "switch":
|
|
5267
5273
|
return handleSwitch(p, pages, ctx);
|
|
5268
5274
|
default:
|
|
5269
|
-
return
|
|
5275
|
+
return fail7(`Unknown subcommand: ${p.subcommand}`);
|
|
5270
5276
|
}
|
|
5271
5277
|
}
|
|
5272
5278
|
});
|
|
@@ -5323,11 +5329,11 @@ async function handleNew(p, _pages, ctx) {
|
|
|
5323
5329
|
}
|
|
5324
5330
|
async function handleClose(p, pages, ctx) {
|
|
5325
5331
|
if (pages.length <= 1) {
|
|
5326
|
-
return
|
|
5332
|
+
return fail7("Cannot close the last remaining tab");
|
|
5327
5333
|
}
|
|
5328
5334
|
const closeIndex = p.index ?? pages.indexOf(ctx.page);
|
|
5329
5335
|
if (closeIndex < 0 || closeIndex >= pages.length) {
|
|
5330
|
-
return
|
|
5336
|
+
return fail7(`Invalid tab index: ${closeIndex}. Valid range: 0-${pages.length - 1}`);
|
|
5331
5337
|
}
|
|
5332
5338
|
const pageToClose = pages[closeIndex];
|
|
5333
5339
|
const isActivePage = pageToClose === ctx.page;
|
|
@@ -5350,10 +5356,10 @@ async function handleClose(p, pages, ctx) {
|
|
|
5350
5356
|
}
|
|
5351
5357
|
async function handleSwitch(p, pages, ctx) {
|
|
5352
5358
|
if (p.index === void 0) {
|
|
5353
|
-
return
|
|
5359
|
+
return fail7("Parameter --index is required for switch subcommand");
|
|
5354
5360
|
}
|
|
5355
5361
|
if (p.index < 0 || p.index >= pages.length) {
|
|
5356
|
-
return
|
|
5362
|
+
return fail7(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
|
|
5357
5363
|
}
|
|
5358
5364
|
const targetPage = pages[p.index];
|
|
5359
5365
|
await targetPage.bringToFront().catch(() => {
|
|
@@ -5605,7 +5611,7 @@ registerCommandDefinition("addinitscript", ["script"]);
|
|
|
5605
5611
|
|
|
5606
5612
|
// src/commands/find.ts
|
|
5607
5613
|
import { z as z25 } from "zod";
|
|
5608
|
-
import { ok as ok24, fail as
|
|
5614
|
+
import { ok as ok24, fail as fail8, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
|
|
5609
5615
|
var actionSchema2 = z25.enum(["click", "fill", "type", "select", "hover", "check"]);
|
|
5610
5616
|
var findCommand = registerCommand({
|
|
5611
5617
|
name: "find",
|
|
@@ -5646,7 +5652,7 @@ var findCommand = registerCommand({
|
|
|
5646
5652
|
});
|
|
5647
5653
|
const count = await locator.count();
|
|
5648
5654
|
if (count === 0) {
|
|
5649
|
-
return
|
|
5655
|
+
return fail8(`No element found with ${p.strategy}="${p.value}"`);
|
|
5650
5656
|
}
|
|
5651
5657
|
const tips = [];
|
|
5652
5658
|
const target = selectTarget(locator, p.strategy);
|
|
@@ -5658,15 +5664,15 @@ var findCommand = registerCommand({
|
|
|
5658
5664
|
await target.click({ timeout: p.timeout, force: true });
|
|
5659
5665
|
return okWithTips({ matched: count, selector, action: "click" }, tips);
|
|
5660
5666
|
} else if (actionName === "fill") {
|
|
5661
|
-
if (actionValue === void 0) return
|
|
5667
|
+
if (actionValue === void 0) return fail8("find fill requires a value");
|
|
5662
5668
|
await target.fill(actionValue, { timeout: p.timeout, force: true });
|
|
5663
5669
|
return okWithTips({ matched: count, selector, action: `fill("${actionValue}")` }, tips);
|
|
5664
5670
|
} else if (actionName === "type") {
|
|
5665
|
-
if (actionValue === void 0) return
|
|
5671
|
+
if (actionValue === void 0) return fail8("find type requires a value");
|
|
5666
5672
|
await target.type(actionValue, { delay: 10, timeout: p.timeout });
|
|
5667
5673
|
return okWithTips({ matched: count, selector, action: `type("${actionValue}")` }, tips);
|
|
5668
5674
|
} else if (actionName === "select") {
|
|
5669
|
-
if (actionValue === void 0) return
|
|
5675
|
+
if (actionValue === void 0) return fail8("find select requires a value");
|
|
5670
5676
|
await target.selectOption(actionValue);
|
|
5671
5677
|
return okWithTips({ matched: count, selector, action: `select("${actionValue}")` }, tips);
|
|
5672
5678
|
} else if (actionName === "hover") {
|
|
@@ -6672,7 +6678,7 @@ async function guardCheck(commandName) {
|
|
|
6672
6678
|
}
|
|
6673
6679
|
}
|
|
6674
6680
|
function errorResult(message) {
|
|
6675
|
-
return { ...
|
|
6681
|
+
return { ...fail9(message), duration: 0 };
|
|
6676
6682
|
}
|
|
6677
6683
|
function tipsToMessages(tips) {
|
|
6678
6684
|
if (!tips || tips.length === 0) return [];
|
|
@@ -6749,7 +6755,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6749
6755
|
}
|
|
6750
6756
|
let targetPageOverride = null;
|
|
6751
6757
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
6752
|
-
const { findTargetPage } = await import("./browser-
|
|
6758
|
+
const { findTargetPage } = await import("./browser-6WJHQDPV.js");
|
|
6753
6759
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
6754
6760
|
if (!targetPageOverride) {
|
|
6755
6761
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -6973,7 +6979,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6973
6979
|
duration,
|
|
6974
6980
|
timestamp: start
|
|
6975
6981
|
});
|
|
6976
|
-
return { ...
|
|
6982
|
+
return { ...fail9(errorMessage), duration };
|
|
6977
6983
|
} finally {
|
|
6978
6984
|
}
|
|
6979
6985
|
}
|
|
@@ -7014,7 +7020,7 @@ async function executeChain(input, options) {
|
|
|
7014
7020
|
results.push({
|
|
7015
7021
|
command: cmdName,
|
|
7016
7022
|
raw: cmdStr,
|
|
7017
|
-
...
|
|
7023
|
+
...fail9(`Plugin "${cmdName}" requires a sub-command`),
|
|
7018
7024
|
duration: 0
|
|
7019
7025
|
});
|
|
7020
7026
|
if (type === "and") {
|
|
@@ -7033,7 +7039,7 @@ async function executeChain(input, options) {
|
|
|
7033
7039
|
results.push({
|
|
7034
7040
|
command: cmdName,
|
|
7035
7041
|
raw: cmdStr,
|
|
7036
|
-
...
|
|
7042
|
+
...fail9(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
|
|
7037
7043
|
duration: 0
|
|
7038
7044
|
});
|
|
7039
7045
|
if (type === "and") {
|
|
@@ -7165,7 +7171,7 @@ async function executeChain(input, options) {
|
|
|
7165
7171
|
results.push({
|
|
7166
7172
|
command: `${cmdName} ${subCommand}`,
|
|
7167
7173
|
raw: cmdStr,
|
|
7168
|
-
...
|
|
7174
|
+
...fail9(errorMessage),
|
|
7169
7175
|
duration: duration2
|
|
7170
7176
|
});
|
|
7171
7177
|
if (type === "and") {
|
|
@@ -7787,60 +7793,6 @@ var PlaybackEngine = class _PlaybackEngine {
|
|
|
7787
7793
|
// src/daemon/rpc-handlers.ts
|
|
7788
7794
|
var activeRecorders = /* @__PURE__ */ new Map();
|
|
7789
7795
|
var replayResumeResolvers = /* @__PURE__ */ new Map();
|
|
7790
|
-
var CONFIG_DIR3 = join7(homedir7(), ".xbrowser");
|
|
7791
|
-
var RECORDING_INJECT_JS = `
|
|
7792
|
-
(function(){
|
|
7793
|
-
if(window.__xb_rec) return;
|
|
7794
|
-
window.__xb_rec = true;
|
|
7795
|
-
window.__xb_evts = [];
|
|
7796
|
-
window.__xb_t0 = Date.now();
|
|
7797
|
-
function d(el){
|
|
7798
|
-
if(!el||!el.tagName) return {tag:'unknown'};
|
|
7799
|
-
var o={tag:el.tagName.toLowerCase(),text:(el.textContent||'').trim().substring(0,80)};
|
|
7800
|
-
if(el.getAttribute('role')) o.role=el.getAttribute('role');
|
|
7801
|
-
if(el.id) o.id=el.id;
|
|
7802
|
-
if(el.getAttribute('type')) o.type=el.getAttribute('type');
|
|
7803
|
-
if(el.getAttribute('placeholder')) o.placeholder=el.getAttribute('placeholder');
|
|
7804
|
-
if(el.getAttribute('aria-label')) o.ariaLabel=el.getAttribute('aria-label');
|
|
7805
|
-
if(el.contentEditable==='true') o.contentEditable=true;
|
|
7806
|
-
return o;
|
|
7807
|
-
}
|
|
7808
|
-
function p(t,det){
|
|
7809
|
-
var e={type:t,ts:Date.now()-window.__xb_t0,url:location.href};
|
|
7810
|
-
for(var k in det) e[k]=det[k];
|
|
7811
|
-
window.__xb_evts.push(e);
|
|
7812
|
-
}
|
|
7813
|
-
document.addEventListener('click',function(e){p('click',{target:d(e.target),x:e.clientX,y:e.clientY})},true);
|
|
7814
|
-
document.addEventListener('dblclick',function(e){p('dblclick',{target:d(e.target),x:e.clientX,y:e.clientY})},true);
|
|
7815
|
-
document.addEventListener('input',function(e){var el=e.target;p('input',{target:d(el),value:(el.value||el.textContent||'').substring(0,200)})},true);
|
|
7816
|
-
document.addEventListener('change',function(e){p('change',{target:d(e.target),value:(e.target.value||'').substring(0,100)})},true);
|
|
7817
|
-
document.addEventListener('keydown',function(e){if(e.key==='Enter'||e.key==='Tab'||e.key==='Escape'||e.key.startsWith('Arrow'))p('keydown',{key:e.key,target:d(e.target)})},true);
|
|
7818
|
-
document.addEventListener('submit',function(e){p('submit',{target:d(e.target)})},true);
|
|
7819
|
-
var __xb_last_focus=null;document.addEventListener('focus',function(e){var t=e.target.tagName;if(t==='INPUT'||t==='TEXTAREA'||e.target.contentEditable==='true'){var sel=e.target.id||e.target.name||e.target.placeholder;if(sel===__xb_last_focus)return;__xb_last_focus=sel;p('input_focused',{target:d(e.target)})}},true);
|
|
7820
|
-
var obs=new MutationObserver(function(mutations){
|
|
7821
|
-
for(var m of mutations){
|
|
7822
|
-
for(var node of m.addedNodes){
|
|
7823
|
-
if(node.nodeType===1&&node.tagName){
|
|
7824
|
-
var text=(node.textContent||'').trim().substring(0,60);
|
|
7825
|
-
if(text&&text.length>1) p('dom_added',{tag:node.tagName.toLowerCase(),role:node.getAttribute&&node.getAttribute('role'),text:text});
|
|
7826
|
-
}
|
|
7827
|
-
}
|
|
7828
|
-
}
|
|
7829
|
-
});
|
|
7830
|
-
if(document.body) obs.observe(document.body,{childList:true,subtree:true});
|
|
7831
|
-
p('recording_started',{url:location.href});
|
|
7832
|
-
})();
|
|
7833
|
-
`;
|
|
7834
|
-
async function injectRecording(page) {
|
|
7835
|
-
try {
|
|
7836
|
-
await page.evaluate(RECORDING_INJECT_JS);
|
|
7837
|
-
} catch {
|
|
7838
|
-
}
|
|
7839
|
-
try {
|
|
7840
|
-
await page.addInitScript(RECORDING_INJECT_JS);
|
|
7841
|
-
} catch {
|
|
7842
|
-
}
|
|
7843
|
-
}
|
|
7844
7796
|
function createRPCHandler() {
|
|
7845
7797
|
let previewWS = null;
|
|
7846
7798
|
const INTERACTION_COMMANDS2 = /* @__PURE__ */ new Set([
|
|
@@ -7905,15 +7857,6 @@ function createRPCHandler() {
|
|
|
7905
7857
|
return handleNetworkFeedback(params);
|
|
7906
7858
|
case "network:export":
|
|
7907
7859
|
return handleNetworkExport(params);
|
|
7908
|
-
// ── Recording ──
|
|
7909
|
-
case "recording:status":
|
|
7910
|
-
return handleRecordingStatus(params);
|
|
7911
|
-
case "recording:events":
|
|
7912
|
-
return handleRecordingEvents(params);
|
|
7913
|
-
case "recording:clear":
|
|
7914
|
-
return handleRecordingClear(params);
|
|
7915
|
-
case "recording:save":
|
|
7916
|
-
return handleRecordingSave(params);
|
|
7917
7860
|
// ── Command log ──
|
|
7918
7861
|
case "command:log":
|
|
7919
7862
|
return handleCommandLog(params);
|
|
@@ -7980,7 +7923,6 @@ function createRPCHandler() {
|
|
|
7980
7923
|
session = await createSession(name, url);
|
|
7981
7924
|
}
|
|
7982
7925
|
}
|
|
7983
|
-
await injectRecording(session.page);
|
|
7984
7926
|
if (previewWS) previewWS.registerSession(session.name, session.page);
|
|
7985
7927
|
saveSessionDiskMeta(name, {
|
|
7986
7928
|
id: session.id,
|
|
@@ -8201,61 +8143,6 @@ function createRPCHandler() {
|
|
|
8201
8143
|
if (!entry.capture) return { error: `Entry #${id} not found` };
|
|
8202
8144
|
return exportEntry(entry.capture, lang);
|
|
8203
8145
|
}
|
|
8204
|
-
async function handleRecordingStatus(params) {
|
|
8205
|
-
const sess = findSession(params.session || "default");
|
|
8206
|
-
if (!sess) return { recording: false, error: "No session" };
|
|
8207
|
-
try {
|
|
8208
|
-
const result = await sess.page.evaluate(() => ({
|
|
8209
|
-
active: !!window.__xb_rec,
|
|
8210
|
-
events: window.__xb_evts?.length || 0,
|
|
8211
|
-
url: location.href
|
|
8212
|
-
}));
|
|
8213
|
-
return { recording: true, ...result };
|
|
8214
|
-
} catch {
|
|
8215
|
-
return { recording: false, error: "Page unreachable" };
|
|
8216
|
-
}
|
|
8217
|
-
}
|
|
8218
|
-
async function handleRecordingEvents(params) {
|
|
8219
|
-
const sess = findSession(params.session || "default");
|
|
8220
|
-
if (!sess) return { events: [], error: "No session" };
|
|
8221
|
-
try {
|
|
8222
|
-
const events = await sess.page.evaluate(() => window.__xb_evts || []);
|
|
8223
|
-
return { events, url: sess.page.url() };
|
|
8224
|
-
} catch {
|
|
8225
|
-
return { events: [], error: "Page unreachable" };
|
|
8226
|
-
}
|
|
8227
|
-
}
|
|
8228
|
-
async function handleRecordingClear(params) {
|
|
8229
|
-
const sess = findSession(params.session || "default");
|
|
8230
|
-
if (!sess) return { ok: false, error: "No session" };
|
|
8231
|
-
try {
|
|
8232
|
-
await sess.page.evaluate(() => {
|
|
8233
|
-
window.__xb_evts = [];
|
|
8234
|
-
window.__xb_t0 = Date.now();
|
|
8235
|
-
});
|
|
8236
|
-
return { ok: true };
|
|
8237
|
-
} catch {
|
|
8238
|
-
return { ok: false, error: "Page unreachable" };
|
|
8239
|
-
}
|
|
8240
|
-
}
|
|
8241
|
-
async function handleRecordingSave(params) {
|
|
8242
|
-
const sess = findSession(params.session || "default");
|
|
8243
|
-
if (!sess) return { ok: false, error: "No session" };
|
|
8244
|
-
try {
|
|
8245
|
-
const events = await sess.page.evaluate(() => window.__xb_evts || []);
|
|
8246
|
-
const recordingsDir = join7(CONFIG_DIR3, "recordings");
|
|
8247
|
-
mkdirSync5(recordingsDir, { recursive: true });
|
|
8248
|
-
const outPath = params.path || join7(recordingsDir, `recording-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`);
|
|
8249
|
-
writeFileSync5(outPath, JSON.stringify({
|
|
8250
|
-
startUrl: sess.page.url(),
|
|
8251
|
-
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8252
|
-
events
|
|
8253
|
-
}, null, 2));
|
|
8254
|
-
return { ok: true, path: outPath, events: events.length };
|
|
8255
|
-
} catch (e) {
|
|
8256
|
-
return { ok: false, error: errMsg(e) };
|
|
8257
|
-
}
|
|
8258
|
-
}
|
|
8259
8146
|
function handleCommandLog(params) {
|
|
8260
8147
|
const sessionName = params.session || "default";
|
|
8261
8148
|
const limit = params.limit || 50;
|
|
@@ -8293,7 +8180,7 @@ function createRPCHandler() {
|
|
|
8293
8180
|
} catch {
|
|
8294
8181
|
}
|
|
8295
8182
|
}
|
|
8296
|
-
recorder.recordCommandAction({
|
|
8183
|
+
await recorder.recordCommandAction({
|
|
8297
8184
|
type: actionType,
|
|
8298
8185
|
selector,
|
|
8299
8186
|
value,
|
|
@@ -8316,7 +8203,6 @@ function createRPCHandler() {
|
|
|
8316
8203
|
try {
|
|
8317
8204
|
const sessionOpts = cdpEndpoint ? { cdpEndpoint } : void 0;
|
|
8318
8205
|
session = await createSession(sessionName, url, sessionOpts);
|
|
8319
|
-
await injectRecording(session.page);
|
|
8320
8206
|
if (previewWS) previewWS.registerSession(session.name, session.page);
|
|
8321
8207
|
saveSessionDiskMeta(sessionName, {
|
|
8322
8208
|
id: session.id,
|
|
@@ -8352,8 +8238,8 @@ function createRPCHandler() {
|
|
|
8352
8238
|
const existingData = SessionRecorder.readData(sessionName);
|
|
8353
8239
|
if (existingData) {
|
|
8354
8240
|
if (outputPath) {
|
|
8355
|
-
const { writeFileSync:
|
|
8356
|
-
|
|
8241
|
+
const { writeFileSync: writeFileSync6 } = await import("fs");
|
|
8242
|
+
writeFileSync6(outputPath, JSON.stringify(existingData, null, 2), "utf-8");
|
|
8357
8243
|
}
|
|
8358
8244
|
return {
|
|
8359
8245
|
ok: true,
|
|
@@ -8370,14 +8256,14 @@ function createRPCHandler() {
|
|
|
8370
8256
|
const { data, summary } = await recorder.stop();
|
|
8371
8257
|
activeRecorders.delete(sessionName);
|
|
8372
8258
|
if (outputPath) {
|
|
8373
|
-
const { writeFileSync:
|
|
8259
|
+
const { writeFileSync: writeFileSync6, mkdirSync: mkdirSync6 } = await import("fs");
|
|
8374
8260
|
const { dirname: dirname2 } = await import("path");
|
|
8375
|
-
|
|
8261
|
+
mkdirSync6(dirname2(outputPath), { recursive: true });
|
|
8376
8262
|
if (outputPath.endsWith(".yaml") || outputPath.endsWith(".yml")) {
|
|
8377
8263
|
const yaml2 = (await import("yaml")).default;
|
|
8378
|
-
|
|
8264
|
+
writeFileSync6(outputPath, yaml2.stringify(data), "utf-8");
|
|
8379
8265
|
} else {
|
|
8380
|
-
|
|
8266
|
+
writeFileSync6(outputPath, JSON.stringify(data, null, 2), "utf-8");
|
|
8381
8267
|
}
|
|
8382
8268
|
}
|
|
8383
8269
|
return {
|
|
@@ -9586,12 +9472,12 @@ var FileListHandler = class {
|
|
|
9586
9472
|
const msg = ctx.message;
|
|
9587
9473
|
try {
|
|
9588
9474
|
const { readdirSync, statSync } = await import("fs");
|
|
9589
|
-
const { join:
|
|
9475
|
+
const { join: join8, resolve } = await import("path");
|
|
9590
9476
|
const targetPath = resolve(msg.path);
|
|
9591
9477
|
const entries = readdirSync(targetPath);
|
|
9592
9478
|
const files = entries.map((name) => {
|
|
9593
9479
|
try {
|
|
9594
|
-
const stat = statSync(
|
|
9480
|
+
const stat = statSync(join8(targetPath, name));
|
|
9595
9481
|
return { name, isDir: stat.isDirectory(), size: stat.size, modified: stat.mtime.toISOString() };
|
|
9596
9482
|
} catch {
|
|
9597
9483
|
return { name, isDir: false, size: 0, modified: "" };
|
|
@@ -11252,8 +11138,8 @@ connectWS();
|
|
|
11252
11138
|
}
|
|
11253
11139
|
|
|
11254
11140
|
// src/daemon/daemon-main.ts
|
|
11255
|
-
var
|
|
11256
|
-
var LOG_FILE =
|
|
11141
|
+
var CONFIG_DIR3 = join7(homedir7(), ".xbrowser");
|
|
11142
|
+
var LOG_FILE = join7(CONFIG_DIR3, "daemon.log");
|
|
11257
11143
|
function log(msg) {
|
|
11258
11144
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
|
|
11259
11145
|
const line = `[DAEMON ${ts}] ${msg}
|
|
@@ -11285,7 +11171,7 @@ async function main() {
|
|
|
11285
11171
|
if (err.code === "EADDRINUSE") {
|
|
11286
11172
|
log(`Port ${daemonPort} already in use \u2014 another daemon instance likely won the startup race. Exiting gracefully.`);
|
|
11287
11173
|
try {
|
|
11288
|
-
unlinkSync(
|
|
11174
|
+
unlinkSync(join7(CONFIG_DIR3, "daemon.json"));
|
|
11289
11175
|
} catch {
|
|
11290
11176
|
}
|
|
11291
11177
|
process.exit(0);
|
|
@@ -11319,8 +11205,8 @@ async function main() {
|
|
|
11319
11205
|
rpcHandler.setPreviewWS(previewWS);
|
|
11320
11206
|
previewWS.on("screencast-started", (sid) => log(`Preview screencast started: ${sid}`));
|
|
11321
11207
|
previewWS.on("screencast-stopped", (sid) => log(`Preview screencast stopped: ${sid}`));
|
|
11322
|
-
|
|
11323
|
-
|
|
11208
|
+
mkdirSync5(CONFIG_DIR3, { recursive: true });
|
|
11209
|
+
writeFileSync5(join7(CONFIG_DIR3, "daemon.json"), JSON.stringify({
|
|
11324
11210
|
port: daemonPort,
|
|
11325
11211
|
pid: process.pid,
|
|
11326
11212
|
startedAt: Date.now()
|
package/dist/index.d.ts
CHANGED
|
@@ -1737,6 +1737,8 @@ interface UserAction {
|
|
|
1737
1737
|
distance: number;
|
|
1738
1738
|
duration: number;
|
|
1739
1739
|
};
|
|
1740
|
+
/** Base64-encoded PNG screenshot of the target element (captured on key actions) */
|
|
1741
|
+
elementScreenshot?: string;
|
|
1740
1742
|
}
|
|
1741
1743
|
interface NetworkEntry {
|
|
1742
1744
|
id: number;
|
|
@@ -1854,7 +1856,7 @@ declare class SessionRecorder {
|
|
|
1854
1856
|
value?: string;
|
|
1855
1857
|
url?: string;
|
|
1856
1858
|
element?: UserAction['element'];
|
|
1857
|
-
}): void
|
|
1859
|
+
}): Promise<void>;
|
|
1858
1860
|
get networkCount(): number;
|
|
1859
1861
|
getLiveData(): RecordingData;
|
|
1860
1862
|
addManualCheckpoint(type: string, hint: string, selector?: string): CheckpointEntry;
|
|
@@ -1884,6 +1886,14 @@ declare class SessionRecorder {
|
|
|
1884
1886
|
private handleFileChooser;
|
|
1885
1887
|
private pollActions;
|
|
1886
1888
|
private flushPendingActions;
|
|
1889
|
+
/**
|
|
1890
|
+
* Capture a screenshot of the target element for key action types.
|
|
1891
|
+
* Returns a base64-encoded PNG string, or undefined if the element has no
|
|
1892
|
+
* selector or the screenshot fails (non-critical — never blocks recording).
|
|
1893
|
+
*
|
|
1894
|
+
* Only captures for: click, input, change, dblclick, and their CDP counterparts.
|
|
1895
|
+
*/
|
|
1896
|
+
private captureElementScreenshot;
|
|
1887
1897
|
/**
|
|
1888
1898
|
* After a click, wait 300ms then scan for popover/dropdown/menu elements
|
|
1889
1899
|
* near the click position. This runs server-side to avoid race conditions
|