@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/cli.js
CHANGED
|
@@ -18,20 +18,6 @@ import {
|
|
|
18
18
|
saveSessionDiskMeta,
|
|
19
19
|
setActivePage
|
|
20
20
|
} from "./chunk-2ONMTDLK.js";
|
|
21
|
-
import {
|
|
22
|
-
PluginMetadataParser
|
|
23
|
-
} from "./chunk-HINTG75P.js";
|
|
24
|
-
import {
|
|
25
|
-
NPM_REGISTRY_URL,
|
|
26
|
-
NPM_SCOPE,
|
|
27
|
-
ensureProxyFetch,
|
|
28
|
-
getConfigValue,
|
|
29
|
-
getMarketplaceUrl,
|
|
30
|
-
loadConfig,
|
|
31
|
-
readJsonFile,
|
|
32
|
-
resolveNpmPackageWithFallback,
|
|
33
|
-
setConfigValue
|
|
34
|
-
} from "./chunk-FF5WHQHN.js";
|
|
35
21
|
import {
|
|
36
22
|
forwardCommandLog,
|
|
37
23
|
forwardNetworkAnalyze,
|
|
@@ -395,15 +381,15 @@ var clickCommand = registerCommand({
|
|
|
395
381
|
let detectedNewPage;
|
|
396
382
|
let cleanup;
|
|
397
383
|
if (ctx.browserContext?.on) {
|
|
398
|
-
const pagePromise = new Promise((
|
|
384
|
+
const pagePromise = new Promise((resolve16) => {
|
|
399
385
|
const timer = setTimeout(() => {
|
|
400
386
|
ctx.browserContext.off("page", handler);
|
|
401
|
-
|
|
387
|
+
resolve16(void 0);
|
|
402
388
|
}, 3e3);
|
|
403
389
|
const handler = (page2) => {
|
|
404
390
|
clearTimeout(timer);
|
|
405
391
|
ctx.browserContext.off("page", handler);
|
|
406
|
-
|
|
392
|
+
resolve16(page2);
|
|
407
393
|
};
|
|
408
394
|
ctx.browserContext.on("page", handler);
|
|
409
395
|
});
|
|
@@ -1161,7 +1147,7 @@ var consoleCheckCommand = registerCommand({
|
|
|
1161
1147
|
await page.goto(p.url, { waitUntil: "domcontentloaded" });
|
|
1162
1148
|
}
|
|
1163
1149
|
const messages = await page.evaluate((args) => {
|
|
1164
|
-
return new Promise((
|
|
1150
|
+
return new Promise((resolve16) => {
|
|
1165
1151
|
const collected = [];
|
|
1166
1152
|
const originalConsole = {
|
|
1167
1153
|
log: console.log,
|
|
@@ -1228,7 +1214,7 @@ ${a.stack || ""}`;
|
|
|
1228
1214
|
console.warn = originalConsole.warn;
|
|
1229
1215
|
console.error = originalConsole.error;
|
|
1230
1216
|
console.info = originalConsole.info;
|
|
1231
|
-
|
|
1217
|
+
resolve16(collected);
|
|
1232
1218
|
}, args.duration);
|
|
1233
1219
|
});
|
|
1234
1220
|
}, { duration: p.duration });
|
|
@@ -1640,7 +1626,7 @@ async function executeAction(page, action) {
|
|
|
1640
1626
|
if (action.selector) {
|
|
1641
1627
|
await page.waitForSelector(action.selector, { timeout: 3e4 });
|
|
1642
1628
|
} else if (action.milliseconds) {
|
|
1643
|
-
await new Promise((
|
|
1629
|
+
await new Promise((resolve16) => setTimeout(resolve16, action.milliseconds));
|
|
1644
1630
|
} else {
|
|
1645
1631
|
throw new Error("wait action requires either milliseconds or selector");
|
|
1646
1632
|
}
|
|
@@ -1723,8 +1709,8 @@ var actionsCommand = registerCommand({
|
|
|
1723
1709
|
results.push(result);
|
|
1724
1710
|
}
|
|
1725
1711
|
})();
|
|
1726
|
-
const timeoutPromise = new Promise((
|
|
1727
|
-
setTimeout(
|
|
1712
|
+
const timeoutPromise = new Promise((resolve16) => {
|
|
1713
|
+
setTimeout(resolve16, timeoutMs);
|
|
1728
1714
|
});
|
|
1729
1715
|
await Promise.race([executionPromise, timeoutPromise]);
|
|
1730
1716
|
const title = await ctx.page.title();
|
|
@@ -2398,11 +2384,11 @@ async function navigateForMap(page, url, timeout = 15e3) {
|
|
|
2398
2384
|
}
|
|
2399
2385
|
async function extractPageLinks(page, baseUrl) {
|
|
2400
2386
|
await navigateForMap(page, baseUrl);
|
|
2401
|
-
await new Promise((
|
|
2387
|
+
await new Promise((resolve16) => setTimeout(resolve16, 2e3));
|
|
2402
2388
|
await page.evaluate(() => {
|
|
2403
2389
|
window.scrollTo(0, document.body.scrollHeight);
|
|
2404
2390
|
});
|
|
2405
|
-
await new Promise((
|
|
2391
|
+
await new Promise((resolve16) => setTimeout(resolve16, 1e3));
|
|
2406
2392
|
const origin = new URL(baseUrl).origin;
|
|
2407
2393
|
const rawLinks = await page.evaluate((evalOrigin) => {
|
|
2408
2394
|
return Array.from(document.querySelectorAll("a[href]")).map((a) => {
|
|
@@ -4232,25 +4218,25 @@ aria snapshot\uFF1A
|
|
|
4232
4218
|
async function analyzeWithLLM(ariaSnapshot) {
|
|
4233
4219
|
const piBin = process.env.PI_CLI_PATH || "pi";
|
|
4234
4220
|
const prompt = LLM_PROMPT.replace("{snapshot}", ariaSnapshot.slice(0, 4e3));
|
|
4235
|
-
return new Promise((
|
|
4221
|
+
return new Promise((resolve16) => {
|
|
4236
4222
|
execFile(
|
|
4237
4223
|
piBin,
|
|
4238
4224
|
["--provider", LLM_PROVIDER, "--model", LLM_MODEL, prompt],
|
|
4239
4225
|
{ timeout: LLM_TIMEOUT_MS, maxBuffer: 1024 * 1024 },
|
|
4240
4226
|
(err, stdout, _stderr) => {
|
|
4241
4227
|
if (err) {
|
|
4242
|
-
|
|
4228
|
+
resolve16(null);
|
|
4243
4229
|
return;
|
|
4244
4230
|
}
|
|
4245
4231
|
const output = (stdout || "").trim();
|
|
4246
4232
|
if (!output) {
|
|
4247
|
-
|
|
4233
|
+
resolve16(null);
|
|
4248
4234
|
return;
|
|
4249
4235
|
}
|
|
4250
4236
|
try {
|
|
4251
4237
|
const parsed = parse(output);
|
|
4252
4238
|
if (!parsed || typeof parsed !== "object") {
|
|
4253
|
-
|
|
4239
|
+
resolve16(null);
|
|
4254
4240
|
return;
|
|
4255
4241
|
}
|
|
4256
4242
|
const elements = {};
|
|
@@ -4265,9 +4251,9 @@ async function analyzeWithLLM(ariaSnapshot) {
|
|
|
4265
4251
|
};
|
|
4266
4252
|
}
|
|
4267
4253
|
}
|
|
4268
|
-
|
|
4254
|
+
resolve16(Object.keys(elements).length > 0 ? elements : null);
|
|
4269
4255
|
} catch {
|
|
4270
|
-
|
|
4256
|
+
resolve16(null);
|
|
4271
4257
|
}
|
|
4272
4258
|
}
|
|
4273
4259
|
);
|
|
@@ -4820,11 +4806,11 @@ function resolveScriptContent(params) {
|
|
|
4820
4806
|
async function readStdin() {
|
|
4821
4807
|
const { createReadStream } = await import("fs");
|
|
4822
4808
|
const { createInterface: createInterface2 } = await import("readline");
|
|
4823
|
-
return new Promise((
|
|
4809
|
+
return new Promise((resolve16, reject) => {
|
|
4824
4810
|
const lines = [];
|
|
4825
4811
|
const rl = createInterface2({ input: createReadStream("/dev/stdin") });
|
|
4826
4812
|
rl.on("line", (line) => lines.push(line));
|
|
4827
|
-
rl.on("close", () =>
|
|
4813
|
+
rl.on("close", () => resolve16(lines.join("\n")));
|
|
4828
4814
|
rl.on("error", reject);
|
|
4829
4815
|
});
|
|
4830
4816
|
}
|
|
@@ -5562,12 +5548,95 @@ var promoCommand = registerCommand({
|
|
|
5562
5548
|
import {
|
|
5563
5549
|
Core
|
|
5564
5550
|
} from "@dyyz1993/xcli-core";
|
|
5565
|
-
import { resolve as
|
|
5566
|
-
import { existsSync as
|
|
5551
|
+
import { resolve as resolve8 } from "path";
|
|
5552
|
+
import { existsSync as existsSync5, readdirSync } from "fs";
|
|
5567
5553
|
import { homedir as homedir2 } from "os";
|
|
5568
5554
|
|
|
5555
|
+
// src/plugin/metadata-parser.ts
|
|
5556
|
+
import { existsSync as existsSync3 } from "fs";
|
|
5557
|
+
import { resolve as resolve7 } from "path";
|
|
5558
|
+
|
|
5559
|
+
// src/utils/json-file.ts
|
|
5560
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
|
|
5561
|
+
function readJsonFile(filePath, defaultValue) {
|
|
5562
|
+
try {
|
|
5563
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
5564
|
+
return JSON.parse(content);
|
|
5565
|
+
} catch {
|
|
5566
|
+
return defaultValue;
|
|
5567
|
+
}
|
|
5568
|
+
}
|
|
5569
|
+
|
|
5570
|
+
// src/plugin/metadata-parser.ts
|
|
5571
|
+
var PluginMetadataParser = class {
|
|
5572
|
+
static XBROWSER_KEYWORDS = ["xbrowser", "xbrowser-plugin"];
|
|
5573
|
+
static parseFromPackageJson(pluginPath) {
|
|
5574
|
+
const packageJsonPath = resolve7(pluginPath, "package.json");
|
|
5575
|
+
if (!existsSync3(packageJsonPath)) {
|
|
5576
|
+
return null;
|
|
5577
|
+
}
|
|
5578
|
+
const packageJson = readJsonFile(packageJsonPath, null);
|
|
5579
|
+
if (!packageJson) return null;
|
|
5580
|
+
if (!packageJson.xbrowser) {
|
|
5581
|
+
return null;
|
|
5582
|
+
}
|
|
5583
|
+
const xbrowser = packageJson.xbrowser;
|
|
5584
|
+
const metadata = {
|
|
5585
|
+
id: xbrowser.id || packageJson.name,
|
|
5586
|
+
name: xbrowser.name || packageJson.name,
|
|
5587
|
+
description: xbrowser.description || packageJson.description || "",
|
|
5588
|
+
version: xbrowser.version || packageJson.version || "1.0.0",
|
|
5589
|
+
author: xbrowser.author || this.extractAuthor(packageJson.author),
|
|
5590
|
+
homepage: xbrowser.homepage || packageJson.homepage,
|
|
5591
|
+
commands: xbrowser.commands,
|
|
5592
|
+
sites: xbrowser.sites,
|
|
5593
|
+
tags: xbrowser.tags,
|
|
5594
|
+
screenshot: xbrowser.screenshot,
|
|
5595
|
+
license: xbrowser.license || packageJson.license
|
|
5596
|
+
};
|
|
5597
|
+
return metadata;
|
|
5598
|
+
}
|
|
5599
|
+
static isXBrowserPlugin(packageJson) {
|
|
5600
|
+
if (packageJson.xbrowser) {
|
|
5601
|
+
return true;
|
|
5602
|
+
}
|
|
5603
|
+
const keywords = packageJson.keywords;
|
|
5604
|
+
if (!keywords) return false;
|
|
5605
|
+
return this.XBROWSER_KEYWORDS.some((kw) => keywords.includes(kw));
|
|
5606
|
+
}
|
|
5607
|
+
static fromNPMResult(result) {
|
|
5608
|
+
const author = typeof result.author === "string" ? result.author : result.author?.name || "Unknown";
|
|
5609
|
+
return {
|
|
5610
|
+
id: result.name,
|
|
5611
|
+
name: result.name.replace(/^xbrowser-plugin-/, "").replace(/^@[^/]+\//, ""),
|
|
5612
|
+
description: result.description || "",
|
|
5613
|
+
version: result.version,
|
|
5614
|
+
author,
|
|
5615
|
+
homepage: result.homepage || result.links?.homepage,
|
|
5616
|
+
tags: result.keywords,
|
|
5617
|
+
license: ""
|
|
5618
|
+
};
|
|
5619
|
+
}
|
|
5620
|
+
static extractAuthor(author) {
|
|
5621
|
+
if (typeof author === "string") return author;
|
|
5622
|
+
if (typeof author === "object" && author !== null) {
|
|
5623
|
+
const authorObj = author;
|
|
5624
|
+
return authorObj.name || "Unknown";
|
|
5625
|
+
}
|
|
5626
|
+
return "Unknown";
|
|
5627
|
+
}
|
|
5628
|
+
static validateMetadata(metadata) {
|
|
5629
|
+
const errors = [];
|
|
5630
|
+
if (!metadata.id) errors.push("id is required");
|
|
5631
|
+
if (!metadata.name) errors.push("name is required");
|
|
5632
|
+
if (!metadata.description) errors.push("description is required");
|
|
5633
|
+
if (!metadata.version) errors.push("version is required");
|
|
5634
|
+
return errors;
|
|
5635
|
+
}
|
|
5636
|
+
};
|
|
5637
|
+
|
|
5569
5638
|
// src/plugin/ensure-deps.ts
|
|
5570
|
-
import { existsSync as
|
|
5639
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
5571
5640
|
import { join as join2 } from "path";
|
|
5572
5641
|
import { execSync as execSync6 } from "child_process";
|
|
5573
5642
|
var SHARED_PLUGIN_DEPENDENCIES = {
|
|
@@ -5576,11 +5645,11 @@ var SHARED_PLUGIN_DEPENDENCIES = {
|
|
|
5576
5645
|
};
|
|
5577
5646
|
function ensurePluginDependencies(pluginsDir) {
|
|
5578
5647
|
const zodPath = join2(pluginsDir, "node_modules", "zod");
|
|
5579
|
-
if (
|
|
5648
|
+
if (existsSync4(zodPath)) return;
|
|
5580
5649
|
mkdirSync2(pluginsDir, { recursive: true });
|
|
5581
5650
|
const pkgPath = join2(pluginsDir, "package.json");
|
|
5582
5651
|
let pkg2 = {};
|
|
5583
|
-
if (
|
|
5652
|
+
if (existsSync4(pkgPath)) {
|
|
5584
5653
|
try {
|
|
5585
5654
|
pkg2 = readJsonFile(pkgPath, {});
|
|
5586
5655
|
} catch {
|
|
@@ -5594,11 +5663,11 @@ function ensurePluginDependencies(pluginsDir) {
|
|
|
5594
5663
|
needsInstall = true;
|
|
5595
5664
|
}
|
|
5596
5665
|
}
|
|
5597
|
-
if (!needsInstall &&
|
|
5666
|
+
if (!needsInstall && existsSync4(join2(pluginsDir, "node_modules"))) return;
|
|
5598
5667
|
pkg2.dependencies = existingDeps;
|
|
5599
5668
|
pkg2.private = true;
|
|
5600
5669
|
pkg2.description = pkg2.description || "xbrowser plugins \u2014 shared dependencies";
|
|
5601
|
-
|
|
5670
|
+
writeFileSync4(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
5602
5671
|
try {
|
|
5603
5672
|
execSync6("npm install --production --no-package-lock --no-fund --no-audit", {
|
|
5604
5673
|
cwd: pluginsDir,
|
|
@@ -5628,7 +5697,7 @@ var XBrowserPluginLoader = class {
|
|
|
5628
5697
|
envPrefix: "XBROWSER",
|
|
5629
5698
|
pluginDirs: [
|
|
5630
5699
|
...DEFAULT_PLUGIN_DIRS,
|
|
5631
|
-
|
|
5700
|
+
resolve8(cwd, ".xcli/plugins")
|
|
5632
5701
|
]
|
|
5633
5702
|
};
|
|
5634
5703
|
this.core = new Core(coreConfig);
|
|
@@ -5667,31 +5736,31 @@ var XBrowserPluginLoader = class {
|
|
|
5667
5736
|
}
|
|
5668
5737
|
async scanAndLoad() {
|
|
5669
5738
|
const cwd = this.options.cwd || process.cwd();
|
|
5670
|
-
const globalDir = this.options.globalDir ||
|
|
5739
|
+
const globalDir = this.options.globalDir || resolve8(homedir2(), ".xbrowser/plugins");
|
|
5671
5740
|
ensurePluginDependencies(globalDir);
|
|
5672
5741
|
const dirs = [
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
this.options.userDir ||
|
|
5742
|
+
resolve8(cwd, ".xcli/plugins"),
|
|
5743
|
+
resolve8(cwd, "../.xcli/plugins"),
|
|
5744
|
+
this.options.userDir || resolve8(homedir2(), ".xcli/plugins"),
|
|
5676
5745
|
globalDir
|
|
5677
5746
|
];
|
|
5678
5747
|
const loaded = [];
|
|
5679
5748
|
const seen = /* @__PURE__ */ new Set();
|
|
5680
5749
|
for (const dir of dirs) {
|
|
5681
|
-
if (!
|
|
5750
|
+
if (!existsSync5(dir)) continue;
|
|
5682
5751
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
5683
5752
|
for (const entry of entries) {
|
|
5684
5753
|
if (!entry.isDirectory()) continue;
|
|
5685
5754
|
if (seen.has(entry.name)) continue;
|
|
5686
5755
|
seen.add(entry.name);
|
|
5687
|
-
const pluginDir =
|
|
5688
|
-
let indexPath =
|
|
5689
|
-
if (!
|
|
5690
|
-
indexPath =
|
|
5756
|
+
const pluginDir = resolve8(dir, entry.name);
|
|
5757
|
+
let indexPath = resolve8(pluginDir, "index.js");
|
|
5758
|
+
if (!existsSync5(indexPath)) {
|
|
5759
|
+
indexPath = resolve8(pluginDir, "index.ts");
|
|
5691
5760
|
}
|
|
5692
|
-
if (!
|
|
5761
|
+
if (!existsSync5(indexPath)) continue;
|
|
5693
5762
|
try {
|
|
5694
|
-
if (!
|
|
5763
|
+
if (!existsSync5(resolve8(pluginDir, "package.json"))) {
|
|
5695
5764
|
console.warn(`\u26A0\uFE0F Plugin "${entry.name}" has no package.json. Use "xbrowser create ${entry.name} --template static" for proper structure.`);
|
|
5696
5765
|
} else {
|
|
5697
5766
|
const metadata = PluginMetadataParser.parseFromPackageJson(pluginDir);
|
|
@@ -5708,22 +5777,6 @@ var XBrowserPluginLoader = class {
|
|
|
5708
5777
|
}
|
|
5709
5778
|
}
|
|
5710
5779
|
}
|
|
5711
|
-
try {
|
|
5712
|
-
const { default: setupMarketplace } = await import("./marketplace-W545W4FR.js");
|
|
5713
|
-
setupMarketplace(this.loader.getAPI());
|
|
5714
|
-
} catch (err) {
|
|
5715
|
-
if (process.env.XBROWSER_DEBUG) {
|
|
5716
|
-
console.warn(`\u26A0\uFE0F Built-in marketplace plugin load failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5717
|
-
}
|
|
5718
|
-
}
|
|
5719
|
-
try {
|
|
5720
|
-
const { default: setupAdmin } = await import("./admin-RPJJ5CAF.js");
|
|
5721
|
-
setupAdmin(this.loader.getAPI());
|
|
5722
|
-
} catch (err) {
|
|
5723
|
-
if (process.env.XBROWSER_DEBUG) {
|
|
5724
|
-
console.warn(`\u26A0\uFE0F Built-in admin plugin load failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5725
|
-
}
|
|
5726
|
-
}
|
|
5727
5780
|
return loaded;
|
|
5728
5781
|
}
|
|
5729
5782
|
async unload() {
|
|
@@ -6124,7 +6177,7 @@ var TipsManager = class {
|
|
|
6124
6177
|
}
|
|
6125
6178
|
}
|
|
6126
6179
|
debounce() {
|
|
6127
|
-
return new Promise((
|
|
6180
|
+
return new Promise((resolve16) => setTimeout(resolve16, DEBOUNCE_MS));
|
|
6128
6181
|
}
|
|
6129
6182
|
formatTips(tips) {
|
|
6130
6183
|
return tips.map((tip) => {
|
|
@@ -6151,6 +6204,24 @@ function getTipsManager() {
|
|
|
6151
6204
|
return globalTipsManager;
|
|
6152
6205
|
}
|
|
6153
6206
|
|
|
6207
|
+
// src/hooks/loader.ts
|
|
6208
|
+
var builtinHooks = {
|
|
6209
|
+
screenshot: () => import("./screenshot-MB6R7RSS.js").then((m) => m.screenshotHook)
|
|
6210
|
+
};
|
|
6211
|
+
async function loadHooks() {
|
|
6212
|
+
const names = process.env.XBROWSER_HOOKS;
|
|
6213
|
+
if (!names) return [];
|
|
6214
|
+
const hooks = [];
|
|
6215
|
+
for (const name of names.split(",")) {
|
|
6216
|
+
const trimmed = name.trim();
|
|
6217
|
+
const factory = builtinHooks[trimmed];
|
|
6218
|
+
if (factory) {
|
|
6219
|
+
hooks.push(await factory());
|
|
6220
|
+
}
|
|
6221
|
+
}
|
|
6222
|
+
return hooks;
|
|
6223
|
+
}
|
|
6224
|
+
|
|
6154
6225
|
// src/executor.ts
|
|
6155
6226
|
import { homedir as homedir3 } from "os";
|
|
6156
6227
|
import { join as join3 } from "path";
|
|
@@ -6310,9 +6381,22 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6310
6381
|
}
|
|
6311
6382
|
}
|
|
6312
6383
|
try {
|
|
6384
|
+
const hooks = await loadHooks();
|
|
6385
|
+
if (hooks.length > 0 && session?.page) {
|
|
6386
|
+
await Promise.all(hooks.map((h) => h.onBeforeCommand?.({ page: session.page, command: commandName, params })));
|
|
6387
|
+
}
|
|
6313
6388
|
const raw = await command.handler(params, ctx);
|
|
6314
6389
|
const end = Date.now();
|
|
6315
6390
|
const duration = end - start;
|
|
6391
|
+
let hookOutputs;
|
|
6392
|
+
if (hooks.length > 0 && session?.page) {
|
|
6393
|
+
const outputs = [];
|
|
6394
|
+
for (const h of hooks) {
|
|
6395
|
+
const output = await h.onAfterCommand?.({ page: session.page, command: commandName, params, result: raw, duration });
|
|
6396
|
+
if (output) outputs.push({ _hook: h.name, ...output });
|
|
6397
|
+
}
|
|
6398
|
+
if (outputs.length > 0) hookOutputs = outputs;
|
|
6399
|
+
}
|
|
6316
6400
|
if (session && isCommandResult(raw)) {
|
|
6317
6401
|
const resultData = raw.data;
|
|
6318
6402
|
const convUrl = resultData?.conversationUrl;
|
|
@@ -6360,9 +6444,9 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6360
6444
|
timestamp: start
|
|
6361
6445
|
});
|
|
6362
6446
|
if (isSuccess) {
|
|
6363
|
-
return { ...ok25(raw.data, merged.length > 0 ? merged : raw.tips), duration };
|
|
6447
|
+
return { ...ok25(raw.data, merged.length > 0 ? merged : raw.tips), duration, ...hookOutputs ? { hookOutputs } : {} };
|
|
6364
6448
|
}
|
|
6365
|
-
return { success: false, data: raw.data, message: raw.message, tips: merged.length > 0 ? merged : raw.tips || [], duration };
|
|
6449
|
+
return { success: false, data: raw.data, message: raw.message, tips: merged.length > 0 ? merged : raw.tips || [], duration, ...hookOutputs ? { hookOutputs } : {} };
|
|
6366
6450
|
}
|
|
6367
6451
|
recordArchive(session?.id, sessionName, {
|
|
6368
6452
|
step: 0,
|
|
@@ -6373,7 +6457,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6373
6457
|
duration,
|
|
6374
6458
|
timestamp: start
|
|
6375
6459
|
});
|
|
6376
|
-
return { ...ok25(raw, smartTips), duration };
|
|
6460
|
+
return { ...ok25(raw, smartTips), duration, ...hookOutputs ? { hookOutputs } : {} };
|
|
6377
6461
|
} catch (err) {
|
|
6378
6462
|
const end = Date.now();
|
|
6379
6463
|
const duration = end - start;
|
|
@@ -6768,6 +6852,68 @@ var sessionKillBuiltin = {
|
|
|
6768
6852
|
}
|
|
6769
6853
|
};
|
|
6770
6854
|
|
|
6855
|
+
// src/config.ts
|
|
6856
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
6857
|
+
import { join as join4 } from "path";
|
|
6858
|
+
import { homedir as homedir4, tmpdir } from "os";
|
|
6859
|
+
function getConfigFile() {
|
|
6860
|
+
return join4(homedir4() || tmpdir(), ".xbrowser", "config.json");
|
|
6861
|
+
}
|
|
6862
|
+
function loadConfig() {
|
|
6863
|
+
const configFile = getConfigFile();
|
|
6864
|
+
if (!existsSync6(configFile)) return {};
|
|
6865
|
+
return readJsonFile(configFile, {});
|
|
6866
|
+
}
|
|
6867
|
+
function saveConfig(config) {
|
|
6868
|
+
const dir = join4(homedir4() || tmpdir(), ".xbrowser");
|
|
6869
|
+
const configFile = getConfigFile();
|
|
6870
|
+
if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
|
|
6871
|
+
writeFileSync5(configFile, JSON.stringify(config, null, 2), "utf-8");
|
|
6872
|
+
}
|
|
6873
|
+
function getConfigValue(key) {
|
|
6874
|
+
return loadConfig()[key];
|
|
6875
|
+
}
|
|
6876
|
+
function setConfigValue(key, value) {
|
|
6877
|
+
const config = loadConfig();
|
|
6878
|
+
config[key] = value;
|
|
6879
|
+
saveConfig(config);
|
|
6880
|
+
}
|
|
6881
|
+
var DEFAULT_MARKETPLACE_URL = "https://marketplace.xbrowser.dev";
|
|
6882
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
6883
|
+
var NPM_SCOPE = "@xbrowser/";
|
|
6884
|
+
function getMarketplaceUrl() {
|
|
6885
|
+
return process.env.XBROWSER_MARKETPLACE_URL || getConfigValue("marketplaceUrl") || DEFAULT_MARKETPLACE_URL;
|
|
6886
|
+
}
|
|
6887
|
+
function resolveNpmPackageName(name) {
|
|
6888
|
+
if (name.startsWith("@")) return name;
|
|
6889
|
+
return `${NPM_SCOPE}${name}`;
|
|
6890
|
+
}
|
|
6891
|
+
var NPM_NAME_ALIASES = {
|
|
6892
|
+
"1688": "alibaba-1688"
|
|
6893
|
+
};
|
|
6894
|
+
function generateNpmCandidates(name) {
|
|
6895
|
+
if (name.startsWith("@")) return [name];
|
|
6896
|
+
const resolved = NPM_NAME_ALIASES[name] ?? name;
|
|
6897
|
+
return [
|
|
6898
|
+
`${NPM_SCOPE}xbrowser-plugin-${resolved}`,
|
|
6899
|
+
`${NPM_SCOPE}${resolved}`,
|
|
6900
|
+
`xbrowser-plugin-${resolved}`
|
|
6901
|
+
];
|
|
6902
|
+
}
|
|
6903
|
+
async function resolveNpmPackageWithFallback(name) {
|
|
6904
|
+
const candidates = generateNpmCandidates(name);
|
|
6905
|
+
for (const candidate of candidates) {
|
|
6906
|
+
try {
|
|
6907
|
+
const encoded = encodeURIComponent(candidate);
|
|
6908
|
+
const res = await fetch(`${NPM_REGISTRY_URL}/${encoded}`);
|
|
6909
|
+
if (res.ok) return candidate;
|
|
6910
|
+
} catch {
|
|
6911
|
+
continue;
|
|
6912
|
+
}
|
|
6913
|
+
}
|
|
6914
|
+
return resolveNpmPackageName(name);
|
|
6915
|
+
}
|
|
6916
|
+
|
|
6771
6917
|
// src/builtins/config.ts
|
|
6772
6918
|
var configBuiltin = {
|
|
6773
6919
|
name: "config",
|
|
@@ -6830,32 +6976,78 @@ var configBuiltin = {
|
|
|
6830
6976
|
|
|
6831
6977
|
// src/plugin/installer.ts
|
|
6832
6978
|
import {
|
|
6833
|
-
existsSync as
|
|
6979
|
+
existsSync as existsSync13,
|
|
6834
6980
|
readdirSync as readdirSync3,
|
|
6835
|
-
mkdirSync as
|
|
6981
|
+
mkdirSync as mkdirSync8,
|
|
6836
6982
|
rmSync as rmSync7
|
|
6837
6983
|
} from "fs";
|
|
6838
|
-
import { resolve as
|
|
6839
|
-
import { homedir as
|
|
6984
|
+
import { resolve as resolve15, basename as basename2 } from "path";
|
|
6985
|
+
import { homedir as homedir5 } from "os";
|
|
6840
6986
|
|
|
6841
6987
|
// src/plugin/install-sources/local.ts
|
|
6842
|
-
import { existsSync as
|
|
6843
|
-
import { resolve as
|
|
6988
|
+
import { existsSync as existsSync8, cpSync as cpSync2, rmSync as rmSync2 } from "fs";
|
|
6989
|
+
import { resolve as resolve10 } from "path";
|
|
6844
6990
|
|
|
6845
6991
|
// src/plugin/install-utils.ts
|
|
6846
6992
|
import {
|
|
6847
|
-
existsSync as
|
|
6993
|
+
existsSync as existsSync7,
|
|
6848
6994
|
readdirSync as readdirSync2,
|
|
6849
6995
|
cpSync,
|
|
6850
6996
|
rmSync,
|
|
6851
|
-
mkdirSync as
|
|
6852
|
-
readFileSync as
|
|
6997
|
+
mkdirSync as mkdirSync4,
|
|
6998
|
+
readFileSync as readFileSync10,
|
|
6853
6999
|
createWriteStream
|
|
6854
7000
|
} from "fs";
|
|
6855
|
-
import { resolve as
|
|
7001
|
+
import { resolve as resolve9 } from "path";
|
|
6856
7002
|
import { execSync as execSync7 } from "child_process";
|
|
6857
7003
|
import { pipeline } from "stream/promises";
|
|
6858
7004
|
import { Readable } from "stream";
|
|
7005
|
+
|
|
7006
|
+
// src/utils/proxy-fetch.ts
|
|
7007
|
+
var patched = false;
|
|
7008
|
+
async function ensureProxyFetch() {
|
|
7009
|
+
if (patched) return;
|
|
7010
|
+
patched = true;
|
|
7011
|
+
if (process.env.https_proxy && !process.env.HTTPS_PROXY) {
|
|
7012
|
+
process.env.HTTPS_PROXY = process.env.https_proxy;
|
|
7013
|
+
}
|
|
7014
|
+
if (process.env.http_proxy && !process.env.HTTP_PROXY) {
|
|
7015
|
+
process.env.HTTP_PROXY = process.env.http_proxy;
|
|
7016
|
+
}
|
|
7017
|
+
if (process.env.all_proxy && !process.env.ALL_PROXY) {
|
|
7018
|
+
process.env.ALL_PROXY = process.env.all_proxy;
|
|
7019
|
+
}
|
|
7020
|
+
const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.HTTP_PROXY || process.env.all_proxy || process.env.ALL_PROXY;
|
|
7021
|
+
if (!proxyUrl) return;
|
|
7022
|
+
try {
|
|
7023
|
+
const undici = await import("undici");
|
|
7024
|
+
const EnvHttpProxyAgent = undici.EnvHttpProxyAgent;
|
|
7025
|
+
const uFetch = undici.fetch;
|
|
7026
|
+
const UFormData = undici.FormData;
|
|
7027
|
+
if (EnvHttpProxyAgent && uFetch && UFormData) {
|
|
7028
|
+
const agent = new EnvHttpProxyAgent();
|
|
7029
|
+
globalThis.fetch = ((input, init) => {
|
|
7030
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
7031
|
+
const body = init?.body;
|
|
7032
|
+
if (body instanceof globalThis.FormData && !(body instanceof UFormData)) {
|
|
7033
|
+
const ufd = new UFormData();
|
|
7034
|
+
body.forEach((value, key) => {
|
|
7035
|
+
if (value instanceof Blob) {
|
|
7036
|
+
ufd.append(key, value, value.name || "file");
|
|
7037
|
+
} else {
|
|
7038
|
+
ufd.append(key, value);
|
|
7039
|
+
}
|
|
7040
|
+
});
|
|
7041
|
+
return uFetch(url, { ...init, body: ufd, dispatcher: agent });
|
|
7042
|
+
}
|
|
7043
|
+
return uFetch(url, { ...init, dispatcher: agent });
|
|
7044
|
+
});
|
|
7045
|
+
}
|
|
7046
|
+
} catch {
|
|
7047
|
+
}
|
|
7048
|
+
}
|
|
7049
|
+
|
|
7050
|
+
// src/plugin/install-utils.ts
|
|
6859
7051
|
async function downloadToFile(url, destPath) {
|
|
6860
7052
|
await ensureProxyFetch();
|
|
6861
7053
|
if (url.startsWith("file://")) {
|
|
@@ -6874,7 +7066,7 @@ async function downloadToFile(url, destPath) {
|
|
|
6874
7066
|
await pipeline(nodeStream, createWriteStream(destPath));
|
|
6875
7067
|
}
|
|
6876
7068
|
function extractTarGz(tarballPath, targetDir) {
|
|
6877
|
-
|
|
7069
|
+
mkdirSync4(targetDir, { recursive: true });
|
|
6878
7070
|
execSync7(`tar -xzf "${tarballPath}" -C "${targetDir}"`, { stdio: "pipe" });
|
|
6879
7071
|
}
|
|
6880
7072
|
function flattenPackageRoot(targetDir) {
|
|
@@ -6882,11 +7074,11 @@ function flattenPackageRoot(targetDir) {
|
|
|
6882
7074
|
const dirs = entries.filter((e) => e.isDirectory());
|
|
6883
7075
|
const files = entries.filter((e) => !e.isDirectory());
|
|
6884
7076
|
if (dirs.length === 1 && files.length === 0) {
|
|
6885
|
-
const pkgDir =
|
|
7077
|
+
const pkgDir = resolve9(targetDir, dirs[0].name);
|
|
6886
7078
|
const items = readdirSync2(pkgDir);
|
|
6887
7079
|
for (const item of items) {
|
|
6888
|
-
const src =
|
|
6889
|
-
const dst =
|
|
7080
|
+
const src = resolve9(pkgDir, item);
|
|
7081
|
+
const dst = resolve9(targetDir, item);
|
|
6890
7082
|
cpSync(src, dst, { recursive: true, force: true });
|
|
6891
7083
|
}
|
|
6892
7084
|
rmSync(pkgDir, { recursive: true, force: true });
|
|
@@ -6894,19 +7086,19 @@ function flattenPackageRoot(targetDir) {
|
|
|
6894
7086
|
}
|
|
6895
7087
|
async function verifyPlugin(dir) {
|
|
6896
7088
|
const warnings = [];
|
|
6897
|
-
const indexPath =
|
|
6898
|
-
if (!
|
|
6899
|
-
const indexJs =
|
|
6900
|
-
if (!
|
|
7089
|
+
const indexPath = resolve9(dir, "index.ts");
|
|
7090
|
+
if (!existsSync7(indexPath)) {
|
|
7091
|
+
const indexJs = resolve9(dir, "index.js");
|
|
7092
|
+
if (!existsSync7(indexJs)) {
|
|
6901
7093
|
return { valid: false, error: "No index.ts or index.js entry point found", warnings };
|
|
6902
7094
|
}
|
|
6903
7095
|
}
|
|
6904
|
-
const pkgPath =
|
|
6905
|
-
if (!
|
|
7096
|
+
const pkgPath = resolve9(dir, "package.json");
|
|
7097
|
+
if (!existsSync7(pkgPath)) {
|
|
6906
7098
|
warnings.push("No package.json found");
|
|
6907
7099
|
} else {
|
|
6908
7100
|
try {
|
|
6909
|
-
const pkg2 = JSON.parse(
|
|
7101
|
+
const pkg2 = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
6910
7102
|
if (!pkg2.xbrowser) {
|
|
6911
7103
|
warnings.push("No xbrowser metadata in package.json");
|
|
6912
7104
|
}
|
|
@@ -6925,8 +7117,8 @@ function safeCleanup(dir) {
|
|
|
6925
7117
|
|
|
6926
7118
|
// src/plugin/install-sources/local.ts
|
|
6927
7119
|
async function installFromLocal(source, name, targetDir) {
|
|
6928
|
-
const srcPath =
|
|
6929
|
-
if (!
|
|
7120
|
+
const srcPath = resolve10(source);
|
|
7121
|
+
if (!existsSync8(srcPath)) {
|
|
6930
7122
|
throw new Error(`Local path does not exist: ${srcPath}`);
|
|
6931
7123
|
}
|
|
6932
7124
|
const tmpTarget = `${targetDir}-tmp-${Date.now()}`;
|
|
@@ -6939,7 +7131,7 @@ async function installFromLocal(source, name, targetDir) {
|
|
|
6939
7131
|
safeCleanup(tmpTarget);
|
|
6940
7132
|
throw new Error(`Invalid plugin: ${verify.error}`);
|
|
6941
7133
|
}
|
|
6942
|
-
if (
|
|
7134
|
+
if (existsSync8(targetDir)) {
|
|
6943
7135
|
rmSync2(targetDir, { recursive: true, force: true });
|
|
6944
7136
|
}
|
|
6945
7137
|
cpSync2(tmpTarget, targetDir, { recursive: true, force: true });
|
|
@@ -6959,9 +7151,9 @@ async function installFromLocal(source, name, targetDir) {
|
|
|
6959
7151
|
}
|
|
6960
7152
|
|
|
6961
7153
|
// src/plugin/install-sources/npm.ts
|
|
6962
|
-
import { existsSync as
|
|
6963
|
-
import { resolve as
|
|
6964
|
-
import { tmpdir } from "os";
|
|
7154
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync6, rmSync as rmSync3, cpSync as cpSync3 } from "fs";
|
|
7155
|
+
import { resolve as resolve11, join as join5 } from "path";
|
|
7156
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
6965
7157
|
async function installFromNpm(packageName, name, targetDir) {
|
|
6966
7158
|
await ensureProxyFetch();
|
|
6967
7159
|
const encodedName = encodeURIComponent(packageName);
|
|
@@ -6979,13 +7171,13 @@ async function installFromNpm(packageName, name, targetDir) {
|
|
|
6979
7171
|
throw new Error(`No tarball URL for ${packageName}@${latestVersion}`);
|
|
6980
7172
|
}
|
|
6981
7173
|
const tarballUrl = versionMeta.dist.tarball;
|
|
6982
|
-
const tmpDir =
|
|
6983
|
-
|
|
7174
|
+
const tmpDir = join5(tmpdir2(), `xbrowser-npm-${Date.now()}`);
|
|
7175
|
+
mkdirSync5(tmpDir, { recursive: true });
|
|
6984
7176
|
let warnings = [];
|
|
6985
7177
|
try {
|
|
6986
|
-
const tarballPath =
|
|
7178
|
+
const tarballPath = join5(tmpDir, `${name}.tgz`);
|
|
6987
7179
|
await downloadToFile(tarballUrl, tarballPath);
|
|
6988
|
-
const extractDir =
|
|
7180
|
+
const extractDir = join5(tmpDir, "extracted");
|
|
6989
7181
|
extractTarGz(tarballPath, extractDir);
|
|
6990
7182
|
flattenPackageRoot(extractDir);
|
|
6991
7183
|
const verify = await verifyPlugin(extractDir);
|
|
@@ -6993,16 +7185,16 @@ async function installFromNpm(packageName, name, targetDir) {
|
|
|
6993
7185
|
if (!verify.valid) {
|
|
6994
7186
|
throw new Error(`Invalid npm plugin: ${verify.error}`);
|
|
6995
7187
|
}
|
|
6996
|
-
if (
|
|
7188
|
+
if (existsSync9(targetDir)) {
|
|
6997
7189
|
rmSync3(targetDir, { recursive: true, force: true });
|
|
6998
7190
|
}
|
|
6999
7191
|
cpSync3(extractDir, targetDir, { recursive: true, force: true });
|
|
7000
|
-
const pkgPath =
|
|
7001
|
-
if (
|
|
7002
|
-
const pkg2 = JSON.parse(
|
|
7192
|
+
const pkgPath = resolve11(targetDir, "package.json");
|
|
7193
|
+
if (existsSync9(pkgPath)) {
|
|
7194
|
+
const pkg2 = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
7003
7195
|
if (!pkg2._npmSource) {
|
|
7004
7196
|
pkg2._npmSource = { name: packageName, version: latestVersion };
|
|
7005
|
-
|
|
7197
|
+
writeFileSync6(pkgPath, JSON.stringify(pkg2, null, 2));
|
|
7006
7198
|
}
|
|
7007
7199
|
}
|
|
7008
7200
|
} finally {
|
|
@@ -7019,12 +7211,12 @@ async function installFromNpm(packageName, name, targetDir) {
|
|
|
7019
7211
|
}
|
|
7020
7212
|
|
|
7021
7213
|
// src/plugin/install-sources/git.ts
|
|
7022
|
-
import { existsSync as
|
|
7023
|
-
import { resolve as
|
|
7024
|
-
import { tmpdir as
|
|
7214
|
+
import { existsSync as existsSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync7, rmSync as rmSync4, cpSync as cpSync4 } from "fs";
|
|
7215
|
+
import { resolve as resolve12, join as join6 } from "path";
|
|
7216
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
7025
7217
|
import { execSync as execSync8 } from "child_process";
|
|
7026
7218
|
async function installFromGit(gitUrl, name, targetDir) {
|
|
7027
|
-
const tmpDir =
|
|
7219
|
+
const tmpDir = join6(tmpdir3(), `xbrowser-git-${Date.now()}`);
|
|
7028
7220
|
let warnings = [];
|
|
7029
7221
|
try {
|
|
7030
7222
|
execSync8(`git clone --depth 1 "${gitUrl}" "${tmpDir}"`, { stdio: "pipe" });
|
|
@@ -7033,17 +7225,17 @@ async function installFromGit(gitUrl, name, targetDir) {
|
|
|
7033
7225
|
if (!verify.valid) {
|
|
7034
7226
|
throw new Error(`Invalid git plugin: ${verify.error}`);
|
|
7035
7227
|
}
|
|
7036
|
-
if (
|
|
7228
|
+
if (existsSync10(targetDir)) {
|
|
7037
7229
|
rmSync4(targetDir, { recursive: true, force: true });
|
|
7038
7230
|
}
|
|
7039
7231
|
cpSync4(tmpDir, targetDir, { recursive: true, force: true });
|
|
7040
|
-
rmSync4(
|
|
7041
|
-
const pkgPath =
|
|
7042
|
-
if (
|
|
7043
|
-
const pkg2 = JSON.parse(
|
|
7232
|
+
rmSync4(resolve12(targetDir, ".git"), { recursive: true, force: true });
|
|
7233
|
+
const pkgPath = resolve12(targetDir, "package.json");
|
|
7234
|
+
if (existsSync10(pkgPath)) {
|
|
7235
|
+
const pkg2 = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
7044
7236
|
if (!pkg2._gitSource) {
|
|
7045
7237
|
pkg2._gitSource = { url: gitUrl };
|
|
7046
|
-
|
|
7238
|
+
writeFileSync7(pkgPath, JSON.stringify(pkg2, null, 2));
|
|
7047
7239
|
}
|
|
7048
7240
|
}
|
|
7049
7241
|
} finally {
|
|
@@ -7060,18 +7252,18 @@ async function installFromGit(gitUrl, name, targetDir) {
|
|
|
7060
7252
|
}
|
|
7061
7253
|
|
|
7062
7254
|
// src/plugin/install-sources/url.ts
|
|
7063
|
-
import { existsSync as
|
|
7064
|
-
import { resolve as
|
|
7065
|
-
import { tmpdir as
|
|
7255
|
+
import { existsSync as existsSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, rmSync as rmSync5, cpSync as cpSync5 } from "fs";
|
|
7256
|
+
import { resolve as resolve13, join as join7, basename } from "path";
|
|
7257
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
7066
7258
|
async function installFromUrl(url, name, targetDir) {
|
|
7067
|
-
const tmpDir =
|
|
7068
|
-
|
|
7259
|
+
const tmpDir = join7(tmpdir4(), `xbrowser-url-${Date.now()}`);
|
|
7260
|
+
mkdirSync6(tmpDir, { recursive: true });
|
|
7069
7261
|
let warnings = [];
|
|
7070
7262
|
try {
|
|
7071
7263
|
const fileName = basename(new URL(url).pathname) || "plugin.tar.gz";
|
|
7072
|
-
const tarballPath =
|
|
7264
|
+
const tarballPath = join7(tmpDir, fileName);
|
|
7073
7265
|
await downloadToFile(url, tarballPath);
|
|
7074
|
-
const extractDir =
|
|
7266
|
+
const extractDir = join7(tmpDir, "extracted");
|
|
7075
7267
|
extractTarGz(tarballPath, extractDir);
|
|
7076
7268
|
flattenPackageRoot(extractDir);
|
|
7077
7269
|
const verify = await verifyPlugin(extractDir);
|
|
@@ -7079,16 +7271,16 @@ async function installFromUrl(url, name, targetDir) {
|
|
|
7079
7271
|
if (!verify.valid) {
|
|
7080
7272
|
throw new Error(`Invalid plugin from URL: ${verify.error}`);
|
|
7081
7273
|
}
|
|
7082
|
-
if (
|
|
7274
|
+
if (existsSync11(targetDir)) {
|
|
7083
7275
|
rmSync5(targetDir, { recursive: true, force: true });
|
|
7084
7276
|
}
|
|
7085
7277
|
cpSync5(extractDir, targetDir, { recursive: true, force: true });
|
|
7086
|
-
const pkgPath =
|
|
7087
|
-
if (
|
|
7088
|
-
const pkg2 = JSON.parse(
|
|
7278
|
+
const pkgPath = resolve13(targetDir, "package.json");
|
|
7279
|
+
if (existsSync11(pkgPath)) {
|
|
7280
|
+
const pkg2 = JSON.parse(readFileSync13(pkgPath, "utf-8"));
|
|
7089
7281
|
if (!pkg2._urlSource) {
|
|
7090
7282
|
pkg2._urlSource = { url };
|
|
7091
|
-
|
|
7283
|
+
writeFileSync8(pkgPath, JSON.stringify(pkg2, null, 2));
|
|
7092
7284
|
}
|
|
7093
7285
|
}
|
|
7094
7286
|
} finally {
|
|
@@ -7106,15 +7298,15 @@ async function installFromUrl(url, name, targetDir) {
|
|
|
7106
7298
|
|
|
7107
7299
|
// src/plugin/install-sources/marketplace.ts
|
|
7108
7300
|
import {
|
|
7109
|
-
existsSync as
|
|
7110
|
-
mkdirSync as
|
|
7111
|
-
writeFileSync as
|
|
7112
|
-
readFileSync as
|
|
7301
|
+
existsSync as existsSync12,
|
|
7302
|
+
mkdirSync as mkdirSync7,
|
|
7303
|
+
writeFileSync as writeFileSync9,
|
|
7304
|
+
readFileSync as readFileSync14,
|
|
7113
7305
|
rmSync as rmSync6,
|
|
7114
7306
|
cpSync as cpSync6
|
|
7115
7307
|
} from "fs";
|
|
7116
|
-
import { resolve as
|
|
7117
|
-
import { tmpdir as
|
|
7308
|
+
import { resolve as resolve14, join as join8, dirname as dirname2 } from "path";
|
|
7309
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
7118
7310
|
import { gunzipSync } from "zlib";
|
|
7119
7311
|
async function installFromMarketplace(pluginsDir, slug, options) {
|
|
7120
7312
|
await ensureProxyFetch();
|
|
@@ -7130,13 +7322,13 @@ async function installFromMarketplace(pluginsDir, slug, options) {
|
|
|
7130
7322
|
}
|
|
7131
7323
|
const plugin = detailData.data;
|
|
7132
7324
|
const name = options?.name || String(plugin.slug || slug);
|
|
7133
|
-
const targetDir =
|
|
7134
|
-
if (
|
|
7325
|
+
const targetDir = resolve14(pluginsDir, name);
|
|
7326
|
+
if (existsSync12(targetDir) && !options?.force) {
|
|
7135
7327
|
throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
|
|
7136
7328
|
}
|
|
7137
|
-
|
|
7138
|
-
const tmpDir =
|
|
7139
|
-
|
|
7329
|
+
mkdirSync7(targetDir, { recursive: true });
|
|
7330
|
+
const tmpDir = join8(tmpdir5(), `xbrowser-marketplace-${Date.now()}`);
|
|
7331
|
+
mkdirSync7(tmpDir, { recursive: true });
|
|
7140
7332
|
const realSlug = String(plugin.slug || slug);
|
|
7141
7333
|
try {
|
|
7142
7334
|
await downloadAndExtractMarketplaceTarball(baseUrl, realSlug, tmpDir, targetDir);
|
|
@@ -7166,14 +7358,14 @@ function isManifestArray(data) {
|
|
|
7166
7358
|
return Array.isArray(data) && data.length > 0 && typeof data[0].path === "string" && typeof data[0].content === "string";
|
|
7167
7359
|
}
|
|
7168
7360
|
function extractManifestToDir(manifest, targetDir) {
|
|
7169
|
-
if (
|
|
7361
|
+
if (existsSync12(targetDir)) {
|
|
7170
7362
|
rmSync6(targetDir, { recursive: true, force: true });
|
|
7171
7363
|
}
|
|
7172
|
-
|
|
7364
|
+
mkdirSync7(targetDir, { recursive: true });
|
|
7173
7365
|
for (const file of manifest) {
|
|
7174
|
-
const filePath =
|
|
7175
|
-
|
|
7176
|
-
|
|
7366
|
+
const filePath = resolve14(targetDir, file.path);
|
|
7367
|
+
mkdirSync7(dirname2(filePath), { recursive: true });
|
|
7368
|
+
writeFileSync9(filePath, Buffer.from(file.content, "base64"));
|
|
7177
7369
|
}
|
|
7178
7370
|
}
|
|
7179
7371
|
function tryParseAsGzippedManifest(buffer) {
|
|
@@ -7198,18 +7390,18 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
|
|
|
7198
7390
|
}
|
|
7199
7391
|
if (tarballRes.status === 302 || tarballRes.headers.get("location")) {
|
|
7200
7392
|
const redirectUrl = tarballRes.headers.get("location");
|
|
7201
|
-
const tarballPath =
|
|
7393
|
+
const tarballPath = join8(tmpDir, `${slug}.tar.gz`);
|
|
7202
7394
|
await downloadToFile(redirectUrl, tarballPath);
|
|
7203
|
-
const buffer =
|
|
7395
|
+
const buffer = readFileSync14(tarballPath);
|
|
7204
7396
|
const manifest = tryParseAsGzippedManifest(buffer);
|
|
7205
7397
|
if (manifest) {
|
|
7206
7398
|
extractManifestToDir(manifest, targetDir);
|
|
7207
7399
|
return;
|
|
7208
7400
|
}
|
|
7209
|
-
const extractDir =
|
|
7401
|
+
const extractDir = join8(tmpDir, "extracted");
|
|
7210
7402
|
extractTarGz(tarballPath, extractDir);
|
|
7211
7403
|
flattenPackageRoot(extractDir);
|
|
7212
|
-
if (
|
|
7404
|
+
if (existsSync12(targetDir)) {
|
|
7213
7405
|
rmSync6(targetDir, { recursive: true, force: true });
|
|
7214
7406
|
}
|
|
7215
7407
|
cpSync6(extractDir, targetDir, { recursive: true, force: true });
|
|
@@ -7220,13 +7412,13 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
|
|
|
7220
7412
|
extractManifestToDir(manifest, targetDir);
|
|
7221
7413
|
return;
|
|
7222
7414
|
}
|
|
7223
|
-
const tarballPath =
|
|
7224
|
-
|
|
7415
|
+
const tarballPath = join8(tmpDir, `${slug}.tar.gz`);
|
|
7416
|
+
writeFileSync9(tarballPath, buffer);
|
|
7225
7417
|
try {
|
|
7226
|
-
const extractDir =
|
|
7418
|
+
const extractDir = join8(tmpDir, "extracted");
|
|
7227
7419
|
extractTarGz(tarballPath, extractDir);
|
|
7228
7420
|
flattenPackageRoot(extractDir);
|
|
7229
|
-
if (
|
|
7421
|
+
if (existsSync12(targetDir)) {
|
|
7230
7422
|
rmSync6(targetDir, { recursive: true, force: true });
|
|
7231
7423
|
}
|
|
7232
7424
|
cpSync6(extractDir, targetDir, { recursive: true, force: true });
|
|
@@ -7261,25 +7453,25 @@ function writeMarketplacePackageJson(plugin, slug, name, baseUrl, targetDir) {
|
|
|
7261
7453
|
url: baseUrl
|
|
7262
7454
|
}
|
|
7263
7455
|
};
|
|
7264
|
-
const pkgPath =
|
|
7265
|
-
if (!
|
|
7266
|
-
|
|
7456
|
+
const pkgPath = resolve14(targetDir, "package.json");
|
|
7457
|
+
if (!existsSync12(pkgPath)) {
|
|
7458
|
+
writeFileSync9(pkgPath, JSON.stringify(packageJson, null, 2));
|
|
7267
7459
|
} else {
|
|
7268
7460
|
try {
|
|
7269
|
-
const existing = JSON.parse(
|
|
7461
|
+
const existing = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
7270
7462
|
const merged = {
|
|
7271
7463
|
...existing,
|
|
7272
7464
|
xbrowser: { ...existing.xbrowser, ...packageJson.xbrowser },
|
|
7273
7465
|
_marketplace: packageJson._marketplace
|
|
7274
7466
|
};
|
|
7275
|
-
|
|
7467
|
+
writeFileSync9(pkgPath, JSON.stringify(merged, null, 2));
|
|
7276
7468
|
} catch {
|
|
7277
|
-
|
|
7469
|
+
writeFileSync9(pkgPath, JSON.stringify(packageJson, null, 2));
|
|
7278
7470
|
}
|
|
7279
7471
|
}
|
|
7280
7472
|
}
|
|
7281
7473
|
function ensureIndexFile(plugin, name, targetDir) {
|
|
7282
|
-
if (!
|
|
7474
|
+
if (!existsSync12(resolve14(targetDir, "index.ts")) && !existsSync12(resolve14(targetDir, "index.js"))) {
|
|
7283
7475
|
const commands = plugin.commands || [];
|
|
7284
7476
|
const commandHandlers = commands.length > 0 ? commands.map((cmd) => {
|
|
7285
7477
|
return [
|
|
@@ -7294,8 +7486,8 @@ function ensureIndexFile(plugin, name, targetDir) {
|
|
|
7294
7486
|
` handler: async () => ({ data: { message: 'Hello from ${name}!' }, tips: [] }),`,
|
|
7295
7487
|
` });`
|
|
7296
7488
|
].join("\n");
|
|
7297
|
-
|
|
7298
|
-
|
|
7489
|
+
writeFileSync9(
|
|
7490
|
+
resolve14(targetDir, "index.ts"),
|
|
7299
7491
|
[
|
|
7300
7492
|
`import type { XCLIAPI } from '@dyyz1993/xcli-core';`,
|
|
7301
7493
|
``,
|
|
@@ -7316,7 +7508,7 @@ function ensureIndexFile(plugin, name, targetDir) {
|
|
|
7316
7508
|
var PluginInstaller = class {
|
|
7317
7509
|
pluginsDir;
|
|
7318
7510
|
constructor(pluginsDir) {
|
|
7319
|
-
this.pluginsDir = pluginsDir ||
|
|
7511
|
+
this.pluginsDir = pluginsDir || resolve15(homedir5(), ".xbrowser/plugins");
|
|
7320
7512
|
}
|
|
7321
7513
|
getPluginsDir() {
|
|
7322
7514
|
return this.pluginsDir;
|
|
@@ -7332,11 +7524,11 @@ var PluginInstaller = class {
|
|
|
7332
7524
|
async install(source, options) {
|
|
7333
7525
|
const type = this.detectSourceType(source);
|
|
7334
7526
|
const name = options?.name || this.deriveName(source, type);
|
|
7335
|
-
const targetDir =
|
|
7336
|
-
if (
|
|
7527
|
+
const targetDir = resolve15(this.pluginsDir, name);
|
|
7528
|
+
if (existsSync13(targetDir) && !options?.force) {
|
|
7337
7529
|
throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
|
|
7338
7530
|
}
|
|
7339
|
-
|
|
7531
|
+
mkdirSync8(targetDir, { recursive: true });
|
|
7340
7532
|
const resolvedSource = type === "npm" ? await resolveNpmPackageWithFallback(source) : source;
|
|
7341
7533
|
switch (type) {
|
|
7342
7534
|
case "local":
|
|
@@ -7393,8 +7585,8 @@ var PluginInstaller = class {
|
|
|
7393
7585
|
* @throws If the plugin is not installed.
|
|
7394
7586
|
*/
|
|
7395
7587
|
async uninstall(name) {
|
|
7396
|
-
const targetDir =
|
|
7397
|
-
if (!
|
|
7588
|
+
const targetDir = resolve15(this.pluginsDir, name);
|
|
7589
|
+
if (!existsSync13(targetDir)) {
|
|
7398
7590
|
throw new Error(`Plugin "${name}" not found`);
|
|
7399
7591
|
}
|
|
7400
7592
|
rmSync7(targetDir, { recursive: true, force: true });
|
|
@@ -7405,18 +7597,18 @@ var PluginInstaller = class {
|
|
|
7405
7597
|
* @returns Array of installed plugin information.
|
|
7406
7598
|
*/
|
|
7407
7599
|
async list(_options) {
|
|
7408
|
-
if (!
|
|
7600
|
+
if (!existsSync13(this.pluginsDir)) return [];
|
|
7409
7601
|
const entries = readdirSync3(this.pluginsDir, { withFileTypes: true });
|
|
7410
7602
|
const plugins = [];
|
|
7411
7603
|
for (const entry of entries) {
|
|
7412
7604
|
if (!entry.isDirectory()) continue;
|
|
7413
|
-
const pluginPath =
|
|
7414
|
-
const indexPath =
|
|
7415
|
-
const indexJsPath =
|
|
7416
|
-
if (!
|
|
7605
|
+
const pluginPath = resolve15(this.pluginsDir, entry.name);
|
|
7606
|
+
const indexPath = resolve15(pluginPath, "index.ts");
|
|
7607
|
+
const indexJsPath = resolve15(pluginPath, "index.js");
|
|
7608
|
+
if (!existsSync13(indexPath) && !existsSync13(indexJsPath)) continue;
|
|
7417
7609
|
const metadata = PluginMetadataParser.parseFromPackageJson(pluginPath);
|
|
7418
7610
|
let source = "local";
|
|
7419
|
-
const pkg2 = readJsonFile(
|
|
7611
|
+
const pkg2 = readJsonFile(resolve15(pluginPath, "package.json"), {});
|
|
7420
7612
|
if (pkg2._marketplace) source = "marketplace";
|
|
7421
7613
|
else if (pkg2._npmSource) source = "npm";
|
|
7422
7614
|
else if (pkg2._gitSource) source = "git";
|
|
@@ -7440,10 +7632,10 @@ var PluginInstaller = class {
|
|
|
7440
7632
|
}
|
|
7441
7633
|
if (source.startsWith("file://")) {
|
|
7442
7634
|
const filePath = decodeURIComponent(new URL(source).pathname);
|
|
7443
|
-
if (
|
|
7635
|
+
if (existsSync13(filePath)) return "url";
|
|
7444
7636
|
}
|
|
7445
7637
|
if (source.endsWith(".git") || source.includes("github.com/")) return "git";
|
|
7446
|
-
if (
|
|
7638
|
+
if (existsSync13(resolve15(source))) return "local";
|
|
7447
7639
|
return "npm";
|
|
7448
7640
|
}
|
|
7449
7641
|
deriveName(source, type) {
|
|
@@ -7519,10 +7711,6 @@ function handlePluginHelp() {
|
|
|
7519
7711
|
" uninstall <name> Uninstall a plugin",
|
|
7520
7712
|
" list [--json] List installed plugins",
|
|
7521
7713
|
" reload <name> Reload a plugin",
|
|
7522
|
-
" publish [--registry <url>] [--dry-run] Publish plugin to marketplace",
|
|
7523
|
-
" login [--token <api-key>] [--registry <url>] Login to marketplace",
|
|
7524
|
-
" whoami Show current logged-in user",
|
|
7525
|
-
" logout Logout from marketplace",
|
|
7526
7714
|
"",
|
|
7527
7715
|
"Examples:",
|
|
7528
7716
|
" xbrowser plugin search scraper",
|
|
@@ -7530,12 +7718,7 @@ function handlePluginHelp() {
|
|
|
7530
7718
|
" xbrowser plugin install ./my-plugin",
|
|
7531
7719
|
" xbrowser plugin uninstall my-plugin",
|
|
7532
7720
|
" xbrowser plugin list",
|
|
7533
|
-
" xbrowser plugin reload my-plugin"
|
|
7534
|
-
" xbrowser plugin publish",
|
|
7535
|
-
" xbrowser plugin publish --dry-run",
|
|
7536
|
-
" xbrowser plugin login --token my-api-key",
|
|
7537
|
-
" xbrowser plugin whoami",
|
|
7538
|
-
" xbrowser plugin logout"
|
|
7721
|
+
" xbrowser plugin reload my-plugin"
|
|
7539
7722
|
].join("\n");
|
|
7540
7723
|
}
|
|
7541
7724
|
var pluginInstallBuiltin = {
|
|
@@ -7837,76 +8020,60 @@ var NPMSearcher = class {
|
|
|
7837
8020
|
}
|
|
7838
8021
|
};
|
|
7839
8022
|
|
|
7840
|
-
// src/plugin
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
const
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
}
|
|
7860
|
-
const data = await response.json();
|
|
7861
|
-
if (!data.success || !data.data?.items) {
|
|
7862
|
-
return [];
|
|
7863
|
-
}
|
|
7864
|
-
return data.data.items.map((item) => this.parseMarketplacePlugin(item));
|
|
7865
|
-
} catch {
|
|
7866
|
-
return [];
|
|
7867
|
-
}
|
|
7868
|
-
}
|
|
7869
|
-
static async getPluginDetail(slug) {
|
|
7870
|
-
await ensureProxyFetch();
|
|
7871
|
-
const baseUrl = this.getBaseUrl();
|
|
7872
|
-
const url = `${baseUrl}/api/plugins/${slug}`;
|
|
7873
|
-
try {
|
|
7874
|
-
const response = await fetch(url);
|
|
7875
|
-
if (!response.ok) return null;
|
|
7876
|
-
const data = await response.json();
|
|
7877
|
-
if (!data.success || !data.data) return null;
|
|
7878
|
-
return this.parseMarketplacePlugin(data.data);
|
|
7879
|
-
} catch {
|
|
7880
|
-
return null;
|
|
7881
|
-
}
|
|
7882
|
-
}
|
|
7883
|
-
static parseMarketplacePlugin(item) {
|
|
7884
|
-
return {
|
|
8023
|
+
// src/builtins/plugin-search.ts
|
|
8024
|
+
async function searchFromMarketplacePlugin(options, loader) {
|
|
8025
|
+
const sites = loader.getCore().loader.getSites();
|
|
8026
|
+
const marketplaceSite = sites.find((s) => s.name === "marketplace");
|
|
8027
|
+
if (!marketplaceSite) return [];
|
|
8028
|
+
const searchCmd = marketplaceSite.getCommand("search");
|
|
8029
|
+
if (!searchCmd) return [];
|
|
8030
|
+
try {
|
|
8031
|
+
const result = await searchCmd.handler(
|
|
8032
|
+
{
|
|
8033
|
+
query: options.query,
|
|
8034
|
+
tag: options.tag,
|
|
8035
|
+
site: options.site,
|
|
8036
|
+
limit: options.limit
|
|
8037
|
+
},
|
|
8038
|
+
{}
|
|
8039
|
+
);
|
|
8040
|
+
const items = extractItems(result);
|
|
8041
|
+
return items.map((item) => ({
|
|
7885
8042
|
source: "marketplace",
|
|
7886
|
-
slug: String(item.slug || ""),
|
|
7887
8043
|
name: String(item.name || ""),
|
|
7888
8044
|
version: String(item.version || "latest"),
|
|
7889
8045
|
description: String(item.description || ""),
|
|
7890
|
-
author: String(item.
|
|
7891
|
-
homepage: typeof item.
|
|
7892
|
-
repository: typeof item.repositoryUrl === "string" ? item.repositoryUrl : void 0,
|
|
7893
|
-
keywords: Array.isArray(item.tags) ? item.tags.map(String) : [],
|
|
8046
|
+
author: String(item.author || ""),
|
|
8047
|
+
homepage: typeof item.homepage === "string" ? item.homepage : void 0,
|
|
7894
8048
|
tags: Array.isArray(item.tags) ? item.tags.map(String) : [],
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
8049
|
+
downloads: typeof item.downloads === "number" ? item.downloads : 0,
|
|
8050
|
+
slug: String(item.slug || ""),
|
|
8051
|
+
commands: Array.isArray(item.commands) ? item.commands.map(String) : []
|
|
8052
|
+
}));
|
|
8053
|
+
} catch {
|
|
8054
|
+
return [];
|
|
7900
8055
|
}
|
|
7901
|
-
}
|
|
7902
|
-
|
|
7903
|
-
|
|
8056
|
+
}
|
|
8057
|
+
function extractItems(result) {
|
|
8058
|
+
if (!result || typeof result !== "object") return [];
|
|
8059
|
+
const r = result;
|
|
8060
|
+
if ("data" in r) {
|
|
8061
|
+
const data = r.data;
|
|
8062
|
+
if (data && "items" in data && Array.isArray(data.items)) {
|
|
8063
|
+
return data.items;
|
|
8064
|
+
}
|
|
8065
|
+
}
|
|
8066
|
+
if ("items" in r && Array.isArray(r.items)) {
|
|
8067
|
+
return r.items;
|
|
8068
|
+
}
|
|
8069
|
+
return [];
|
|
8070
|
+
}
|
|
7904
8071
|
var pluginSearchBuiltin = {
|
|
7905
8072
|
name: "plugin search",
|
|
7906
8073
|
description: "Search for xbrowser plugins on npm registry and marketplace",
|
|
7907
8074
|
help: {
|
|
7908
8075
|
usage: "xbrowser plugin search <query> [options]",
|
|
7909
|
-
description: "Search npm registry and
|
|
8076
|
+
description: "Search npm registry and installed plugin search providers for xbrowser-compatible plugins",
|
|
7910
8077
|
options: [
|
|
7911
8078
|
{ name: "--tag <tag>", description: "Filter by plugin tag" },
|
|
7912
8079
|
{ name: "--site <site>", description: "Filter by target site" },
|
|
@@ -7928,26 +8095,24 @@ var pluginSearchBuiltin = {
|
|
|
7928
8095
|
limit: options["limit"] ? Number.parseInt(String(options["limit"])) : 20
|
|
7929
8096
|
};
|
|
7930
8097
|
console.log(
|
|
7931
|
-
`Searching
|
|
8098
|
+
`Searching for xbrowser plugins...${query ? ` (query: "${query}")` : ""}`
|
|
7932
8099
|
);
|
|
7933
|
-
const
|
|
8100
|
+
const loader = await getPluginLoader();
|
|
8101
|
+
const [npmSettled, pluginSettled] = await Promise.allSettled([
|
|
7934
8102
|
NPMSearcher.search(searchOptions),
|
|
7935
|
-
|
|
8103
|
+
searchFromMarketplacePlugin(searchOptions, loader)
|
|
7936
8104
|
]);
|
|
7937
8105
|
const npmResults = npmSettled.status === "fulfilled" ? npmSettled.value : [];
|
|
7938
|
-
const
|
|
8106
|
+
const pluginResults = pluginSettled.status === "fulfilled" ? pluginSettled.value : [];
|
|
7939
8107
|
if (npmSettled.status === "rejected") {
|
|
7940
8108
|
console.warn(`Warning: npm search failed: ${npmSettled.reason}`);
|
|
7941
8109
|
}
|
|
7942
|
-
|
|
7943
|
-
console.warn(`Warning: marketplace search failed: ${marketplaceSettled.reason}`);
|
|
7944
|
-
}
|
|
7945
|
-
const total = npmResults.length + marketplaceResults.length;
|
|
8110
|
+
const total = npmResults.length + pluginResults.length;
|
|
7946
8111
|
if (total === 0) {
|
|
7947
8112
|
console.log("No plugins found.");
|
|
7948
8113
|
return;
|
|
7949
8114
|
}
|
|
7950
|
-
console.log(`Found ${total} plugin(s) (npm: ${npmResults.length},
|
|
8115
|
+
console.log(`Found ${total} plugin(s) (npm: ${npmResults.length}, plugins: ${pluginResults.length}):
|
|
7951
8116
|
`);
|
|
7952
8117
|
if (npmResults.length > 0) {
|
|
7953
8118
|
console.log("--- npm ---\n");
|
|
@@ -7971,14 +8136,14 @@ var pluginSearchBuiltin = {
|
|
|
7971
8136
|
console.log("");
|
|
7972
8137
|
});
|
|
7973
8138
|
}
|
|
7974
|
-
if (
|
|
8139
|
+
if (pluginResults.length > 0) {
|
|
7975
8140
|
console.log("--- marketplace ---\n");
|
|
7976
|
-
|
|
8141
|
+
pluginResults.forEach((result, idx) => {
|
|
7977
8142
|
console.log(`${idx + 1}. ${result.name} [marketplace]`);
|
|
7978
8143
|
console.log(` ${result.description}`);
|
|
7979
8144
|
console.log(` Version: ${result.version}`);
|
|
7980
|
-
console.log(` Author: ${result.author}`);
|
|
7981
|
-
console.log(` Downloads: ${result.downloads}`);
|
|
8145
|
+
if (result.author) console.log(` Author: ${result.author}`);
|
|
8146
|
+
if (result.downloads) console.log(` Downloads: ${result.downloads}`);
|
|
7982
8147
|
if (result.tags && result.tags.length > 0) {
|
|
7983
8148
|
console.log(` Tags: ${result.tags.join(", ")}`);
|
|
7984
8149
|
}
|
|
@@ -7988,7 +8153,9 @@ var pluginSearchBuiltin = {
|
|
|
7988
8153
|
if (result.homepage) {
|
|
7989
8154
|
console.log(` Homepage: ${result.homepage}`);
|
|
7990
8155
|
}
|
|
7991
|
-
|
|
8156
|
+
if (result.slug) {
|
|
8157
|
+
console.log(` Install: xbrowser plugin install ${result.slug} --from-marketplace`);
|
|
8158
|
+
}
|
|
7992
8159
|
console.log("");
|
|
7993
8160
|
});
|
|
7994
8161
|
}
|
|
@@ -8758,15 +8925,15 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
8758
8925
|
}
|
|
8759
8926
|
|
|
8760
8927
|
// src/cli/session-routes.ts
|
|
8761
|
-
import { homedir as
|
|
8762
|
-
import { join as
|
|
8928
|
+
import { homedir as homedir6 } from "os";
|
|
8929
|
+
import { join as join10 } from "path";
|
|
8763
8930
|
import { readdirSync as readdirSync4, rmSync as rmSync8 } from "fs";
|
|
8764
8931
|
function cleanSessionFiles() {
|
|
8765
|
-
const dir =
|
|
8932
|
+
const dir = join10(homedir6(), ".xbrowser", "sessions");
|
|
8766
8933
|
let count = 0;
|
|
8767
8934
|
try {
|
|
8768
8935
|
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
8769
|
-
const p =
|
|
8936
|
+
const p = join10(dir, entry.name);
|
|
8770
8937
|
rmSync8(p, { recursive: true, force: true });
|
|
8771
8938
|
count++;
|
|
8772
8939
|
}
|
|
@@ -8869,6 +9036,67 @@ function applyRegistryOverride(options) {
|
|
|
8869
9036
|
process.env.XBROWSER_MARKETPLACE_URL = registry2;
|
|
8870
9037
|
}
|
|
8871
9038
|
}
|
|
9039
|
+
function extractItems2(result) {
|
|
9040
|
+
if (!result || typeof result !== "object") return [];
|
|
9041
|
+
const r = result;
|
|
9042
|
+
if ("data" in r) {
|
|
9043
|
+
const data = r.data;
|
|
9044
|
+
if (data && "items" in data && Array.isArray(data.items)) {
|
|
9045
|
+
return data.items;
|
|
9046
|
+
}
|
|
9047
|
+
}
|
|
9048
|
+
if ("items" in r && Array.isArray(r.items)) {
|
|
9049
|
+
return r.items;
|
|
9050
|
+
}
|
|
9051
|
+
return [];
|
|
9052
|
+
}
|
|
9053
|
+
async function searchFromMarketplacePlugin2(options, loader) {
|
|
9054
|
+
const sites = loader.getCore().loader.getSites();
|
|
9055
|
+
const marketplaceSite = sites.find((s) => s.name === "marketplace");
|
|
9056
|
+
if (!marketplaceSite) return [];
|
|
9057
|
+
const searchCmd = marketplaceSite.getCommand("search");
|
|
9058
|
+
if (!searchCmd) return [];
|
|
9059
|
+
try {
|
|
9060
|
+
const result = await searchCmd.handler(
|
|
9061
|
+
{
|
|
9062
|
+
query: options.query,
|
|
9063
|
+
tag: options.tag,
|
|
9064
|
+
site: options.site,
|
|
9065
|
+
limit: options.limit
|
|
9066
|
+
},
|
|
9067
|
+
{}
|
|
9068
|
+
);
|
|
9069
|
+
const items = extractItems2(result);
|
|
9070
|
+
return items.map((item) => ({ ...item, source: "marketplace" }));
|
|
9071
|
+
} catch {
|
|
9072
|
+
return [];
|
|
9073
|
+
}
|
|
9074
|
+
}
|
|
9075
|
+
async function infoFromMarketplacePlugin(slug, loader) {
|
|
9076
|
+
const sites = loader.getCore().loader.getSites();
|
|
9077
|
+
const marketplaceSite = sites.find((s) => s.name === "marketplace");
|
|
9078
|
+
if (!marketplaceSite) return null;
|
|
9079
|
+
const infoCmd = marketplaceSite.getCommand("info");
|
|
9080
|
+
if (!infoCmd) return null;
|
|
9081
|
+
try {
|
|
9082
|
+
const result = await infoCmd.handler({ slug }, {});
|
|
9083
|
+
if (!result || typeof result !== "object") return null;
|
|
9084
|
+
const r = result;
|
|
9085
|
+
let plugin = null;
|
|
9086
|
+
if ("data" in r) {
|
|
9087
|
+
const data = r.data;
|
|
9088
|
+
plugin = data?.plugin || null;
|
|
9089
|
+
}
|
|
9090
|
+
if (!plugin && "plugin" in r) {
|
|
9091
|
+
plugin = r.plugin;
|
|
9092
|
+
}
|
|
9093
|
+
if (plugin && plugin.name) {
|
|
9094
|
+
return { ...plugin, source: "marketplace" };
|
|
9095
|
+
}
|
|
9096
|
+
} catch {
|
|
9097
|
+
}
|
|
9098
|
+
return null;
|
|
9099
|
+
}
|
|
8872
9100
|
async function handleSearch(args, options, mode) {
|
|
8873
9101
|
const query = args[0] || "";
|
|
8874
9102
|
applyRegistryOverride(options);
|
|
@@ -8876,11 +9104,10 @@ async function handleSearch(args, options, mode) {
|
|
|
8876
9104
|
const searchLimit = options.limit ? Number(options.limit) : 20;
|
|
8877
9105
|
const searchOpts = { query, tag: options.tag, site: options.site, limit: searchLimit };
|
|
8878
9106
|
const results = [];
|
|
8879
|
-
const
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
if (marketplaceResults.length === 0) {
|
|
9107
|
+
const loader = await getPluginLoader();
|
|
9108
|
+
const pluginResults = await searchFromMarketplacePlugin2(searchOpts, loader);
|
|
9109
|
+
results.push(...pluginResults);
|
|
9110
|
+
if (pluginResults.length === 0) {
|
|
8884
9111
|
try {
|
|
8885
9112
|
const npmResults = await NPMSearcher.search(searchOpts);
|
|
8886
9113
|
for (const r of npmResults) {
|
|
@@ -8913,12 +9140,11 @@ async function handlePluginInfo(args, options, mode) {
|
|
|
8913
9140
|
if (!slug) outputError("Usage: xbrowser plugin info <slug>");
|
|
8914
9141
|
applyRegistryOverride(options);
|
|
8915
9142
|
await ensureProxyFetch();
|
|
8916
|
-
const
|
|
9143
|
+
const loader = await getPluginLoader();
|
|
8917
9144
|
try {
|
|
8918
|
-
const
|
|
8919
|
-
if (
|
|
8920
|
-
const
|
|
8921
|
-
const d = raw.data || raw;
|
|
9145
|
+
const pluginInfo = await infoFromMarketplacePlugin(slug, loader);
|
|
9146
|
+
if (pluginInfo) {
|
|
9147
|
+
const d = pluginInfo;
|
|
8922
9148
|
if (mode === "json") {
|
|
8923
9149
|
outputResult({ source: "marketplace", ...d }, mode);
|
|
8924
9150
|
return;
|
|
@@ -8926,12 +9152,11 @@ async function handlePluginInfo(args, options, mode) {
|
|
|
8926
9152
|
console.log(`\u540D\u79F0: ${d.name || ""}`);
|
|
8927
9153
|
console.log(`\u7248\u672C: ${d.version || ""}`);
|
|
8928
9154
|
console.log(`\u63CF\u8FF0: ${d.description || ""}`);
|
|
8929
|
-
console.log(`\u4F5C\u8005: ${d.
|
|
8930
|
-
console.log(`\u72B6\u6001: ${d.status || ""}`);
|
|
9155
|
+
console.log(`\u4F5C\u8005: ${d.author || ""}`);
|
|
8931
9156
|
console.log(`\u547D\u4EE4: ${(d.commands || []).join(", ")}`);
|
|
8932
|
-
console.log(`\u4E0B\u8F7D\u91CF: ${d.
|
|
9157
|
+
console.log(`\u4E0B\u8F7D\u91CF: ${d.downloads || 0}`);
|
|
8933
9158
|
console.log(`\u6807\u7B7E: ${(d.tags || []).join(", ")}`);
|
|
8934
|
-
console.log(`\u7F51\u7AD9: ${(d.
|
|
9159
|
+
console.log(`\u7F51\u7AD9: ${(d.sites || []).join(", ")}`);
|
|
8935
9160
|
return;
|
|
8936
9161
|
}
|
|
8937
9162
|
} catch {
|
|
@@ -9434,7 +9659,7 @@ async function handleFilter(args, _mode) {
|
|
|
9434
9659
|
|
|
9435
9660
|
// src/stdin.ts
|
|
9436
9661
|
import { createInterface } from "readline";
|
|
9437
|
-
import { readFileSync as
|
|
9662
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
9438
9663
|
async function readStdin2() {
|
|
9439
9664
|
if (process.stdin.isTTY) return [];
|
|
9440
9665
|
const lines = [];
|
|
@@ -9448,7 +9673,7 @@ async function readStdin2() {
|
|
|
9448
9673
|
return lines;
|
|
9449
9674
|
}
|
|
9450
9675
|
function readCommandFile(filePath) {
|
|
9451
|
-
const content =
|
|
9676
|
+
const content = readFileSync15(filePath, "utf-8");
|
|
9452
9677
|
return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
9453
9678
|
}
|
|
9454
9679
|
|
|
@@ -9880,24 +10105,6 @@ Commands:
|
|
|
9880
10105
|
viewer [--name <n>] [--selector <sel>] Generate viewer URL
|
|
9881
10106
|
help Show this help
|
|
9882
10107
|
--version, -v Show version
|
|
9883
|
-
|
|
9884
|
-
Marketplace & Admin:
|
|
9885
|
-
marketplace publish [--dry-run] Publish plugin to marketplace
|
|
9886
|
-
marketplace login [--token <key>] Login to marketplace
|
|
9887
|
-
marketplace register Register developer account
|
|
9888
|
-
marketplace whoami Show current user
|
|
9889
|
-
marketplace logout Logout from marketplace
|
|
9890
|
-
admin pending List pending plugins (admin only)
|
|
9891
|
-
admin approve <slug> Approve a plugin (admin only)
|
|
9892
|
-
admin reject <slug> [--reason] Reject a plugin (admin only)
|
|
9893
|
-
admin feature <slug> Toggle featured (admin only)
|
|
9894
|
-
admin remove <slug> Remove a plugin (admin only)
|
|
9895
|
-
admin stats Dashboard stats (admin only)
|
|
9896
|
-
admin inventory Plugin inventory (admin only)
|
|
9897
|
-
admin list [--status <status>] List all plugins (admin only)
|
|
9898
|
-
admin bulk-approve <slugs...> Bulk approve (admin only)
|
|
9899
|
-
admin cleanup Reset data (admin only)
|
|
9900
|
-
|
|
9901
10108
|
Plugin Commands:
|
|
9902
10109
|
Installed plugins provide additional commands.
|
|
9903
10110
|
Use 'xbrowser plugin list' to see installed plugins and their commands.
|
|
@@ -10260,19 +10467,19 @@ function headersToObject(headers) {
|
|
|
10260
10467
|
return result;
|
|
10261
10468
|
}
|
|
10262
10469
|
function readBody(req) {
|
|
10263
|
-
return new Promise((
|
|
10470
|
+
return new Promise((resolve16, reject) => {
|
|
10264
10471
|
const chunks = [];
|
|
10265
10472
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
10266
10473
|
req.on("end", () => {
|
|
10267
10474
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
10268
10475
|
if (!raw) {
|
|
10269
|
-
|
|
10476
|
+
resolve16(null);
|
|
10270
10477
|
return;
|
|
10271
10478
|
}
|
|
10272
10479
|
try {
|
|
10273
|
-
|
|
10480
|
+
resolve16(JSON.parse(raw));
|
|
10274
10481
|
} catch {
|
|
10275
|
-
|
|
10482
|
+
resolve16(null);
|
|
10276
10483
|
}
|
|
10277
10484
|
});
|
|
10278
10485
|
req.on("error", reject);
|
|
@@ -10317,7 +10524,7 @@ var HTTPServer = class {
|
|
|
10317
10524
|
res.end(JSON.stringify({ error: "INTERNAL_ERROR", message, statusCode: 500 }));
|
|
10318
10525
|
});
|
|
10319
10526
|
});
|
|
10320
|
-
return new Promise((
|
|
10527
|
+
return new Promise((resolve16, reject) => {
|
|
10321
10528
|
const server = this.server;
|
|
10322
10529
|
server.on("error", (err) => {
|
|
10323
10530
|
this.server = null;
|
|
@@ -10329,7 +10536,7 @@ var HTTPServer = class {
|
|
|
10329
10536
|
this.port = addr.port;
|
|
10330
10537
|
}
|
|
10331
10538
|
console.log(`HTTP server listening on http://${this.host}:${this.port}`);
|
|
10332
|
-
|
|
10539
|
+
resolve16({ port: this.port, host: this.host });
|
|
10333
10540
|
});
|
|
10334
10541
|
});
|
|
10335
10542
|
}
|
|
@@ -10340,13 +10547,13 @@ var HTTPServer = class {
|
|
|
10340
10547
|
*/
|
|
10341
10548
|
async stop() {
|
|
10342
10549
|
if (!this.server) return;
|
|
10343
|
-
return new Promise((
|
|
10550
|
+
return new Promise((resolve16, reject) => {
|
|
10344
10551
|
this.server.close((err) => {
|
|
10345
10552
|
if (err) {
|
|
10346
10553
|
reject(err);
|
|
10347
10554
|
} else {
|
|
10348
10555
|
this.server = null;
|
|
10349
|
-
|
|
10556
|
+
resolve16();
|
|
10350
10557
|
}
|
|
10351
10558
|
});
|
|
10352
10559
|
});
|