@xbrowser/cli 0.14.0 → 0.14.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-VUJDJCIN.js → chunk-ITKPSIP7.js} +93 -4
- package/dist/cli.js +512 -305
- package/dist/daemon-main.js +172 -80
- package/dist/{human-interaction-QPHNDD76.js → human-interaction-W753RVJB.js} +1 -2
- package/dist/index.d.ts +7 -68
- package/dist/index.js +407 -271
- package/dist/screenshot-CWAWMXVA.js +28 -0
- package/dist/screenshot-MB6R7RSS.js +26 -0
- package/package.json +1 -1
- package/dist/admin-6UTU2RZ2.js +0 -281
- package/dist/admin-MDGF4CET.js +0 -285
- package/dist/admin-RPJJ5CAF.js +0 -282
- package/dist/chunk-43VX3TYN.js +0 -83
- package/dist/chunk-DESA2KMG.js +0 -77
- package/dist/chunk-FF5WHQHN.js +0 -135
- package/dist/chunk-HINTG75P.js +0 -77
- package/dist/chunk-KTSQU4QT.js +0 -29
- package/dist/chunk-M7CMBPCA.js +0 -100
- package/dist/chunk-NFGO7J2I.js +0 -29
- package/dist/chunk-OLB6UJ25.js +0 -438
- package/dist/chunk-VEKPHQBR.js +0 -47
- package/dist/chunk-YEN2ODUI.js +0 -14
- package/dist/marketplace-FCVN5OTZ.js +0 -706
- package/dist/marketplace-FPT5YLKB.js +0 -351
- package/dist/marketplace-W545W4FR.js +0 -706
package/dist/daemon-main.js
CHANGED
|
@@ -22,21 +22,15 @@ import {
|
|
|
22
22
|
saveSessionDiskMeta,
|
|
23
23
|
setActivePage
|
|
24
24
|
} from "./chunk-F3ZWFCJJ.js";
|
|
25
|
-
import {
|
|
26
|
-
PluginMetadataParser
|
|
27
|
-
} from "./chunk-DESA2KMG.js";
|
|
28
|
-
import {
|
|
29
|
-
readJsonFile
|
|
30
|
-
} from "./chunk-YEN2ODUI.js";
|
|
31
25
|
|
|
32
26
|
// src/daemon/daemon-main.ts
|
|
33
|
-
import { writeFileSync as
|
|
27
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, appendFileSync } from "fs";
|
|
34
28
|
import { join as join6 } from "path";
|
|
35
29
|
import { homedir as homedir6 } from "os";
|
|
36
30
|
import { startHttpServer } from "@dyyz1993/xcli-core";
|
|
37
31
|
|
|
38
32
|
// src/daemon/rpc-handlers.ts
|
|
39
|
-
import { writeFileSync as
|
|
33
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
40
34
|
import { join as join5 } from "path";
|
|
41
35
|
import { homedir as homedir5 } from "os";
|
|
42
36
|
import {
|
|
@@ -368,15 +362,15 @@ var clickCommand = registerCommand({
|
|
|
368
362
|
let detectedNewPage;
|
|
369
363
|
let cleanup;
|
|
370
364
|
if (ctx.browserContext?.on) {
|
|
371
|
-
const pagePromise = new Promise((
|
|
365
|
+
const pagePromise = new Promise((resolve9) => {
|
|
372
366
|
const timer = setTimeout(() => {
|
|
373
367
|
ctx.browserContext.off("page", handler);
|
|
374
|
-
|
|
368
|
+
resolve9(void 0);
|
|
375
369
|
}, 3e3);
|
|
376
370
|
const handler = (page2) => {
|
|
377
371
|
clearTimeout(timer);
|
|
378
372
|
ctx.browserContext.off("page", handler);
|
|
379
|
-
|
|
373
|
+
resolve9(page2);
|
|
380
374
|
};
|
|
381
375
|
ctx.browserContext.on("page", handler);
|
|
382
376
|
});
|
|
@@ -1134,7 +1128,7 @@ var consoleCheckCommand = registerCommand({
|
|
|
1134
1128
|
await page.goto(p.url, { waitUntil: "domcontentloaded" });
|
|
1135
1129
|
}
|
|
1136
1130
|
const messages = await page.evaluate((args) => {
|
|
1137
|
-
return new Promise((
|
|
1131
|
+
return new Promise((resolve9) => {
|
|
1138
1132
|
const collected = [];
|
|
1139
1133
|
const originalConsole = {
|
|
1140
1134
|
log: console.log,
|
|
@@ -1201,7 +1195,7 @@ ${a.stack || ""}`;
|
|
|
1201
1195
|
console.warn = originalConsole.warn;
|
|
1202
1196
|
console.error = originalConsole.error;
|
|
1203
1197
|
console.info = originalConsole.info;
|
|
1204
|
-
|
|
1198
|
+
resolve9(collected);
|
|
1205
1199
|
}, args.duration);
|
|
1206
1200
|
});
|
|
1207
1201
|
}, { duration: p.duration });
|
|
@@ -1613,7 +1607,7 @@ async function executeAction(page, action) {
|
|
|
1613
1607
|
if (action.selector) {
|
|
1614
1608
|
await page.waitForSelector(action.selector, { timeout: 3e4 });
|
|
1615
1609
|
} else if (action.milliseconds) {
|
|
1616
|
-
await new Promise((
|
|
1610
|
+
await new Promise((resolve9) => setTimeout(resolve9, action.milliseconds));
|
|
1617
1611
|
} else {
|
|
1618
1612
|
throw new Error("wait action requires either milliseconds or selector");
|
|
1619
1613
|
}
|
|
@@ -1696,8 +1690,8 @@ var actionsCommand = registerCommand({
|
|
|
1696
1690
|
results.push(result);
|
|
1697
1691
|
}
|
|
1698
1692
|
})();
|
|
1699
|
-
const timeoutPromise = new Promise((
|
|
1700
|
-
setTimeout(
|
|
1693
|
+
const timeoutPromise = new Promise((resolve9) => {
|
|
1694
|
+
setTimeout(resolve9, timeoutMs);
|
|
1701
1695
|
});
|
|
1702
1696
|
await Promise.race([executionPromise, timeoutPromise]);
|
|
1703
1697
|
const title = await ctx.page.title();
|
|
@@ -2371,11 +2365,11 @@ async function navigateForMap(page, url, timeout = 15e3) {
|
|
|
2371
2365
|
}
|
|
2372
2366
|
async function extractPageLinks(page, baseUrl) {
|
|
2373
2367
|
await navigateForMap(page, baseUrl);
|
|
2374
|
-
await new Promise((
|
|
2368
|
+
await new Promise((resolve9) => setTimeout(resolve9, 2e3));
|
|
2375
2369
|
await page.evaluate(() => {
|
|
2376
2370
|
window.scrollTo(0, document.body.scrollHeight);
|
|
2377
2371
|
});
|
|
2378
|
-
await new Promise((
|
|
2372
|
+
await new Promise((resolve9) => setTimeout(resolve9, 1e3));
|
|
2379
2373
|
const origin = new URL(baseUrl).origin;
|
|
2380
2374
|
const rawLinks = await page.evaluate((evalOrigin) => {
|
|
2381
2375
|
return Array.from(document.querySelectorAll("a[href]")).map((a) => {
|
|
@@ -4205,25 +4199,25 @@ aria snapshot\uFF1A
|
|
|
4205
4199
|
async function analyzeWithLLM(ariaSnapshot) {
|
|
4206
4200
|
const piBin = process.env.PI_CLI_PATH || "pi";
|
|
4207
4201
|
const prompt = LLM_PROMPT.replace("{snapshot}", ariaSnapshot.slice(0, 4e3));
|
|
4208
|
-
return new Promise((
|
|
4202
|
+
return new Promise((resolve9) => {
|
|
4209
4203
|
execFile(
|
|
4210
4204
|
piBin,
|
|
4211
4205
|
["--provider", LLM_PROVIDER, "--model", LLM_MODEL, prompt],
|
|
4212
4206
|
{ timeout: LLM_TIMEOUT_MS, maxBuffer: 1024 * 1024 },
|
|
4213
4207
|
(err, stdout, _stderr) => {
|
|
4214
4208
|
if (err) {
|
|
4215
|
-
|
|
4209
|
+
resolve9(null);
|
|
4216
4210
|
return;
|
|
4217
4211
|
}
|
|
4218
4212
|
const output = (stdout || "").trim();
|
|
4219
4213
|
if (!output) {
|
|
4220
|
-
|
|
4214
|
+
resolve9(null);
|
|
4221
4215
|
return;
|
|
4222
4216
|
}
|
|
4223
4217
|
try {
|
|
4224
4218
|
const parsed = parse(output);
|
|
4225
4219
|
if (!parsed || typeof parsed !== "object") {
|
|
4226
|
-
|
|
4220
|
+
resolve9(null);
|
|
4227
4221
|
return;
|
|
4228
4222
|
}
|
|
4229
4223
|
const elements = {};
|
|
@@ -4238,9 +4232,9 @@ async function analyzeWithLLM(ariaSnapshot) {
|
|
|
4238
4232
|
};
|
|
4239
4233
|
}
|
|
4240
4234
|
}
|
|
4241
|
-
|
|
4235
|
+
resolve9(Object.keys(elements).length > 0 ? elements : null);
|
|
4242
4236
|
} catch {
|
|
4243
|
-
|
|
4237
|
+
resolve9(null);
|
|
4244
4238
|
}
|
|
4245
4239
|
}
|
|
4246
4240
|
);
|
|
@@ -4793,11 +4787,11 @@ function resolveScriptContent(params) {
|
|
|
4793
4787
|
async function readStdin() {
|
|
4794
4788
|
const { createReadStream } = await import("fs");
|
|
4795
4789
|
const { createInterface } = await import("readline");
|
|
4796
|
-
return new Promise((
|
|
4790
|
+
return new Promise((resolve9, reject) => {
|
|
4797
4791
|
const lines = [];
|
|
4798
4792
|
const rl = createInterface({ input: createReadStream("/dev/stdin") });
|
|
4799
4793
|
rl.on("line", (line) => lines.push(line));
|
|
4800
|
-
rl.on("close", () =>
|
|
4794
|
+
rl.on("close", () => resolve9(lines.join("\n")));
|
|
4801
4795
|
rl.on("error", reject);
|
|
4802
4796
|
});
|
|
4803
4797
|
}
|
|
@@ -5535,12 +5529,95 @@ var promoCommand = registerCommand({
|
|
|
5535
5529
|
import {
|
|
5536
5530
|
Core
|
|
5537
5531
|
} from "@dyyz1993/xcli-core";
|
|
5538
|
-
import { resolve as
|
|
5539
|
-
import { existsSync as
|
|
5532
|
+
import { resolve as resolve8 } from "path";
|
|
5533
|
+
import { existsSync as existsSync5, readdirSync } from "fs";
|
|
5540
5534
|
import { homedir as homedir2 } from "os";
|
|
5541
5535
|
|
|
5536
|
+
// src/plugin/metadata-parser.ts
|
|
5537
|
+
import { existsSync as existsSync3 } from "fs";
|
|
5538
|
+
import { resolve as resolve7 } from "path";
|
|
5539
|
+
|
|
5540
|
+
// src/utils/json-file.ts
|
|
5541
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
|
|
5542
|
+
function readJsonFile(filePath, defaultValue) {
|
|
5543
|
+
try {
|
|
5544
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
5545
|
+
return JSON.parse(content);
|
|
5546
|
+
} catch {
|
|
5547
|
+
return defaultValue;
|
|
5548
|
+
}
|
|
5549
|
+
}
|
|
5550
|
+
|
|
5551
|
+
// src/plugin/metadata-parser.ts
|
|
5552
|
+
var PluginMetadataParser = class {
|
|
5553
|
+
static XBROWSER_KEYWORDS = ["xbrowser", "xbrowser-plugin"];
|
|
5554
|
+
static parseFromPackageJson(pluginPath) {
|
|
5555
|
+
const packageJsonPath = resolve7(pluginPath, "package.json");
|
|
5556
|
+
if (!existsSync3(packageJsonPath)) {
|
|
5557
|
+
return null;
|
|
5558
|
+
}
|
|
5559
|
+
const packageJson = readJsonFile(packageJsonPath, null);
|
|
5560
|
+
if (!packageJson) return null;
|
|
5561
|
+
if (!packageJson.xbrowser) {
|
|
5562
|
+
return null;
|
|
5563
|
+
}
|
|
5564
|
+
const xbrowser = packageJson.xbrowser;
|
|
5565
|
+
const metadata = {
|
|
5566
|
+
id: xbrowser.id || packageJson.name,
|
|
5567
|
+
name: xbrowser.name || packageJson.name,
|
|
5568
|
+
description: xbrowser.description || packageJson.description || "",
|
|
5569
|
+
version: xbrowser.version || packageJson.version || "1.0.0",
|
|
5570
|
+
author: xbrowser.author || this.extractAuthor(packageJson.author),
|
|
5571
|
+
homepage: xbrowser.homepage || packageJson.homepage,
|
|
5572
|
+
commands: xbrowser.commands,
|
|
5573
|
+
sites: xbrowser.sites,
|
|
5574
|
+
tags: xbrowser.tags,
|
|
5575
|
+
screenshot: xbrowser.screenshot,
|
|
5576
|
+
license: xbrowser.license || packageJson.license
|
|
5577
|
+
};
|
|
5578
|
+
return metadata;
|
|
5579
|
+
}
|
|
5580
|
+
static isXBrowserPlugin(packageJson) {
|
|
5581
|
+
if (packageJson.xbrowser) {
|
|
5582
|
+
return true;
|
|
5583
|
+
}
|
|
5584
|
+
const keywords = packageJson.keywords;
|
|
5585
|
+
if (!keywords) return false;
|
|
5586
|
+
return this.XBROWSER_KEYWORDS.some((kw) => keywords.includes(kw));
|
|
5587
|
+
}
|
|
5588
|
+
static fromNPMResult(result) {
|
|
5589
|
+
const author = typeof result.author === "string" ? result.author : result.author?.name || "Unknown";
|
|
5590
|
+
return {
|
|
5591
|
+
id: result.name,
|
|
5592
|
+
name: result.name.replace(/^xbrowser-plugin-/, "").replace(/^@[^/]+\//, ""),
|
|
5593
|
+
description: result.description || "",
|
|
5594
|
+
version: result.version,
|
|
5595
|
+
author,
|
|
5596
|
+
homepage: result.homepage || result.links?.homepage,
|
|
5597
|
+
tags: result.keywords,
|
|
5598
|
+
license: ""
|
|
5599
|
+
};
|
|
5600
|
+
}
|
|
5601
|
+
static extractAuthor(author) {
|
|
5602
|
+
if (typeof author === "string") return author;
|
|
5603
|
+
if (typeof author === "object" && author !== null) {
|
|
5604
|
+
const authorObj = author;
|
|
5605
|
+
return authorObj.name || "Unknown";
|
|
5606
|
+
}
|
|
5607
|
+
return "Unknown";
|
|
5608
|
+
}
|
|
5609
|
+
static validateMetadata(metadata) {
|
|
5610
|
+
const errors = [];
|
|
5611
|
+
if (!metadata.id) errors.push("id is required");
|
|
5612
|
+
if (!metadata.name) errors.push("name is required");
|
|
5613
|
+
if (!metadata.description) errors.push("description is required");
|
|
5614
|
+
if (!metadata.version) errors.push("version is required");
|
|
5615
|
+
return errors;
|
|
5616
|
+
}
|
|
5617
|
+
};
|
|
5618
|
+
|
|
5542
5619
|
// src/plugin/ensure-deps.ts
|
|
5543
|
-
import { existsSync as
|
|
5620
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
5544
5621
|
import { join as join2 } from "path";
|
|
5545
5622
|
import { execSync as execSync6 } from "child_process";
|
|
5546
5623
|
var SHARED_PLUGIN_DEPENDENCIES = {
|
|
@@ -5549,11 +5626,11 @@ var SHARED_PLUGIN_DEPENDENCIES = {
|
|
|
5549
5626
|
};
|
|
5550
5627
|
function ensurePluginDependencies(pluginsDir) {
|
|
5551
5628
|
const zodPath = join2(pluginsDir, "node_modules", "zod");
|
|
5552
|
-
if (
|
|
5629
|
+
if (existsSync4(zodPath)) return;
|
|
5553
5630
|
mkdirSync2(pluginsDir, { recursive: true });
|
|
5554
5631
|
const pkgPath = join2(pluginsDir, "package.json");
|
|
5555
5632
|
let pkg = {};
|
|
5556
|
-
if (
|
|
5633
|
+
if (existsSync4(pkgPath)) {
|
|
5557
5634
|
try {
|
|
5558
5635
|
pkg = readJsonFile(pkgPath, {});
|
|
5559
5636
|
} catch {
|
|
@@ -5567,11 +5644,11 @@ function ensurePluginDependencies(pluginsDir) {
|
|
|
5567
5644
|
needsInstall = true;
|
|
5568
5645
|
}
|
|
5569
5646
|
}
|
|
5570
|
-
if (!needsInstall &&
|
|
5647
|
+
if (!needsInstall && existsSync4(join2(pluginsDir, "node_modules"))) return;
|
|
5571
5648
|
pkg.dependencies = existingDeps;
|
|
5572
5649
|
pkg.private = true;
|
|
5573
5650
|
pkg.description = pkg.description || "xbrowser plugins \u2014 shared dependencies";
|
|
5574
|
-
|
|
5651
|
+
writeFileSync4(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
5575
5652
|
try {
|
|
5576
5653
|
execSync6("npm install --production --no-package-lock --no-fund --no-audit", {
|
|
5577
5654
|
cwd: pluginsDir,
|
|
@@ -5601,7 +5678,7 @@ var XBrowserPluginLoader = class {
|
|
|
5601
5678
|
envPrefix: "XBROWSER",
|
|
5602
5679
|
pluginDirs: [
|
|
5603
5680
|
...DEFAULT_PLUGIN_DIRS,
|
|
5604
|
-
|
|
5681
|
+
resolve8(cwd, ".xcli/plugins")
|
|
5605
5682
|
]
|
|
5606
5683
|
};
|
|
5607
5684
|
this.core = new Core(coreConfig);
|
|
@@ -5640,31 +5717,31 @@ var XBrowserPluginLoader = class {
|
|
|
5640
5717
|
}
|
|
5641
5718
|
async scanAndLoad() {
|
|
5642
5719
|
const cwd = this.options.cwd || process.cwd();
|
|
5643
|
-
const globalDir = this.options.globalDir ||
|
|
5720
|
+
const globalDir = this.options.globalDir || resolve8(homedir2(), ".xbrowser/plugins");
|
|
5644
5721
|
ensurePluginDependencies(globalDir);
|
|
5645
5722
|
const dirs = [
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
this.options.userDir ||
|
|
5723
|
+
resolve8(cwd, ".xcli/plugins"),
|
|
5724
|
+
resolve8(cwd, "../.xcli/plugins"),
|
|
5725
|
+
this.options.userDir || resolve8(homedir2(), ".xcli/plugins"),
|
|
5649
5726
|
globalDir
|
|
5650
5727
|
];
|
|
5651
5728
|
const loaded = [];
|
|
5652
5729
|
const seen = /* @__PURE__ */ new Set();
|
|
5653
5730
|
for (const dir of dirs) {
|
|
5654
|
-
if (!
|
|
5731
|
+
if (!existsSync5(dir)) continue;
|
|
5655
5732
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
5656
5733
|
for (const entry of entries) {
|
|
5657
5734
|
if (!entry.isDirectory()) continue;
|
|
5658
5735
|
if (seen.has(entry.name)) continue;
|
|
5659
5736
|
seen.add(entry.name);
|
|
5660
|
-
const pluginDir =
|
|
5661
|
-
let indexPath =
|
|
5662
|
-
if (!
|
|
5663
|
-
indexPath =
|
|
5737
|
+
const pluginDir = resolve8(dir, entry.name);
|
|
5738
|
+
let indexPath = resolve8(pluginDir, "index.js");
|
|
5739
|
+
if (!existsSync5(indexPath)) {
|
|
5740
|
+
indexPath = resolve8(pluginDir, "index.ts");
|
|
5664
5741
|
}
|
|
5665
|
-
if (!
|
|
5742
|
+
if (!existsSync5(indexPath)) continue;
|
|
5666
5743
|
try {
|
|
5667
|
-
if (!
|
|
5744
|
+
if (!existsSync5(resolve8(pluginDir, "package.json"))) {
|
|
5668
5745
|
console.warn(`\u26A0\uFE0F Plugin "${entry.name}" has no package.json. Use "xbrowser create ${entry.name} --template static" for proper structure.`);
|
|
5669
5746
|
} else {
|
|
5670
5747
|
const metadata = PluginMetadataParser.parseFromPackageJson(pluginDir);
|
|
@@ -5681,22 +5758,6 @@ var XBrowserPluginLoader = class {
|
|
|
5681
5758
|
}
|
|
5682
5759
|
}
|
|
5683
5760
|
}
|
|
5684
|
-
try {
|
|
5685
|
-
const { default: setupMarketplace } = await import("./marketplace-FCVN5OTZ.js");
|
|
5686
|
-
setupMarketplace(this.loader.getAPI());
|
|
5687
|
-
} catch (err) {
|
|
5688
|
-
if (process.env.XBROWSER_DEBUG) {
|
|
5689
|
-
console.warn(`\u26A0\uFE0F Built-in marketplace plugin load failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5690
|
-
}
|
|
5691
|
-
}
|
|
5692
|
-
try {
|
|
5693
|
-
const { default: setupAdmin } = await import("./admin-6UTU2RZ2.js");
|
|
5694
|
-
setupAdmin(this.loader.getAPI());
|
|
5695
|
-
} catch (err) {
|
|
5696
|
-
if (process.env.XBROWSER_DEBUG) {
|
|
5697
|
-
console.warn(`\u26A0\uFE0F Built-in admin plugin load failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5698
|
-
}
|
|
5699
|
-
}
|
|
5700
5761
|
return loaded;
|
|
5701
5762
|
}
|
|
5702
5763
|
async unload() {
|
|
@@ -6097,7 +6158,7 @@ var TipsManager = class {
|
|
|
6097
6158
|
}
|
|
6098
6159
|
}
|
|
6099
6160
|
debounce() {
|
|
6100
|
-
return new Promise((
|
|
6161
|
+
return new Promise((resolve9) => setTimeout(resolve9, DEBOUNCE_MS));
|
|
6101
6162
|
}
|
|
6102
6163
|
formatTips(tips) {
|
|
6103
6164
|
return tips.map((tip) => {
|
|
@@ -6124,6 +6185,24 @@ function getTipsManager() {
|
|
|
6124
6185
|
return globalTipsManager;
|
|
6125
6186
|
}
|
|
6126
6187
|
|
|
6188
|
+
// src/hooks/loader.ts
|
|
6189
|
+
var builtinHooks = {
|
|
6190
|
+
screenshot: () => import("./screenshot-MB6R7RSS.js").then((m) => m.screenshotHook)
|
|
6191
|
+
};
|
|
6192
|
+
async function loadHooks() {
|
|
6193
|
+
const names = process.env.XBROWSER_HOOKS;
|
|
6194
|
+
if (!names) return [];
|
|
6195
|
+
const hooks = [];
|
|
6196
|
+
for (const name of names.split(",")) {
|
|
6197
|
+
const trimmed = name.trim();
|
|
6198
|
+
const factory = builtinHooks[trimmed];
|
|
6199
|
+
if (factory) {
|
|
6200
|
+
hooks.push(await factory());
|
|
6201
|
+
}
|
|
6202
|
+
}
|
|
6203
|
+
return hooks;
|
|
6204
|
+
}
|
|
6205
|
+
|
|
6127
6206
|
// src/executor.ts
|
|
6128
6207
|
import { homedir as homedir3 } from "os";
|
|
6129
6208
|
import { join as join3 } from "path";
|
|
@@ -6283,9 +6362,22 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6283
6362
|
}
|
|
6284
6363
|
}
|
|
6285
6364
|
try {
|
|
6365
|
+
const hooks = await loadHooks();
|
|
6366
|
+
if (hooks.length > 0 && session?.page) {
|
|
6367
|
+
await Promise.all(hooks.map((h) => h.onBeforeCommand?.({ page: session.page, command: commandName, params })));
|
|
6368
|
+
}
|
|
6286
6369
|
const raw = await command.handler(params, ctx);
|
|
6287
6370
|
const end = Date.now();
|
|
6288
6371
|
const duration = end - start;
|
|
6372
|
+
let hookOutputs;
|
|
6373
|
+
if (hooks.length > 0 && session?.page) {
|
|
6374
|
+
const outputs = [];
|
|
6375
|
+
for (const h of hooks) {
|
|
6376
|
+
const output = await h.onAfterCommand?.({ page: session.page, command: commandName, params, result: raw, duration });
|
|
6377
|
+
if (output) outputs.push({ _hook: h.name, ...output });
|
|
6378
|
+
}
|
|
6379
|
+
if (outputs.length > 0) hookOutputs = outputs;
|
|
6380
|
+
}
|
|
6289
6381
|
if (session && isCommandResult(raw)) {
|
|
6290
6382
|
const resultData = raw.data;
|
|
6291
6383
|
const convUrl = resultData?.conversationUrl;
|
|
@@ -6333,9 +6425,9 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6333
6425
|
timestamp: start
|
|
6334
6426
|
});
|
|
6335
6427
|
if (isSuccess) {
|
|
6336
|
-
return { ...ok25(raw.data, merged.length > 0 ? merged : raw.tips), duration };
|
|
6428
|
+
return { ...ok25(raw.data, merged.length > 0 ? merged : raw.tips), duration, ...hookOutputs ? { hookOutputs } : {} };
|
|
6337
6429
|
}
|
|
6338
|
-
return { success: false, data: raw.data, message: raw.message, tips: merged.length > 0 ? merged : raw.tips || [], duration };
|
|
6430
|
+
return { success: false, data: raw.data, message: raw.message, tips: merged.length > 0 ? merged : raw.tips || [], duration, ...hookOutputs ? { hookOutputs } : {} };
|
|
6339
6431
|
}
|
|
6340
6432
|
recordArchive(session?.id, sessionName, {
|
|
6341
6433
|
step: 0,
|
|
@@ -6346,7 +6438,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6346
6438
|
duration,
|
|
6347
6439
|
timestamp: start
|
|
6348
6440
|
});
|
|
6349
|
-
return { ...ok25(raw, smartTips), duration };
|
|
6441
|
+
return { ...ok25(raw, smartTips), duration, ...hookOutputs ? { hookOutputs } : {} };
|
|
6350
6442
|
} catch (err) {
|
|
6351
6443
|
const end = Date.now();
|
|
6352
6444
|
const duration = end - start;
|
|
@@ -6820,7 +6912,7 @@ async function replayEntry(entry, options = {}) {
|
|
|
6820
6912
|
}
|
|
6821
6913
|
|
|
6822
6914
|
// src/daemon/feedback-store.ts
|
|
6823
|
-
import { readFileSync as
|
|
6915
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
6824
6916
|
import { join as join4 } from "path";
|
|
6825
6917
|
import { homedir as homedir4 } from "os";
|
|
6826
6918
|
var FEEDBACK_FILE = join4(homedir4(), ".xbrowser", "feedback.json");
|
|
@@ -6831,7 +6923,7 @@ var FeedbackStore = class {
|
|
|
6831
6923
|
}
|
|
6832
6924
|
load() {
|
|
6833
6925
|
try {
|
|
6834
|
-
const data =
|
|
6926
|
+
const data = readFileSync10(FEEDBACK_FILE, "utf8");
|
|
6835
6927
|
this.entries = JSON.parse(data);
|
|
6836
6928
|
} catch {
|
|
6837
6929
|
this.entries = [];
|
|
@@ -6840,7 +6932,7 @@ var FeedbackStore = class {
|
|
|
6840
6932
|
save() {
|
|
6841
6933
|
try {
|
|
6842
6934
|
mkdirSync3(join4(homedir4(), ".xbrowser"), { recursive: true });
|
|
6843
|
-
|
|
6935
|
+
writeFileSync5(FEEDBACK_FILE, JSON.stringify(this.entries, null, 2));
|
|
6844
6936
|
} catch {
|
|
6845
6937
|
}
|
|
6846
6938
|
}
|
|
@@ -7513,7 +7605,7 @@ function createRPCHandler() {
|
|
|
7513
7605
|
const recordingsDir = join5(CONFIG_DIR, "recordings");
|
|
7514
7606
|
mkdirSync4(recordingsDir, { recursive: true });
|
|
7515
7607
|
const outPath = params.path || join5(recordingsDir, `recording-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`);
|
|
7516
|
-
|
|
7608
|
+
writeFileSync6(outPath, JSON.stringify({
|
|
7517
7609
|
startUrl: sess.page.url(),
|
|
7518
7610
|
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7519
7611
|
events
|
|
@@ -7683,8 +7775,8 @@ function createRPCHandler() {
|
|
|
7683
7775
|
const result = await engine.play({
|
|
7684
7776
|
slowMo,
|
|
7685
7777
|
onCheckpoint: async (checkpoint) => {
|
|
7686
|
-
return new Promise((
|
|
7687
|
-
replayResumeResolvers.set(sessionName, () =>
|
|
7778
|
+
return new Promise((resolve9) => {
|
|
7779
|
+
replayResumeResolvers.set(sessionName, () => resolve9(true));
|
|
7688
7780
|
console.log(`[replay] Checkpoint reached: [${checkpoint.type}] ${checkpoint.hint}`);
|
|
7689
7781
|
console.log('[replay] Send "replay:resume" RPC to continue.');
|
|
7690
7782
|
});
|
|
@@ -8547,8 +8639,8 @@ var WSServer = class extends EventEmitter {
|
|
|
8547
8639
|
case "file_list": {
|
|
8548
8640
|
try {
|
|
8549
8641
|
const { readdirSync: readdirSync2, statSync } = await import("fs");
|
|
8550
|
-
const { join: join7, resolve:
|
|
8551
|
-
const targetPath =
|
|
8642
|
+
const { join: join7, resolve: resolve9 } = await import("path");
|
|
8643
|
+
const targetPath = resolve9(msg.path);
|
|
8552
8644
|
const entries = readdirSync2(targetPath);
|
|
8553
8645
|
const files = entries.map((name) => {
|
|
8554
8646
|
try {
|
|
@@ -8566,10 +8658,10 @@ var WSServer = class extends EventEmitter {
|
|
|
8566
8658
|
}
|
|
8567
8659
|
case "file_download": {
|
|
8568
8660
|
try {
|
|
8569
|
-
const { readFileSync:
|
|
8570
|
-
const { resolve:
|
|
8571
|
-
const targetPath =
|
|
8572
|
-
const data =
|
|
8661
|
+
const { readFileSync: readFileSync12 } = await import("fs");
|
|
8662
|
+
const { resolve: resolve9, basename } = await import("path");
|
|
8663
|
+
const targetPath = resolve9(msg.path);
|
|
8664
|
+
const data = readFileSync12(targetPath);
|
|
8573
8665
|
const base64 = data.toString("base64");
|
|
8574
8666
|
const ext = targetPath.split(".").pop()?.toLowerCase() || "";
|
|
8575
8667
|
const mimeMap = {
|
|
@@ -8674,7 +8766,7 @@ var WSServer = class extends EventEmitter {
|
|
|
8674
8766
|
this.isRunning = false;
|
|
8675
8767
|
return;
|
|
8676
8768
|
}
|
|
8677
|
-
return new Promise((
|
|
8769
|
+
return new Promise((resolve9, reject) => {
|
|
8678
8770
|
this.wsServer.close((err) => {
|
|
8679
8771
|
if (err) {
|
|
8680
8772
|
reject(err);
|
|
@@ -8682,7 +8774,7 @@ var WSServer = class extends EventEmitter {
|
|
|
8682
8774
|
this.wsServer = null;
|
|
8683
8775
|
this.isRunning = false;
|
|
8684
8776
|
this.emit("stopped");
|
|
8685
|
-
|
|
8777
|
+
resolve9();
|
|
8686
8778
|
}
|
|
8687
8779
|
});
|
|
8688
8780
|
});
|
|
@@ -9869,7 +9961,7 @@ async function main() {
|
|
|
9869
9961
|
previewWS.on("screencast-started", (sid) => log(`Preview screencast started: ${sid}`));
|
|
9870
9962
|
previewWS.on("screencast-stopped", (sid) => log(`Preview screencast stopped: ${sid}`));
|
|
9871
9963
|
mkdirSync5(CONFIG_DIR2, { recursive: true });
|
|
9872
|
-
|
|
9964
|
+
writeFileSync7(join6(CONFIG_DIR2, "daemon.json"), JSON.stringify({
|
|
9873
9965
|
port: daemonPort,
|
|
9874
9966
|
pid: process.pid,
|
|
9875
9967
|
startedAt: Date.now()
|
package/dist/index.d.ts
CHANGED
|
@@ -260,12 +260,17 @@ declare class WSServer extends EventEmitter {
|
|
|
260
260
|
/**
|
|
261
261
|
* Result of a single command execution.
|
|
262
262
|
*/
|
|
263
|
+
interface HookOutput {
|
|
264
|
+
_hook: string;
|
|
265
|
+
[key: string]: unknown;
|
|
266
|
+
}
|
|
263
267
|
interface ExecutionResult {
|
|
264
268
|
success: boolean;
|
|
265
269
|
data: unknown;
|
|
266
270
|
message?: string;
|
|
267
271
|
duration: number;
|
|
268
272
|
tips?: string[];
|
|
273
|
+
hookOutputs?: HookOutput[];
|
|
269
274
|
}
|
|
270
275
|
/**
|
|
271
276
|
* Result of a single step within a command chain execution.
|
|
@@ -278,6 +283,7 @@ interface ChainStepResult {
|
|
|
278
283
|
message?: string;
|
|
279
284
|
duration: number;
|
|
280
285
|
tips?: string[];
|
|
286
|
+
hookOutputs?: HookOutput[];
|
|
281
287
|
}
|
|
282
288
|
/**
|
|
283
289
|
* Result of executing a command chain (multiple commands linked with &&, ||, etc.).
|
|
@@ -1055,73 +1061,6 @@ declare class PluginInstaller {
|
|
|
1055
1061
|
private deriveName;
|
|
1056
1062
|
}
|
|
1057
1063
|
|
|
1058
|
-
/**
|
|
1059
|
-
* Authentication configuration for publishing to a registry.
|
|
1060
|
-
*/
|
|
1061
|
-
interface AuthConfig {
|
|
1062
|
-
token: string;
|
|
1063
|
-
registry: string;
|
|
1064
|
-
}
|
|
1065
|
-
type StorageType = 'npm' | 'r2';
|
|
1066
|
-
/**
|
|
1067
|
-
* Options for publishing a plugin.
|
|
1068
|
-
*/
|
|
1069
|
-
interface PublishOptions {
|
|
1070
|
-
registry: string;
|
|
1071
|
-
token: string;
|
|
1072
|
-
dryRun?: boolean;
|
|
1073
|
-
storage?: StorageType;
|
|
1074
|
-
}
|
|
1075
|
-
interface CommandDocParam {
|
|
1076
|
-
name: string;
|
|
1077
|
-
type: string;
|
|
1078
|
-
description: string;
|
|
1079
|
-
required: boolean;
|
|
1080
|
-
default?: unknown;
|
|
1081
|
-
}
|
|
1082
|
-
interface CommandDocExample {
|
|
1083
|
-
cmd: string;
|
|
1084
|
-
description: string;
|
|
1085
|
-
}
|
|
1086
|
-
interface CommandDoc {
|
|
1087
|
-
name: string;
|
|
1088
|
-
description: string;
|
|
1089
|
-
parameters: CommandDocParam[];
|
|
1090
|
-
examples?: CommandDocExample[];
|
|
1091
|
-
}
|
|
1092
|
-
/**
|
|
1093
|
-
* Result of creating a plugin tarball for publishing.
|
|
1094
|
-
*/
|
|
1095
|
-
interface PublishResult {
|
|
1096
|
-
name: string;
|
|
1097
|
-
version: string;
|
|
1098
|
-
slug: string;
|
|
1099
|
-
description: string;
|
|
1100
|
-
author: string;
|
|
1101
|
-
commands: string[];
|
|
1102
|
-
commandsDocs: CommandDoc[];
|
|
1103
|
-
readme: string | null;
|
|
1104
|
-
tags: string[];
|
|
1105
|
-
sites: string[];
|
|
1106
|
-
fileCount: number;
|
|
1107
|
-
size: number;
|
|
1108
|
-
checksum: string;
|
|
1109
|
-
formData: FormData;
|
|
1110
|
-
}
|
|
1111
|
-
/**
|
|
1112
|
-
* Create a publishable tarball from a plugin directory.
|
|
1113
|
-
*
|
|
1114
|
-
* Collects plugin files, extracts metadata from package.json, detects
|
|
1115
|
-
* registered commands from source code, and prepares a FormData payload
|
|
1116
|
-
* for uploading to the marketplace or an npm-based registry.
|
|
1117
|
-
*
|
|
1118
|
-
* @param pluginDir - Path to the plugin directory containing `index.ts`.
|
|
1119
|
-
* @param options - Publish options including registry, token, storage type, and dry-run flag.
|
|
1120
|
-
* @returns A PublishResult with metadata and the prepared FormData.
|
|
1121
|
-
* @throws If `index.ts` is missing or the plugin has no description.
|
|
1122
|
-
*/
|
|
1123
|
-
declare function createTarball(pluginDir: string, options: PublishOptions): Promise<PublishResult>;
|
|
1124
|
-
|
|
1125
1064
|
/**
|
|
1126
1065
|
* A single recorded browser event (click, type, scroll, navigate, etc.).
|
|
1127
1066
|
*/
|
|
@@ -2310,4 +2249,4 @@ declare function advise(decision: DecisionResult, originalMethod: string): Advis
|
|
|
2310
2249
|
/** Convenience factory: create and start a CDP interceptor proxy with defaults. */
|
|
2311
2250
|
declare function createCDPInterceptor(config: CDPInterceptorConfig): Promise<CDPInterceptorProxy>;
|
|
2312
2251
|
|
|
2313
|
-
export { type APIRequest, type APIResponse, type AdvisoryResult, type AnalysisResult,
|
|
2252
|
+
export { type APIRequest, type APIResponse, type AdvisoryResult, type AnalysisResult, BROWSER_SCOPE, type BatchCollectResult, type BrowserCommandContext, type BrowserCommandDefinition, type BrowserLaunchOptions, type BuiltinCommand, type BuiltinContext, type CDPError, type CDPInterceptorConfig, CDPInterceptorProxy, type CDPInterceptorRule, type CDPInterceptorStats, type CDPLogEntry, type CDPMessage, type CDPRequest, type CDPResponse, type CaptchaDetectionResult, CaptchaDetector, type ChainExecutionResult, type ChainRequest, type ChainStepResult, type CollectResult, type CollectorConfig, type CommandMessage, type CompanyInfo, type ContextChange, type DaemonInfo, DataCollector, DataStorage, type DecisionAction, type DecisionResult, type DomainStat, type ElementRef, type ExecRequest, type ExecutionResult, HTTPServer, type HTTPServerConfig, type HTTPServerError, HumanInteractionManager, type InstallOptions, type InstalledPlugin, type ManagedSession, type MessageDirection, type NetworkEntry, type ParsedPipeline, PlaybackEngine, type PlaybackOptions, type PlaybackResult, PluginInstaller, type PluginLoaderOptions, type RecordedEvent, RecorderController, type RecorderStatus, type Recording, type RecordingControlFile, type RecordingData, type RecordingEvent, type RecordingSession, type RecordingStep, type RecordingSummary, type RegisteredCommand, ResultAnalyzer, type RuleContext, type RuleEngine, type ScopeDefinition, type ScopeLevel, ScreencastCapturer, type ScreencastFrame, type ScreencastMessage, type ScreencastOptions, type SearchResult, type ManagedSession as SessionInfo, SessionRecorder, type StatusMessage, type StorageConfig, type UserAction, type ViolationSeverity, type WSInboundMessage, type WSMessage, WSServer, type WSServerConfig, type WaitForHumanOptions, type WaitForHumanResult, WebhookNotifier, type WebhookPayload, XBrowserPluginLoader, advise, allBuiltins, assertPageScope, attachWaitForHuman, automationSignalsRule, checkBrowserScope, routeCommand as cliRoute, closeAllSessions as closeAllBrowserSessions, closeAllSessions, closeSession, closeSessionByName, createCDPInterceptor, createRuleEngine, createSession, destroyBrowser, destroyBrowser as destroySessionManager, domMutationRule, emulationOverrideRule, eventSimulationRule, executeChain, executeCommand, extractAndSave, extractRecording, filterRecording, findSession, findSession as findSessionInfo, fingerprintingRule, generateBashScript, generateJSScript, generatePythonScript, getAllSessions as getAllBrowserSessions, getAllCommands, getBrowser, getBuiltin, getCaptchaConfig, getCommand, getCommandNames, getCompanyType, getDaemonProcessStatus, getPlatformName, getSessionPage, getWSServerFromCache, inputKeystrokeRule, isChainInput, getAllSessions as listAllBrowserSessions, listSessions, mouseTrajectoryRule, networkAnomalyRule, normalizeSelector, openSession, pageLifecycleRule, parseCommandArgs, parseCommandChain, parseExcludeTypes, printExtractSummary, readCommandFile, readStdin, registerCommand, registerCommandDefinition, resetForTesting, setWSServer, setWSServerCache, splitCommand, startDaemonProcess, stopDaemonProcess, version };
|