@xbrowser/cli 1.2.1 → 1.2.2
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-XYXCS7JW.js → chunk-JPSFUFPG.js} +4 -2
- package/dist/chunk-PHBK3TRN.js +436 -0
- package/dist/cli.js +22 -8
- package/dist/{daemon-client-R4QWHD7V.js → daemon-client-COJQESU2.js} +4 -2
- package/dist/{daemon-client-ZHO6NG36.js → daemon-client-XXKMJZZ7.js} +1 -1
- package/dist/daemon-main.js +102 -495
- package/dist/index.js +22 -8
- package/dist/plugin-singleton-ZBVTWEYK.js +9 -0
- package/package.json +1 -1
package/dist/daemon-main.js
CHANGED
|
@@ -24,6 +24,9 @@ import {
|
|
|
24
24
|
} from "./chunk-SLQR57XZ.js";
|
|
25
25
|
import "./chunk-QFROODUU.js";
|
|
26
26
|
import "./chunk-TNEN6VQ2.js";
|
|
27
|
+
import {
|
|
28
|
+
getPluginLoader
|
|
29
|
+
} from "./chunk-PHBK3TRN.js";
|
|
27
30
|
import {
|
|
28
31
|
getDaemonConfig,
|
|
29
32
|
getDaemonProcessStatus
|
|
@@ -34,15 +37,15 @@ import {
|
|
|
34
37
|
import "./chunk-KFQGP6VL.js";
|
|
35
38
|
|
|
36
39
|
// src/daemon/daemon-main.ts
|
|
37
|
-
import { writeFileSync as
|
|
38
|
-
import { join as
|
|
39
|
-
import { homedir as
|
|
40
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, appendFileSync, unlinkSync } from "fs";
|
|
41
|
+
import { join as join8 } from "path";
|
|
42
|
+
import { homedir as homedir8 } from "os";
|
|
40
43
|
import { startHttpServer } from "@dyyz1993/xcli-core";
|
|
41
44
|
|
|
42
45
|
// src/daemon/rpc-handlers.ts
|
|
43
|
-
import { writeFileSync as
|
|
44
|
-
import { join as
|
|
45
|
-
import { homedir as
|
|
46
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5 } from "fs";
|
|
47
|
+
import { join as join7 } from "path";
|
|
48
|
+
import { homedir as homedir7 } from "os";
|
|
46
49
|
import {
|
|
47
50
|
createSessionMeta,
|
|
48
51
|
removeSession
|
|
@@ -449,15 +452,15 @@ var clickCommand = registerCommand({
|
|
|
449
452
|
let detectedNewPage;
|
|
450
453
|
let cleanup;
|
|
451
454
|
if (ctx.browserContext?.on) {
|
|
452
|
-
const pagePromise = new Promise((
|
|
455
|
+
const pagePromise = new Promise((resolve) => {
|
|
453
456
|
const timer = setTimeout(() => {
|
|
454
457
|
ctx.browserContext.off("page", handler);
|
|
455
|
-
|
|
458
|
+
resolve(void 0);
|
|
456
459
|
}, 3e3);
|
|
457
460
|
const handler = (page2) => {
|
|
458
461
|
clearTimeout(timer);
|
|
459
462
|
ctx.browserContext.off("page", handler);
|
|
460
|
-
|
|
463
|
+
resolve(page2);
|
|
461
464
|
};
|
|
462
465
|
ctx.browserContext.on("page", handler);
|
|
463
466
|
});
|
|
@@ -907,10 +910,10 @@ var setCookieCommand = registerCommand({
|
|
|
907
910
|
description: "Set a cookie",
|
|
908
911
|
scope: "page",
|
|
909
912
|
parameters: z8.object({
|
|
910
|
-
name: z8.string(),
|
|
911
|
-
value: z8.string(),
|
|
912
|
-
domain: z8.string().optional(),
|
|
913
|
-
path: z8.string().optional(),
|
|
913
|
+
name: z8.coerce.string(),
|
|
914
|
+
value: z8.coerce.string(),
|
|
915
|
+
domain: z8.coerce.string().optional(),
|
|
916
|
+
path: z8.coerce.string().optional(),
|
|
914
917
|
expires: z8.number().optional(),
|
|
915
918
|
httpOnly: z8.boolean().optional(),
|
|
916
919
|
secure: z8.boolean().optional(),
|
|
@@ -1255,7 +1258,7 @@ var consoleCheckCommand = registerCommand({
|
|
|
1255
1258
|
await page.goto(p.url, { waitUntil: "domcontentloaded" });
|
|
1256
1259
|
}
|
|
1257
1260
|
const messages = await page.evaluate((args) => {
|
|
1258
|
-
return new Promise((
|
|
1261
|
+
return new Promise((resolve) => {
|
|
1259
1262
|
const collected = [];
|
|
1260
1263
|
const originalConsole = {
|
|
1261
1264
|
log: console.log,
|
|
@@ -1322,7 +1325,7 @@ ${a.stack || ""}`;
|
|
|
1322
1325
|
console.warn = originalConsole.warn;
|
|
1323
1326
|
console.error = originalConsole.error;
|
|
1324
1327
|
console.info = originalConsole.info;
|
|
1325
|
-
|
|
1328
|
+
resolve(collected);
|
|
1326
1329
|
}, args.duration);
|
|
1327
1330
|
});
|
|
1328
1331
|
}, { duration: p.duration });
|
|
@@ -1748,7 +1751,7 @@ async function executeAction(page, action) {
|
|
|
1748
1751
|
if (action.selector) {
|
|
1749
1752
|
await page.waitForSelector(action.selector, { timeout: 3e4 });
|
|
1750
1753
|
} else if (action.milliseconds) {
|
|
1751
|
-
await new Promise((
|
|
1754
|
+
await new Promise((resolve) => setTimeout(resolve, action.milliseconds));
|
|
1752
1755
|
} else {
|
|
1753
1756
|
throw new Error("wait action requires either milliseconds or selector");
|
|
1754
1757
|
}
|
|
@@ -1837,8 +1840,8 @@ var actionsCommand = registerCommand({
|
|
|
1837
1840
|
results.push(result);
|
|
1838
1841
|
}
|
|
1839
1842
|
})();
|
|
1840
|
-
const timeoutPromise = new Promise((
|
|
1841
|
-
setTimeout(
|
|
1843
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
1844
|
+
setTimeout(resolve, timeoutMs);
|
|
1842
1845
|
});
|
|
1843
1846
|
await Promise.race([executionPromise, timeoutPromise]);
|
|
1844
1847
|
const title = await ctx.page.title();
|
|
@@ -2558,11 +2561,11 @@ async function navigateForMap(page, url, timeout = 15e3) {
|
|
|
2558
2561
|
}
|
|
2559
2562
|
async function extractPageLinks(page, baseUrl) {
|
|
2560
2563
|
await navigateForMap(page, baseUrl);
|
|
2561
|
-
await new Promise((
|
|
2564
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
2562
2565
|
await page.evaluate(() => {
|
|
2563
2566
|
window.scrollTo(0, document.body.scrollHeight);
|
|
2564
2567
|
});
|
|
2565
|
-
await new Promise((
|
|
2568
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
2566
2569
|
const origin = new URL(baseUrl).origin;
|
|
2567
2570
|
const rawLinks = await page.evaluate((evalOrigin) => {
|
|
2568
2571
|
return Array.from(document.querySelectorAll("a[href]")).map((a) => {
|
|
@@ -4420,25 +4423,25 @@ aria snapshot\uFF1A
|
|
|
4420
4423
|
async function analyzeWithLLM(ariaSnapshot) {
|
|
4421
4424
|
const piBin = process.env.PI_CLI_PATH || "pi";
|
|
4422
4425
|
const prompt = LLM_PROMPT.replace("{snapshot}", ariaSnapshot.slice(0, 4e3));
|
|
4423
|
-
return new Promise((
|
|
4426
|
+
return new Promise((resolve) => {
|
|
4424
4427
|
execFile(
|
|
4425
4428
|
piBin,
|
|
4426
4429
|
["--provider", LLM_PROVIDER, "--model", LLM_MODEL, prompt],
|
|
4427
4430
|
{ timeout: LLM_TIMEOUT_MS, maxBuffer: 1024 * 1024 },
|
|
4428
4431
|
(err, stdout, _stderr) => {
|
|
4429
4432
|
if (err) {
|
|
4430
|
-
|
|
4433
|
+
resolve(null);
|
|
4431
4434
|
return;
|
|
4432
4435
|
}
|
|
4433
4436
|
const output = (stdout || "").trim();
|
|
4434
4437
|
if (!output) {
|
|
4435
|
-
|
|
4438
|
+
resolve(null);
|
|
4436
4439
|
return;
|
|
4437
4440
|
}
|
|
4438
4441
|
try {
|
|
4439
4442
|
const parsed = parse(output);
|
|
4440
4443
|
if (!parsed || typeof parsed !== "object") {
|
|
4441
|
-
|
|
4444
|
+
resolve(null);
|
|
4442
4445
|
return;
|
|
4443
4446
|
}
|
|
4444
4447
|
const elements = {};
|
|
@@ -4453,9 +4456,9 @@ async function analyzeWithLLM(ariaSnapshot) {
|
|
|
4453
4456
|
};
|
|
4454
4457
|
}
|
|
4455
4458
|
}
|
|
4456
|
-
|
|
4459
|
+
resolve(Object.keys(elements).length > 0 ? elements : null);
|
|
4457
4460
|
} catch {
|
|
4458
|
-
|
|
4461
|
+
resolve(null);
|
|
4459
4462
|
}
|
|
4460
4463
|
}
|
|
4461
4464
|
);
|
|
@@ -4810,7 +4813,7 @@ async function pollUntil(timeout, pollInterval, predicate) {
|
|
|
4810
4813
|
const startedAt = Date.now();
|
|
4811
4814
|
while (Date.now() - startedAt <= timeout) {
|
|
4812
4815
|
if (await predicate()) return true;
|
|
4813
|
-
await new Promise((
|
|
4816
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
4814
4817
|
}
|
|
4815
4818
|
return false;
|
|
4816
4819
|
}
|
|
@@ -5416,11 +5419,11 @@ function resolveScriptContent(params) {
|
|
|
5416
5419
|
async function readStdin() {
|
|
5417
5420
|
const { createReadStream } = await import("fs");
|
|
5418
5421
|
const { createInterface } = await import("readline");
|
|
5419
|
-
return new Promise((
|
|
5422
|
+
return new Promise((resolve, reject) => {
|
|
5420
5423
|
const lines = [];
|
|
5421
5424
|
const rl = createInterface({ input: createReadStream("/dev/stdin") });
|
|
5422
5425
|
rl.on("line", (line) => lines.push(line));
|
|
5423
|
-
rl.on("close", () =>
|
|
5426
|
+
rl.on("close", () => resolve(lines.join("\n")));
|
|
5424
5427
|
rl.on("error", reject);
|
|
5425
5428
|
});
|
|
5426
5429
|
}
|
|
@@ -5888,434 +5891,6 @@ function formatDetectionMessage(result) {
|
|
|
5888
5891
|
Action: ${action}`;
|
|
5889
5892
|
}
|
|
5890
5893
|
|
|
5891
|
-
// src/plugin/loader.ts
|
|
5892
|
-
import {
|
|
5893
|
-
Core
|
|
5894
|
-
} from "@dyyz1993/xcli-core";
|
|
5895
|
-
import { resolve as resolve2 } from "path";
|
|
5896
|
-
import { existsSync as existsSync4, readdirSync } from "fs";
|
|
5897
|
-
import { homedir as homedir5 } from "os";
|
|
5898
|
-
|
|
5899
|
-
// src/plugin/metadata-parser.ts
|
|
5900
|
-
import { existsSync as existsSync2 } from "fs";
|
|
5901
|
-
import { resolve } from "path";
|
|
5902
|
-
|
|
5903
|
-
// src/utils/json-file.ts
|
|
5904
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
5905
|
-
function readJsonFile(filePath, defaultValue) {
|
|
5906
|
-
try {
|
|
5907
|
-
const content = readFileSync3(filePath, "utf-8");
|
|
5908
|
-
return JSON.parse(content);
|
|
5909
|
-
} catch {
|
|
5910
|
-
return defaultValue;
|
|
5911
|
-
}
|
|
5912
|
-
}
|
|
5913
|
-
|
|
5914
|
-
// src/plugin/metadata-parser.ts
|
|
5915
|
-
var PluginMetadataParser = class {
|
|
5916
|
-
static XBROWSER_KEYWORDS = ["xbrowser", "xbrowser-plugin"];
|
|
5917
|
-
static parseFromPackageJson(pluginPath) {
|
|
5918
|
-
const packageJsonPath = resolve(pluginPath, "package.json");
|
|
5919
|
-
if (!existsSync2(packageJsonPath)) {
|
|
5920
|
-
return null;
|
|
5921
|
-
}
|
|
5922
|
-
const packageJson = readJsonFile(packageJsonPath, null);
|
|
5923
|
-
if (!packageJson) return null;
|
|
5924
|
-
if (!packageJson.xbrowser) {
|
|
5925
|
-
return null;
|
|
5926
|
-
}
|
|
5927
|
-
const xbrowser = packageJson.xbrowser;
|
|
5928
|
-
const metadata = {
|
|
5929
|
-
id: xbrowser.id || packageJson.name,
|
|
5930
|
-
name: xbrowser.name || packageJson.name,
|
|
5931
|
-
description: xbrowser.description || packageJson.description || "",
|
|
5932
|
-
version: xbrowser.version || packageJson.version || "1.0.0",
|
|
5933
|
-
author: xbrowser.author || this.extractAuthor(packageJson.author),
|
|
5934
|
-
homepage: xbrowser.homepage || packageJson.homepage,
|
|
5935
|
-
commands: xbrowser.commands,
|
|
5936
|
-
sites: xbrowser.sites,
|
|
5937
|
-
tags: xbrowser.tags,
|
|
5938
|
-
screenshot: xbrowser.screenshot,
|
|
5939
|
-
license: xbrowser.license || packageJson.license
|
|
5940
|
-
};
|
|
5941
|
-
return metadata;
|
|
5942
|
-
}
|
|
5943
|
-
static isXBrowserPlugin(packageJson) {
|
|
5944
|
-
if (packageJson.xbrowser) {
|
|
5945
|
-
return true;
|
|
5946
|
-
}
|
|
5947
|
-
const keywords = packageJson.keywords;
|
|
5948
|
-
if (!keywords) return false;
|
|
5949
|
-
return this.XBROWSER_KEYWORDS.some((kw) => keywords.includes(kw));
|
|
5950
|
-
}
|
|
5951
|
-
static fromNPMResult(result) {
|
|
5952
|
-
const author = typeof result.author === "string" ? result.author : result.author?.name || "Unknown";
|
|
5953
|
-
return {
|
|
5954
|
-
id: result.name,
|
|
5955
|
-
name: result.name.replace(/^xbrowser-plugin-/, "").replace(/^@[^/]+\//, ""),
|
|
5956
|
-
description: result.description || "",
|
|
5957
|
-
version: result.version,
|
|
5958
|
-
author,
|
|
5959
|
-
homepage: result.homepage || result.links?.homepage,
|
|
5960
|
-
tags: result.keywords,
|
|
5961
|
-
license: ""
|
|
5962
|
-
};
|
|
5963
|
-
}
|
|
5964
|
-
static extractAuthor(author) {
|
|
5965
|
-
if (typeof author === "string") return author;
|
|
5966
|
-
if (typeof author === "object" && author !== null) {
|
|
5967
|
-
const authorObj = author;
|
|
5968
|
-
return authorObj.name || "Unknown";
|
|
5969
|
-
}
|
|
5970
|
-
return "Unknown";
|
|
5971
|
-
}
|
|
5972
|
-
static validateMetadata(metadata) {
|
|
5973
|
-
const errors = [];
|
|
5974
|
-
if (!metadata.id) errors.push("id is required");
|
|
5975
|
-
if (!metadata.name) errors.push("name is required");
|
|
5976
|
-
if (!metadata.description) errors.push("description is required");
|
|
5977
|
-
if (!metadata.version) errors.push("version is required");
|
|
5978
|
-
return errors;
|
|
5979
|
-
}
|
|
5980
|
-
};
|
|
5981
|
-
|
|
5982
|
-
// src/plugin/ensure-deps.ts
|
|
5983
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
5984
|
-
import { join as join5 } from "path";
|
|
5985
|
-
import { execSync } from "child_process";
|
|
5986
|
-
var SHARED_PLUGIN_DEPENDENCIES = {
|
|
5987
|
-
"zod": "^3.24.0",
|
|
5988
|
-
"@dyyz1993/xcli-core": "^0.12.1"
|
|
5989
|
-
};
|
|
5990
|
-
function ensurePluginDependencies(pluginsDir) {
|
|
5991
|
-
const zodPath = join5(pluginsDir, "node_modules", "zod");
|
|
5992
|
-
if (existsSync3(zodPath)) return;
|
|
5993
|
-
mkdirSync4(pluginsDir, { recursive: true });
|
|
5994
|
-
const pkgPath = join5(pluginsDir, "package.json");
|
|
5995
|
-
let pkg = {};
|
|
5996
|
-
if (existsSync3(pkgPath)) {
|
|
5997
|
-
try {
|
|
5998
|
-
pkg = readJsonFile(pkgPath, {});
|
|
5999
|
-
} catch {
|
|
6000
|
-
}
|
|
6001
|
-
}
|
|
6002
|
-
const existingDeps = pkg.dependencies || {};
|
|
6003
|
-
let needsInstall = false;
|
|
6004
|
-
for (const [dep, version] of Object.entries(SHARED_PLUGIN_DEPENDENCIES)) {
|
|
6005
|
-
if (!existingDeps[dep]) {
|
|
6006
|
-
existingDeps[dep] = version;
|
|
6007
|
-
needsInstall = true;
|
|
6008
|
-
}
|
|
6009
|
-
}
|
|
6010
|
-
if (!needsInstall && existsSync3(join5(pluginsDir, "node_modules"))) return;
|
|
6011
|
-
pkg.dependencies = existingDeps;
|
|
6012
|
-
pkg.private = true;
|
|
6013
|
-
pkg.description = pkg.description || "xbrowser plugins \u2014 shared dependencies";
|
|
6014
|
-
writeFileSync5(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
6015
|
-
try {
|
|
6016
|
-
execSync("npm install --production --no-package-lock --no-fund --no-audit", {
|
|
6017
|
-
cwd: pluginsDir,
|
|
6018
|
-
stdio: "pipe",
|
|
6019
|
-
timeout: 6e4,
|
|
6020
|
-
env: { ...process.env, NODE_ENV: "production" }
|
|
6021
|
-
});
|
|
6022
|
-
} catch (err) {
|
|
6023
|
-
console.warn(`\u26A0\uFE0F Failed to install shared plugin dependencies: ${err instanceof Error ? err.message : String(err)}`);
|
|
6024
|
-
}
|
|
6025
|
-
}
|
|
6026
|
-
|
|
6027
|
-
// src/plugin/contract.ts
|
|
6028
|
-
import {
|
|
6029
|
-
unwrapZod,
|
|
6030
|
-
fieldsFromZodObjectReflected,
|
|
6031
|
-
zodTypeToContractType
|
|
6032
|
-
} from "@dyyz1993/xcli-core";
|
|
6033
|
-
function buildPluginContract(site) {
|
|
6034
|
-
const commands = site.getAllCommands().map((command) => buildCommandContract(site.getCommand?.(command.name) || command, {
|
|
6035
|
-
siteRequiresLogin: site.config?.requiresLogin
|
|
6036
|
-
}));
|
|
6037
|
-
return {
|
|
6038
|
-
version: 2,
|
|
6039
|
-
plugin: {
|
|
6040
|
-
name: site.name,
|
|
6041
|
-
url: site.url,
|
|
6042
|
-
description: site.config?.description,
|
|
6043
|
-
requiresLogin: site.config?.requiresLogin
|
|
6044
|
-
},
|
|
6045
|
-
commands
|
|
6046
|
-
};
|
|
6047
|
-
}
|
|
6048
|
-
function buildCommandContract(command, options = {}) {
|
|
6049
|
-
const extension = command.xbrowser || {};
|
|
6050
|
-
const inferredFields = fieldsFromZodObject(command.parameters);
|
|
6051
|
-
const fields = mergeFields(inferredFields, extension.form?.fields || []);
|
|
6052
|
-
const positional = extension.positional || fields.filter((field) => field.positional).map((field) => field.name);
|
|
6053
|
-
const requiresLogin = command.requiresLogin === true || options.siteRequiresLogin === true && command.name !== "login" && command.name !== "logout";
|
|
6054
|
-
const capabilities = extension.capabilities || inferCapabilities(command.scope || "project", requiresLogin);
|
|
6055
|
-
const outputSchema = command.result ? summarizeZod(command.result) : void 0;
|
|
6056
|
-
return {
|
|
6057
|
-
name: command.name,
|
|
6058
|
-
description: command.description || "",
|
|
6059
|
-
scope: command.scope || "project",
|
|
6060
|
-
requiresLogin,
|
|
6061
|
-
category: extension.category,
|
|
6062
|
-
capabilities,
|
|
6063
|
-
positional,
|
|
6064
|
-
form: {
|
|
6065
|
-
title: extension.form?.title || command.description || command.name,
|
|
6066
|
-
description: extension.form?.description,
|
|
6067
|
-
submitLabel: extension.form?.submitLabel || "Run",
|
|
6068
|
-
fields
|
|
6069
|
-
},
|
|
6070
|
-
output: extension.output || (outputSchema ? { schema: outputSchema } : void 0)
|
|
6071
|
-
};
|
|
6072
|
-
}
|
|
6073
|
-
function fieldsFromZodObject(schema) {
|
|
6074
|
-
const reflected = fieldsFromZodObjectReflected(schema);
|
|
6075
|
-
return reflected.map((field) => {
|
|
6076
|
-
const widget = widgetFor(field.type, field.enum);
|
|
6077
|
-
return {
|
|
6078
|
-
name: field.name,
|
|
6079
|
-
label: toLabel(field.name),
|
|
6080
|
-
type: field.type,
|
|
6081
|
-
widget,
|
|
6082
|
-
required: field.required,
|
|
6083
|
-
...field.description ? { description: field.description } : {},
|
|
6084
|
-
...field.default !== void 0 ? { default: field.default } : {},
|
|
6085
|
-
...field.enum ? { enum: field.enum } : {},
|
|
6086
|
-
...field.type === "array" ? { multiple: true } : {}
|
|
6087
|
-
};
|
|
6088
|
-
});
|
|
6089
|
-
}
|
|
6090
|
-
function mergeFields(inferred, overrides) {
|
|
6091
|
-
if (overrides.length === 0) return inferred;
|
|
6092
|
-
const byName = new Map(inferred.map((field) => [field.name, field]));
|
|
6093
|
-
const seen = /* @__PURE__ */ new Set();
|
|
6094
|
-
const merged = [];
|
|
6095
|
-
for (const override of overrides) {
|
|
6096
|
-
if (!override.name) continue;
|
|
6097
|
-
const base = byName.get(override.name) || {
|
|
6098
|
-
name: override.name,
|
|
6099
|
-
label: toLabel(override.name),
|
|
6100
|
-
type: "string",
|
|
6101
|
-
widget: "text",
|
|
6102
|
-
required: false
|
|
6103
|
-
};
|
|
6104
|
-
merged.push({ ...base, ...override, name: override.name });
|
|
6105
|
-
seen.add(override.name);
|
|
6106
|
-
}
|
|
6107
|
-
for (const field of inferred) {
|
|
6108
|
-
if (!seen.has(field.name)) merged.push(field);
|
|
6109
|
-
}
|
|
6110
|
-
return merged;
|
|
6111
|
-
}
|
|
6112
|
-
function inferCapabilities(scope, requiresLogin) {
|
|
6113
|
-
const caps = [];
|
|
6114
|
-
if (scope === "page") caps.push("browser.page");
|
|
6115
|
-
if (scope === "browser") caps.push("browser.context");
|
|
6116
|
-
if (requiresLogin) caps.push("auth.login");
|
|
6117
|
-
return caps;
|
|
6118
|
-
}
|
|
6119
|
-
function widgetFor(type, enumValues) {
|
|
6120
|
-
if (enumValues) return "select";
|
|
6121
|
-
if (type === "boolean") return "checkbox";
|
|
6122
|
-
if (type === "number") return "number";
|
|
6123
|
-
if (type === "array") return "multi-select";
|
|
6124
|
-
if (type === "object") return "json";
|
|
6125
|
-
return "text";
|
|
6126
|
-
}
|
|
6127
|
-
function summarizeZod(schema) {
|
|
6128
|
-
const unwrapped = unwrapZod(schema);
|
|
6129
|
-
if (unwrapped.typeName === "ZodArray") {
|
|
6130
|
-
const def = unwrapped.schema?._def;
|
|
6131
|
-
return {
|
|
6132
|
-
type: "array",
|
|
6133
|
-
items: summarizeZod(def?.type || def?.innerType)
|
|
6134
|
-
};
|
|
6135
|
-
}
|
|
6136
|
-
const shape = getObjectShape(schema);
|
|
6137
|
-
if (!shape) {
|
|
6138
|
-
return {
|
|
6139
|
-
type: zodTypeToContractType(unwrapped.typeName),
|
|
6140
|
-
required: !unwrapped.optional,
|
|
6141
|
-
...unwrapped.description ? { description: unwrapped.description } : {}
|
|
6142
|
-
};
|
|
6143
|
-
}
|
|
6144
|
-
return Object.fromEntries(
|
|
6145
|
-
Object.entries(shape).map(([name, field]) => {
|
|
6146
|
-
const inner = unwrapZod(field);
|
|
6147
|
-
return [name, {
|
|
6148
|
-
type: zodTypeToContractType(inner.typeName),
|
|
6149
|
-
required: !inner.optional,
|
|
6150
|
-
...inner.description ? { description: inner.description } : {}
|
|
6151
|
-
}];
|
|
6152
|
-
})
|
|
6153
|
-
);
|
|
6154
|
-
}
|
|
6155
|
-
function getObjectShape(schema) {
|
|
6156
|
-
const zod = schema;
|
|
6157
|
-
const shapeOrFn = zod?.shape ?? zod?._def?.shape;
|
|
6158
|
-
if (!shapeOrFn) return void 0;
|
|
6159
|
-
return typeof shapeOrFn === "function" ? shapeOrFn() : shapeOrFn;
|
|
6160
|
-
}
|
|
6161
|
-
function toLabel(name) {
|
|
6162
|
-
return name.replace(/([A-Z])/g, " $1").replace(/[-_]+/g, " ").replace(/\s+/g, " ").trim().replace(/^./, (char) => char.toUpperCase());
|
|
6163
|
-
}
|
|
6164
|
-
|
|
6165
|
-
// src/plugin/login-required-patch.ts
|
|
6166
|
-
import { SiteInstanceImpl } from "@dyyz1993/xcli-core";
|
|
6167
|
-
var patched = false;
|
|
6168
|
-
function patchLoginRequired() {
|
|
6169
|
-
if (patched) return;
|
|
6170
|
-
patched = true;
|
|
6171
|
-
const target = SiteInstanceImpl.prototype;
|
|
6172
|
-
const originalCommand = target.command;
|
|
6173
|
-
const wrapped = function(...args) {
|
|
6174
|
-
const result = originalCommand.apply(this, args);
|
|
6175
|
-
const [name, cmd] = args;
|
|
6176
|
-
const loginRequired = cmd.loginRequired;
|
|
6177
|
-
if (loginRequired) {
|
|
6178
|
-
const commands = this.commands;
|
|
6179
|
-
const entry = commands?.get(name);
|
|
6180
|
-
if (entry) {
|
|
6181
|
-
entry.loginRequired = loginRequired;
|
|
6182
|
-
}
|
|
6183
|
-
}
|
|
6184
|
-
return result;
|
|
6185
|
-
};
|
|
6186
|
-
Object.defineProperty(target, "command", {
|
|
6187
|
-
value: wrapped,
|
|
6188
|
-
writable: true,
|
|
6189
|
-
configurable: true
|
|
6190
|
-
});
|
|
6191
|
-
}
|
|
6192
|
-
|
|
6193
|
-
// src/plugin/loader.ts
|
|
6194
|
-
var DEFAULT_PLUGIN_DIRS = [".xcli/plugins", "../.xcli/plugins"];
|
|
6195
|
-
var XBrowserPluginLoader = class {
|
|
6196
|
-
core;
|
|
6197
|
-
loader;
|
|
6198
|
-
options;
|
|
6199
|
-
constructor(options) {
|
|
6200
|
-
patchLoginRequired();
|
|
6201
|
-
this.options = options ?? {};
|
|
6202
|
-
const cwd = this.options.cwd || process.cwd();
|
|
6203
|
-
const coreConfig = {
|
|
6204
|
-
name: "xbrowser",
|
|
6205
|
-
version: "0.1.0",
|
|
6206
|
-
description: "Browser automation CLI",
|
|
6207
|
-
configDirName: ".xbrowser",
|
|
6208
|
-
envPrefix: "XBROWSER",
|
|
6209
|
-
pluginDirs: [
|
|
6210
|
-
...DEFAULT_PLUGIN_DIRS,
|
|
6211
|
-
resolve2(cwd, ".xcli/plugins")
|
|
6212
|
-
]
|
|
6213
|
-
};
|
|
6214
|
-
this.core = new Core(coreConfig);
|
|
6215
|
-
this.loader = this.core.loader;
|
|
6216
|
-
}
|
|
6217
|
-
getAPI() {
|
|
6218
|
-
return this.loader.getAPI();
|
|
6219
|
-
}
|
|
6220
|
-
/**
|
|
6221
|
-
* Get the core instance for external use.
|
|
6222
|
-
* @returns The xcli-core Core instance.
|
|
6223
|
-
*/
|
|
6224
|
-
getCore() {
|
|
6225
|
-
return this.core;
|
|
6226
|
-
}
|
|
6227
|
-
getPlugin(id) {
|
|
6228
|
-
return this.loader.getPlugin(id);
|
|
6229
|
-
}
|
|
6230
|
-
getPluginStatus(id) {
|
|
6231
|
-
return this.loader.getPluginStatus(id);
|
|
6232
|
-
}
|
|
6233
|
-
getLoadedPlugins() {
|
|
6234
|
-
return this.loader.getLoadedPlugins();
|
|
6235
|
-
}
|
|
6236
|
-
getPluginContract(siteName, commandName) {
|
|
6237
|
-
const site = this.core.loader.getSite(siteName);
|
|
6238
|
-
if (!site) return void 0;
|
|
6239
|
-
const contract = buildPluginContract(site);
|
|
6240
|
-
if (!commandName) return contract;
|
|
6241
|
-
return contract.commands.find((command) => command.name === commandName);
|
|
6242
|
-
}
|
|
6243
|
-
async loadPlugin(pluginPath, id) {
|
|
6244
|
-
return this.loader.loadPlugin(pluginPath, id);
|
|
6245
|
-
}
|
|
6246
|
-
async unloadPlugin(id) {
|
|
6247
|
-
return this.loader.unloadPlugin(id);
|
|
6248
|
-
}
|
|
6249
|
-
async reloadPlugin(id) {
|
|
6250
|
-
return this.loader.reloadPlugin(id);
|
|
6251
|
-
}
|
|
6252
|
-
async loadFromFunction(setup) {
|
|
6253
|
-
return this.loader.loadFromFunction(setup);
|
|
6254
|
-
}
|
|
6255
|
-
async scanAndLoad() {
|
|
6256
|
-
const cwd = this.options.cwd || process.cwd();
|
|
6257
|
-
const globalDir = this.options.globalDir || resolve2(homedir5(), ".xbrowser/plugins");
|
|
6258
|
-
ensurePluginDependencies(globalDir);
|
|
6259
|
-
const dirs = [
|
|
6260
|
-
resolve2(cwd, ".xcli/plugins"),
|
|
6261
|
-
resolve2(cwd, "../.xcli/plugins"),
|
|
6262
|
-
this.options.userDir || resolve2(homedir5(), ".xcli/plugins"),
|
|
6263
|
-
globalDir
|
|
6264
|
-
];
|
|
6265
|
-
const loaded = [];
|
|
6266
|
-
const seen = /* @__PURE__ */ new Set();
|
|
6267
|
-
for (const dir of dirs) {
|
|
6268
|
-
if (!existsSync4(dir)) continue;
|
|
6269
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
6270
|
-
for (const entry of entries) {
|
|
6271
|
-
if (!entry.isDirectory()) continue;
|
|
6272
|
-
if (seen.has(entry.name)) continue;
|
|
6273
|
-
seen.add(entry.name);
|
|
6274
|
-
const pluginDir = resolve2(dir, entry.name);
|
|
6275
|
-
let indexPath = resolve2(pluginDir, "index.js");
|
|
6276
|
-
if (!existsSync4(indexPath)) {
|
|
6277
|
-
indexPath = resolve2(pluginDir, "index.ts");
|
|
6278
|
-
}
|
|
6279
|
-
if (!existsSync4(indexPath)) continue;
|
|
6280
|
-
try {
|
|
6281
|
-
if (!existsSync4(resolve2(pluginDir, "package.json"))) {
|
|
6282
|
-
console.warn(`\u26A0\uFE0F Plugin "${entry.name}" has no package.json. Use "xbrowser create ${entry.name} --template static" for proper structure.`);
|
|
6283
|
-
} else {
|
|
6284
|
-
const metadata = PluginMetadataParser.parseFromPackageJson(pluginDir);
|
|
6285
|
-
if (!metadata) {
|
|
6286
|
-
console.warn(`\u26A0\uFE0F Plugin "${entry.name}" has package.json but no xbrowser metadata. Add { "xbrowser": { "description": "..." } } to package.json.`);
|
|
6287
|
-
}
|
|
6288
|
-
}
|
|
6289
|
-
const instance = await this.loadPlugin(indexPath, entry.name);
|
|
6290
|
-
loaded.push(instance);
|
|
6291
|
-
} catch (err) {
|
|
6292
|
-
if (process.env.XBROWSER_DEBUG) {
|
|
6293
|
-
console.warn(`\u26A0\uFE0F Plugin "${entry.name}" load failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
6294
|
-
}
|
|
6295
|
-
}
|
|
6296
|
-
}
|
|
6297
|
-
}
|
|
6298
|
-
return loaded;
|
|
6299
|
-
}
|
|
6300
|
-
async unload() {
|
|
6301
|
-
return this.loader.unload();
|
|
6302
|
-
}
|
|
6303
|
-
};
|
|
6304
|
-
|
|
6305
|
-
// src/utils/plugin-singleton.ts
|
|
6306
|
-
var pluginLoader = null;
|
|
6307
|
-
var pluginsScanned = false;
|
|
6308
|
-
async function getPluginLoader() {
|
|
6309
|
-
if (!pluginLoader) {
|
|
6310
|
-
pluginLoader = new XBrowserPluginLoader();
|
|
6311
|
-
}
|
|
6312
|
-
if (!pluginsScanned) {
|
|
6313
|
-
await pluginLoader.scanAndLoad();
|
|
6314
|
-
pluginsScanned = true;
|
|
6315
|
-
}
|
|
6316
|
-
return pluginLoader;
|
|
6317
|
-
}
|
|
6318
|
-
|
|
6319
5894
|
// src/utils/viewer-url.ts
|
|
6320
5895
|
function buildViewerUrl(sessionName = "default") {
|
|
6321
5896
|
try {
|
|
@@ -6799,7 +6374,7 @@ var TipsManager = class {
|
|
|
6799
6374
|
}
|
|
6800
6375
|
}
|
|
6801
6376
|
debounce() {
|
|
6802
|
-
return new Promise((
|
|
6377
|
+
return new Promise((resolve) => setTimeout(resolve, DEBOUNCE_MS));
|
|
6803
6378
|
}
|
|
6804
6379
|
formatTips(tips) {
|
|
6805
6380
|
return tips.map((tip) => {
|
|
@@ -6916,11 +6491,11 @@ async function loadHooks() {
|
|
|
6916
6491
|
}
|
|
6917
6492
|
|
|
6918
6493
|
// src/executor.ts
|
|
6919
|
-
import { homedir as
|
|
6920
|
-
import { join as
|
|
6494
|
+
import { homedir as homedir5 } from "os";
|
|
6495
|
+
import { join as join5 } from "path";
|
|
6921
6496
|
var NAVIGATION_COMMANDS = /* @__PURE__ */ new Set(["goto", "back", "forward", "refresh"]);
|
|
6922
6497
|
var snapshotHintShown = /* @__PURE__ */ new WeakSet();
|
|
6923
|
-
var CONFIG_DIR2 =
|
|
6498
|
+
var CONFIG_DIR2 = join5(homedir5(), ".xbrowser");
|
|
6924
6499
|
var storageCache = /* @__PURE__ */ new Map();
|
|
6925
6500
|
function getPluginStorage(pluginName) {
|
|
6926
6501
|
if (!storageCache.has(pluginName)) {
|
|
@@ -6932,7 +6507,7 @@ var archiveInitialized = false;
|
|
|
6932
6507
|
function ensureArchiveInit() {
|
|
6933
6508
|
if (!archiveInitialized) {
|
|
6934
6509
|
try {
|
|
6935
|
-
configureArchiveStore({ archiveDir:
|
|
6510
|
+
configureArchiveStore({ archiveDir: join5(homedir5(), ".xbrowser", "archives") });
|
|
6936
6511
|
} catch {
|
|
6937
6512
|
}
|
|
6938
6513
|
archiveInitialized = true;
|
|
@@ -7006,7 +6581,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7006
6581
|
params = result.data;
|
|
7007
6582
|
}
|
|
7008
6583
|
if (command.scope !== "cli" && !process.env.XBROWSER_DAEMON_WORKER) {
|
|
7009
|
-
const { forwardExec } = await import("./daemon-client-
|
|
6584
|
+
const { forwardExec } = await import("./daemon-client-COJQESU2.js");
|
|
7010
6585
|
const result = await forwardExec(commandName, params, sessionName, extraOpts?.cdpEndpoint);
|
|
7011
6586
|
if (result) return result;
|
|
7012
6587
|
}
|
|
@@ -7689,10 +7264,10 @@ async function replayEntry(entry, options = {}) {
|
|
|
7689
7264
|
}
|
|
7690
7265
|
|
|
7691
7266
|
// src/daemon/feedback-store.ts
|
|
7692
|
-
import { readFileSync as
|
|
7693
|
-
import { join as
|
|
7694
|
-
import { homedir as
|
|
7695
|
-
var FEEDBACK_FILE =
|
|
7267
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
|
|
7268
|
+
import { join as join6 } from "path";
|
|
7269
|
+
import { homedir as homedir6 } from "os";
|
|
7270
|
+
var FEEDBACK_FILE = join6(homedir6(), ".xbrowser", "feedback.json");
|
|
7696
7271
|
var FeedbackStore = class {
|
|
7697
7272
|
entries = [];
|
|
7698
7273
|
constructor() {
|
|
@@ -7700,7 +7275,7 @@ var FeedbackStore = class {
|
|
|
7700
7275
|
}
|
|
7701
7276
|
load() {
|
|
7702
7277
|
try {
|
|
7703
|
-
const data =
|
|
7278
|
+
const data = readFileSync3(FEEDBACK_FILE, "utf8");
|
|
7704
7279
|
this.entries = JSON.parse(data);
|
|
7705
7280
|
} catch {
|
|
7706
7281
|
this.entries = [];
|
|
@@ -7708,8 +7283,8 @@ var FeedbackStore = class {
|
|
|
7708
7283
|
}
|
|
7709
7284
|
save() {
|
|
7710
7285
|
try {
|
|
7711
|
-
|
|
7712
|
-
|
|
7286
|
+
mkdirSync4(join6(homedir6(), ".xbrowser"), { recursive: true });
|
|
7287
|
+
writeFileSync4(FEEDBACK_FILE, JSON.stringify(this.entries, null, 2));
|
|
7713
7288
|
} catch {
|
|
7714
7289
|
}
|
|
7715
7290
|
}
|
|
@@ -8007,7 +7582,7 @@ var PlaybackEngine = class _PlaybackEngine {
|
|
|
8007
7582
|
// src/daemon/rpc-handlers.ts
|
|
8008
7583
|
var activeRecorders = /* @__PURE__ */ new Map();
|
|
8009
7584
|
var replayResumeResolvers = /* @__PURE__ */ new Map();
|
|
8010
|
-
var CONFIG_DIR3 =
|
|
7585
|
+
var CONFIG_DIR3 = join7(homedir7(), ".xbrowser");
|
|
8011
7586
|
var RECORDING_INJECT_JS = `
|
|
8012
7587
|
(function(){
|
|
8013
7588
|
if(window.__xb_rec) return;
|
|
@@ -8099,6 +7674,8 @@ function createRPCHandler() {
|
|
|
8099
7674
|
// ── Utility ──
|
|
8100
7675
|
case "ping":
|
|
8101
7676
|
return { ok: true, pid: process.pid };
|
|
7677
|
+
case "plugins:reload":
|
|
7678
|
+
return handlePluginsReload();
|
|
8102
7679
|
// ── Network analysis ──
|
|
8103
7680
|
case "network:list":
|
|
8104
7681
|
return handleNetworkList(params);
|
|
@@ -8285,6 +7862,13 @@ function createRPCHandler() {
|
|
|
8285
7862
|
registerSessionIfNew(sessionName);
|
|
8286
7863
|
return result;
|
|
8287
7864
|
}
|
|
7865
|
+
async function handlePluginsReload() {
|
|
7866
|
+
const { resetPluginLoader } = await import("./plugin-singleton-ZBVTWEYK.js");
|
|
7867
|
+
resetPluginLoader();
|
|
7868
|
+
const loader = await import("./plugin-singleton-ZBVTWEYK.js").then((m) => m.getPluginLoader());
|
|
7869
|
+
const sites = loader.getCore().loader.getSites();
|
|
7870
|
+
return { ok: true, plugins: sites.length };
|
|
7871
|
+
}
|
|
8288
7872
|
async function handleAgentObserve(params) {
|
|
8289
7873
|
const sessionName = params.session || "default";
|
|
8290
7874
|
const commandParams = {
|
|
@@ -8455,10 +8039,10 @@ function createRPCHandler() {
|
|
|
8455
8039
|
if (!sess) return { ok: false, error: "No session" };
|
|
8456
8040
|
try {
|
|
8457
8041
|
const events = await sess.page.evaluate(() => window.__xb_evts || []);
|
|
8458
|
-
const recordingsDir =
|
|
8459
|
-
|
|
8460
|
-
const outPath = params.path ||
|
|
8461
|
-
|
|
8042
|
+
const recordingsDir = join7(CONFIG_DIR3, "recordings");
|
|
8043
|
+
mkdirSync5(recordingsDir, { recursive: true });
|
|
8044
|
+
const outPath = params.path || join7(recordingsDir, `recording-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`);
|
|
8045
|
+
writeFileSync5(outPath, JSON.stringify({
|
|
8462
8046
|
startUrl: sess.page.url(),
|
|
8463
8047
|
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8464
8048
|
events
|
|
@@ -8558,13 +8142,19 @@ function createRPCHandler() {
|
|
|
8558
8142
|
}
|
|
8559
8143
|
async function handleRecordStop(params) {
|
|
8560
8144
|
const sessionName = params.session || "default";
|
|
8145
|
+
const outputPath = params.output;
|
|
8561
8146
|
const recorder = activeRecorders.get(sessionName);
|
|
8562
8147
|
if (!recorder) {
|
|
8563
8148
|
const existingData = SessionRecorder.readData(sessionName);
|
|
8564
8149
|
if (existingData) {
|
|
8150
|
+
if (outputPath) {
|
|
8151
|
+
const { writeFileSync: writeFileSync7 } = await import("fs");
|
|
8152
|
+
writeFileSync7(outputPath, JSON.stringify(existingData, null, 2), "utf-8");
|
|
8153
|
+
}
|
|
8565
8154
|
return {
|
|
8566
8155
|
ok: true,
|
|
8567
8156
|
message: "Recorder process already exited. Recording data found on disk.",
|
|
8157
|
+
output: outputPath,
|
|
8568
8158
|
session: sessionName,
|
|
8569
8159
|
actions: existingData.actions.length,
|
|
8570
8160
|
network: existingData.network.length
|
|
@@ -8575,8 +8165,20 @@ function createRPCHandler() {
|
|
|
8575
8165
|
try {
|
|
8576
8166
|
const { data, summary } = await recorder.stop();
|
|
8577
8167
|
activeRecorders.delete(sessionName);
|
|
8168
|
+
if (outputPath) {
|
|
8169
|
+
const { writeFileSync: writeFileSync7, mkdirSync: mkdirSync7 } = await import("fs");
|
|
8170
|
+
const { dirname: dirname2 } = await import("path");
|
|
8171
|
+
mkdirSync7(dirname2(outputPath), { recursive: true });
|
|
8172
|
+
if (outputPath.endsWith(".yaml") || outputPath.endsWith(".yml")) {
|
|
8173
|
+
const yaml2 = (await import("yaml")).default;
|
|
8174
|
+
writeFileSync7(outputPath, yaml2.stringify(data), "utf-8");
|
|
8175
|
+
} else {
|
|
8176
|
+
writeFileSync7(outputPath, JSON.stringify(data, null, 2), "utf-8");
|
|
8177
|
+
}
|
|
8178
|
+
}
|
|
8578
8179
|
return {
|
|
8579
8180
|
ok: true,
|
|
8181
|
+
output: outputPath,
|
|
8580
8182
|
session: sessionName,
|
|
8581
8183
|
actions: data.actions.length,
|
|
8582
8184
|
network: data.network.length,
|
|
@@ -8665,8 +8267,13 @@ function createRPCHandler() {
|
|
|
8665
8267
|
let rawContent;
|
|
8666
8268
|
let parsed;
|
|
8667
8269
|
try {
|
|
8668
|
-
rawContent =
|
|
8669
|
-
|
|
8270
|
+
rawContent = readFileSync5(file, "utf8");
|
|
8271
|
+
try {
|
|
8272
|
+
parsed = JSON.parse(rawContent);
|
|
8273
|
+
} catch {
|
|
8274
|
+
const yaml2 = (await import("yaml")).default;
|
|
8275
|
+
parsed = yaml2.parse(rawContent);
|
|
8276
|
+
}
|
|
8670
8277
|
} catch (e) {
|
|
8671
8278
|
return { ok: false, success: false, duration: 0, eventsPlayed: 0, totalEvents: 0, errors: [{ eventIndex: -1, error: "Failed to read/parse file: " + String(e) }] };
|
|
8672
8279
|
}
|
|
@@ -8709,8 +8316,8 @@ function createRPCHandler() {
|
|
|
8709
8316
|
const result = await engine.play({
|
|
8710
8317
|
slowMo,
|
|
8711
8318
|
onCheckpoint: async (checkpoint) => {
|
|
8712
|
-
return new Promise((
|
|
8713
|
-
replayResumeResolvers.set(sessionName, () =>
|
|
8319
|
+
return new Promise((resolve) => {
|
|
8320
|
+
replayResumeResolvers.set(sessionName, () => resolve(true));
|
|
8714
8321
|
console.log(`[replay] Checkpoint reached: [${checkpoint.type}] ${checkpoint.hint}`);
|
|
8715
8322
|
console.log('[replay] Send "replay:resume" RPC to continue.');
|
|
8716
8323
|
});
|
|
@@ -9774,13 +9381,13 @@ var FileListHandler = class {
|
|
|
9774
9381
|
async handle(ctx) {
|
|
9775
9382
|
const msg = ctx.message;
|
|
9776
9383
|
try {
|
|
9777
|
-
const { readdirSync
|
|
9778
|
-
const { join:
|
|
9779
|
-
const targetPath =
|
|
9780
|
-
const entries =
|
|
9384
|
+
const { readdirSync, statSync } = await import("fs");
|
|
9385
|
+
const { join: join9, resolve } = await import("path");
|
|
9386
|
+
const targetPath = resolve(msg.path);
|
|
9387
|
+
const entries = readdirSync(targetPath);
|
|
9781
9388
|
const files = entries.map((name) => {
|
|
9782
9389
|
try {
|
|
9783
|
-
const stat = statSync(
|
|
9390
|
+
const stat = statSync(join9(targetPath, name));
|
|
9784
9391
|
return { name, isDir: stat.isDirectory(), size: stat.size, modified: stat.mtime.toISOString() };
|
|
9785
9392
|
} catch {
|
|
9786
9393
|
return { name, isDir: false, size: 0, modified: "" };
|
|
@@ -9797,10 +9404,10 @@ var FileDownloadHandler = class {
|
|
|
9797
9404
|
async handle(ctx) {
|
|
9798
9405
|
const msg = ctx.message;
|
|
9799
9406
|
try {
|
|
9800
|
-
const { readFileSync:
|
|
9801
|
-
const { resolve
|
|
9802
|
-
const targetPath =
|
|
9803
|
-
const data =
|
|
9407
|
+
const { readFileSync: readFileSync6 } = await import("fs");
|
|
9408
|
+
const { resolve, basename } = await import("path");
|
|
9409
|
+
const targetPath = resolve(msg.path);
|
|
9410
|
+
const data = readFileSync6(targetPath);
|
|
9804
9411
|
const base64 = data.toString("base64");
|
|
9805
9412
|
const ext = targetPath.split(".").pop()?.toLowerCase() || "";
|
|
9806
9413
|
const mimeMap = {
|
|
@@ -10104,7 +9711,7 @@ var WSServer = class extends EventEmitter2 {
|
|
|
10104
9711
|
this.isRunning = false;
|
|
10105
9712
|
return;
|
|
10106
9713
|
}
|
|
10107
|
-
return new Promise((
|
|
9714
|
+
return new Promise((resolve, reject) => {
|
|
10108
9715
|
this.wsServer.close((err) => {
|
|
10109
9716
|
if (err) {
|
|
10110
9717
|
reject(err);
|
|
@@ -10112,7 +9719,7 @@ var WSServer = class extends EventEmitter2 {
|
|
|
10112
9719
|
this.wsServer = null;
|
|
10113
9720
|
this.isRunning = false;
|
|
10114
9721
|
this.emit("stopped");
|
|
10115
|
-
|
|
9722
|
+
resolve();
|
|
10116
9723
|
}
|
|
10117
9724
|
});
|
|
10118
9725
|
});
|
|
@@ -11441,8 +11048,8 @@ connectWS();
|
|
|
11441
11048
|
}
|
|
11442
11049
|
|
|
11443
11050
|
// src/daemon/daemon-main.ts
|
|
11444
|
-
var CONFIG_DIR4 =
|
|
11445
|
-
var LOG_FILE =
|
|
11051
|
+
var CONFIG_DIR4 = join8(homedir8(), ".xbrowser");
|
|
11052
|
+
var LOG_FILE = join8(CONFIG_DIR4, "daemon.log");
|
|
11446
11053
|
function log(msg) {
|
|
11447
11054
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
|
|
11448
11055
|
const line = `[DAEMON ${ts}] ${msg}
|
|
@@ -11474,7 +11081,7 @@ async function main() {
|
|
|
11474
11081
|
if (err.code === "EADDRINUSE") {
|
|
11475
11082
|
log(`Port ${daemonPort} already in use \u2014 another daemon instance likely won the startup race. Exiting gracefully.`);
|
|
11476
11083
|
try {
|
|
11477
|
-
unlinkSync(
|
|
11084
|
+
unlinkSync(join8(CONFIG_DIR4, "daemon.json"));
|
|
11478
11085
|
} catch {
|
|
11479
11086
|
}
|
|
11480
11087
|
process.exit(0);
|
|
@@ -11508,8 +11115,8 @@ async function main() {
|
|
|
11508
11115
|
rpcHandler.setPreviewWS(previewWS);
|
|
11509
11116
|
previewWS.on("screencast-started", (sid) => log(`Preview screencast started: ${sid}`));
|
|
11510
11117
|
previewWS.on("screencast-stopped", (sid) => log(`Preview screencast stopped: ${sid}`));
|
|
11511
|
-
|
|
11512
|
-
|
|
11118
|
+
mkdirSync6(CONFIG_DIR4, { recursive: true });
|
|
11119
|
+
writeFileSync6(join8(CONFIG_DIR4, "daemon.json"), JSON.stringify({
|
|
11513
11120
|
port: daemonPort,
|
|
11514
11121
|
pid: process.pid,
|
|
11515
11122
|
startedAt: Date.now()
|