@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
|
@@ -279,8 +279,10 @@ async function forwardNetworkInspect(sessionName, id) {
|
|
|
279
279
|
async function forwardRecordStart(session, url, cdpEndpoint) {
|
|
280
280
|
return rpcCall("record:start", { session, url, cdpEndpoint }, 15e3);
|
|
281
281
|
}
|
|
282
|
-
async function forwardRecordStop(session) {
|
|
283
|
-
|
|
282
|
+
async function forwardRecordStop(session, output) {
|
|
283
|
+
const params = { session };
|
|
284
|
+
if (output) params.output = output;
|
|
285
|
+
return rpcCall("record:stop", params, 1e4);
|
|
284
286
|
}
|
|
285
287
|
async function forwardRecordStatus(session) {
|
|
286
288
|
return rpcCall("record:status", { session }, 5e3);
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
// src/plugin/loader.ts
|
|
2
|
+
import {
|
|
3
|
+
Core
|
|
4
|
+
} from "@dyyz1993/xcli-core";
|
|
5
|
+
import { resolve as resolve2 } from "path";
|
|
6
|
+
import { existsSync as existsSync3, readdirSync } from "fs";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
|
|
9
|
+
// src/plugin/metadata-parser.ts
|
|
10
|
+
import { existsSync } from "fs";
|
|
11
|
+
import { resolve } from "path";
|
|
12
|
+
|
|
13
|
+
// src/utils/json-file.ts
|
|
14
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
15
|
+
function readJsonFile(filePath, defaultValue) {
|
|
16
|
+
try {
|
|
17
|
+
const content = readFileSync(filePath, "utf-8");
|
|
18
|
+
return JSON.parse(content);
|
|
19
|
+
} catch {
|
|
20
|
+
return defaultValue;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/plugin/metadata-parser.ts
|
|
25
|
+
var PluginMetadataParser = class {
|
|
26
|
+
static XBROWSER_KEYWORDS = ["xbrowser", "xbrowser-plugin"];
|
|
27
|
+
static parseFromPackageJson(pluginPath) {
|
|
28
|
+
const packageJsonPath = resolve(pluginPath, "package.json");
|
|
29
|
+
if (!existsSync(packageJsonPath)) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const packageJson = readJsonFile(packageJsonPath, null);
|
|
33
|
+
if (!packageJson) return null;
|
|
34
|
+
if (!packageJson.xbrowser) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const xbrowser = packageJson.xbrowser;
|
|
38
|
+
const metadata = {
|
|
39
|
+
id: xbrowser.id || packageJson.name,
|
|
40
|
+
name: xbrowser.name || packageJson.name,
|
|
41
|
+
description: xbrowser.description || packageJson.description || "",
|
|
42
|
+
version: xbrowser.version || packageJson.version || "1.0.0",
|
|
43
|
+
author: xbrowser.author || this.extractAuthor(packageJson.author),
|
|
44
|
+
homepage: xbrowser.homepage || packageJson.homepage,
|
|
45
|
+
commands: xbrowser.commands,
|
|
46
|
+
sites: xbrowser.sites,
|
|
47
|
+
tags: xbrowser.tags,
|
|
48
|
+
screenshot: xbrowser.screenshot,
|
|
49
|
+
license: xbrowser.license || packageJson.license
|
|
50
|
+
};
|
|
51
|
+
return metadata;
|
|
52
|
+
}
|
|
53
|
+
static isXBrowserPlugin(packageJson) {
|
|
54
|
+
if (packageJson.xbrowser) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
const keywords = packageJson.keywords;
|
|
58
|
+
if (!keywords) return false;
|
|
59
|
+
return this.XBROWSER_KEYWORDS.some((kw) => keywords.includes(kw));
|
|
60
|
+
}
|
|
61
|
+
static fromNPMResult(result) {
|
|
62
|
+
const author = typeof result.author === "string" ? result.author : result.author?.name || "Unknown";
|
|
63
|
+
return {
|
|
64
|
+
id: result.name,
|
|
65
|
+
name: result.name.replace(/^xbrowser-plugin-/, "").replace(/^@[^/]+\//, ""),
|
|
66
|
+
description: result.description || "",
|
|
67
|
+
version: result.version,
|
|
68
|
+
author,
|
|
69
|
+
homepage: result.homepage || result.links?.homepage,
|
|
70
|
+
tags: result.keywords,
|
|
71
|
+
license: ""
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
static extractAuthor(author) {
|
|
75
|
+
if (typeof author === "string") return author;
|
|
76
|
+
if (typeof author === "object" && author !== null) {
|
|
77
|
+
const authorObj = author;
|
|
78
|
+
return authorObj.name || "Unknown";
|
|
79
|
+
}
|
|
80
|
+
return "Unknown";
|
|
81
|
+
}
|
|
82
|
+
static validateMetadata(metadata) {
|
|
83
|
+
const errors = [];
|
|
84
|
+
if (!metadata.id) errors.push("id is required");
|
|
85
|
+
if (!metadata.name) errors.push("name is required");
|
|
86
|
+
if (!metadata.description) errors.push("description is required");
|
|
87
|
+
if (!metadata.version) errors.push("version is required");
|
|
88
|
+
return errors;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// src/plugin/ensure-deps.ts
|
|
93
|
+
import { existsSync as existsSync2, mkdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
94
|
+
import { join } from "path";
|
|
95
|
+
import { execSync } from "child_process";
|
|
96
|
+
var SHARED_PLUGIN_DEPENDENCIES = {
|
|
97
|
+
"zod": "^3.24.0",
|
|
98
|
+
"@dyyz1993/xcli-core": "^0.12.1"
|
|
99
|
+
};
|
|
100
|
+
function ensurePluginDependencies(pluginsDir) {
|
|
101
|
+
const zodPath = join(pluginsDir, "node_modules", "zod");
|
|
102
|
+
if (existsSync2(zodPath)) return;
|
|
103
|
+
mkdirSync(pluginsDir, { recursive: true });
|
|
104
|
+
const pkgPath = join(pluginsDir, "package.json");
|
|
105
|
+
let pkg = {};
|
|
106
|
+
if (existsSync2(pkgPath)) {
|
|
107
|
+
try {
|
|
108
|
+
pkg = readJsonFile(pkgPath, {});
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const existingDeps = pkg.dependencies || {};
|
|
113
|
+
let needsInstall = false;
|
|
114
|
+
for (const [dep, version] of Object.entries(SHARED_PLUGIN_DEPENDENCIES)) {
|
|
115
|
+
if (!existingDeps[dep]) {
|
|
116
|
+
existingDeps[dep] = version;
|
|
117
|
+
needsInstall = true;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (!needsInstall && existsSync2(join(pluginsDir, "node_modules"))) return;
|
|
121
|
+
pkg.dependencies = existingDeps;
|
|
122
|
+
pkg.private = true;
|
|
123
|
+
pkg.description = pkg.description || "xbrowser plugins \u2014 shared dependencies";
|
|
124
|
+
writeFileSync2(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
125
|
+
try {
|
|
126
|
+
execSync("npm install --production --no-package-lock --no-fund --no-audit", {
|
|
127
|
+
cwd: pluginsDir,
|
|
128
|
+
stdio: "pipe",
|
|
129
|
+
timeout: 6e4,
|
|
130
|
+
env: { ...process.env, NODE_ENV: "production" }
|
|
131
|
+
});
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.warn(`\u26A0\uFE0F Failed to install shared plugin dependencies: ${err instanceof Error ? err.message : String(err)}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/plugin/contract.ts
|
|
138
|
+
import {
|
|
139
|
+
unwrapZod,
|
|
140
|
+
fieldsFromZodObjectReflected,
|
|
141
|
+
zodTypeToContractType
|
|
142
|
+
} from "@dyyz1993/xcli-core";
|
|
143
|
+
function buildPluginContract(site) {
|
|
144
|
+
const commands = site.getAllCommands().map((command) => buildCommandContract(site.getCommand?.(command.name) || command, {
|
|
145
|
+
siteRequiresLogin: site.config?.requiresLogin
|
|
146
|
+
}));
|
|
147
|
+
return {
|
|
148
|
+
version: 2,
|
|
149
|
+
plugin: {
|
|
150
|
+
name: site.name,
|
|
151
|
+
url: site.url,
|
|
152
|
+
description: site.config?.description,
|
|
153
|
+
requiresLogin: site.config?.requiresLogin
|
|
154
|
+
},
|
|
155
|
+
commands
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function buildCommandContract(command, options = {}) {
|
|
159
|
+
const extension = command.xbrowser || {};
|
|
160
|
+
const inferredFields = fieldsFromZodObject(command.parameters);
|
|
161
|
+
const fields = mergeFields(inferredFields, extension.form?.fields || []);
|
|
162
|
+
const positional = extension.positional || fields.filter((field) => field.positional).map((field) => field.name);
|
|
163
|
+
const requiresLogin = command.requiresLogin === true || options.siteRequiresLogin === true && command.name !== "login" && command.name !== "logout";
|
|
164
|
+
const capabilities = extension.capabilities || inferCapabilities(command.scope || "project", requiresLogin);
|
|
165
|
+
const outputSchema = command.result ? summarizeZod(command.result) : void 0;
|
|
166
|
+
return {
|
|
167
|
+
name: command.name,
|
|
168
|
+
description: command.description || "",
|
|
169
|
+
scope: command.scope || "project",
|
|
170
|
+
requiresLogin,
|
|
171
|
+
category: extension.category,
|
|
172
|
+
capabilities,
|
|
173
|
+
positional,
|
|
174
|
+
form: {
|
|
175
|
+
title: extension.form?.title || command.description || command.name,
|
|
176
|
+
description: extension.form?.description,
|
|
177
|
+
submitLabel: extension.form?.submitLabel || "Run",
|
|
178
|
+
fields
|
|
179
|
+
},
|
|
180
|
+
output: extension.output || (outputSchema ? { schema: outputSchema } : void 0)
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function fieldsFromZodObject(schema) {
|
|
184
|
+
const reflected = fieldsFromZodObjectReflected(schema);
|
|
185
|
+
return reflected.map((field) => {
|
|
186
|
+
const widget = widgetFor(field.type, field.enum);
|
|
187
|
+
return {
|
|
188
|
+
name: field.name,
|
|
189
|
+
label: toLabel(field.name),
|
|
190
|
+
type: field.type,
|
|
191
|
+
widget,
|
|
192
|
+
required: field.required,
|
|
193
|
+
...field.description ? { description: field.description } : {},
|
|
194
|
+
...field.default !== void 0 ? { default: field.default } : {},
|
|
195
|
+
...field.enum ? { enum: field.enum } : {},
|
|
196
|
+
...field.type === "array" ? { multiple: true } : {}
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
function mergeFields(inferred, overrides) {
|
|
201
|
+
if (overrides.length === 0) return inferred;
|
|
202
|
+
const byName = new Map(inferred.map((field) => [field.name, field]));
|
|
203
|
+
const seen = /* @__PURE__ */ new Set();
|
|
204
|
+
const merged = [];
|
|
205
|
+
for (const override of overrides) {
|
|
206
|
+
if (!override.name) continue;
|
|
207
|
+
const base = byName.get(override.name) || {
|
|
208
|
+
name: override.name,
|
|
209
|
+
label: toLabel(override.name),
|
|
210
|
+
type: "string",
|
|
211
|
+
widget: "text",
|
|
212
|
+
required: false
|
|
213
|
+
};
|
|
214
|
+
merged.push({ ...base, ...override, name: override.name });
|
|
215
|
+
seen.add(override.name);
|
|
216
|
+
}
|
|
217
|
+
for (const field of inferred) {
|
|
218
|
+
if (!seen.has(field.name)) merged.push(field);
|
|
219
|
+
}
|
|
220
|
+
return merged;
|
|
221
|
+
}
|
|
222
|
+
function inferCapabilities(scope, requiresLogin) {
|
|
223
|
+
const caps = [];
|
|
224
|
+
if (scope === "page") caps.push("browser.page");
|
|
225
|
+
if (scope === "browser") caps.push("browser.context");
|
|
226
|
+
if (requiresLogin) caps.push("auth.login");
|
|
227
|
+
return caps;
|
|
228
|
+
}
|
|
229
|
+
function widgetFor(type, enumValues) {
|
|
230
|
+
if (enumValues) return "select";
|
|
231
|
+
if (type === "boolean") return "checkbox";
|
|
232
|
+
if (type === "number") return "number";
|
|
233
|
+
if (type === "array") return "multi-select";
|
|
234
|
+
if (type === "object") return "json";
|
|
235
|
+
return "text";
|
|
236
|
+
}
|
|
237
|
+
function summarizeZod(schema) {
|
|
238
|
+
const unwrapped = unwrapZod(schema);
|
|
239
|
+
if (unwrapped.typeName === "ZodArray") {
|
|
240
|
+
const def = unwrapped.schema?._def;
|
|
241
|
+
return {
|
|
242
|
+
type: "array",
|
|
243
|
+
items: summarizeZod(def?.type || def?.innerType)
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const shape = getObjectShape(schema);
|
|
247
|
+
if (!shape) {
|
|
248
|
+
return {
|
|
249
|
+
type: zodTypeToContractType(unwrapped.typeName),
|
|
250
|
+
required: !unwrapped.optional,
|
|
251
|
+
...unwrapped.description ? { description: unwrapped.description } : {}
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
return Object.fromEntries(
|
|
255
|
+
Object.entries(shape).map(([name, field]) => {
|
|
256
|
+
const inner = unwrapZod(field);
|
|
257
|
+
return [name, {
|
|
258
|
+
type: zodTypeToContractType(inner.typeName),
|
|
259
|
+
required: !inner.optional,
|
|
260
|
+
...inner.description ? { description: inner.description } : {}
|
|
261
|
+
}];
|
|
262
|
+
})
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
function getObjectShape(schema) {
|
|
266
|
+
const zod = schema;
|
|
267
|
+
const shapeOrFn = zod?.shape ?? zod?._def?.shape;
|
|
268
|
+
if (!shapeOrFn) return void 0;
|
|
269
|
+
return typeof shapeOrFn === "function" ? shapeOrFn() : shapeOrFn;
|
|
270
|
+
}
|
|
271
|
+
function toLabel(name) {
|
|
272
|
+
return name.replace(/([A-Z])/g, " $1").replace(/[-_]+/g, " ").replace(/\s+/g, " ").trim().replace(/^./, (char) => char.toUpperCase());
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/plugin/login-required-patch.ts
|
|
276
|
+
import { SiteInstanceImpl } from "@dyyz1993/xcli-core";
|
|
277
|
+
var patched = false;
|
|
278
|
+
function patchLoginRequired() {
|
|
279
|
+
if (patched) return;
|
|
280
|
+
patched = true;
|
|
281
|
+
const target = SiteInstanceImpl.prototype;
|
|
282
|
+
const originalCommand = target.command;
|
|
283
|
+
const wrapped = function(...args) {
|
|
284
|
+
const result = originalCommand.apply(this, args);
|
|
285
|
+
const [name, cmd] = args;
|
|
286
|
+
const loginRequired = cmd.loginRequired;
|
|
287
|
+
if (loginRequired) {
|
|
288
|
+
const commands = this.commands;
|
|
289
|
+
const entry = commands?.get(name);
|
|
290
|
+
if (entry) {
|
|
291
|
+
entry.loginRequired = loginRequired;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return result;
|
|
295
|
+
};
|
|
296
|
+
Object.defineProperty(target, "command", {
|
|
297
|
+
value: wrapped,
|
|
298
|
+
writable: true,
|
|
299
|
+
configurable: true
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/plugin/loader.ts
|
|
304
|
+
var DEFAULT_PLUGIN_DIRS = [".xcli/plugins", "../.xcli/plugins"];
|
|
305
|
+
var XBrowserPluginLoader = class {
|
|
306
|
+
core;
|
|
307
|
+
loader;
|
|
308
|
+
options;
|
|
309
|
+
constructor(options) {
|
|
310
|
+
patchLoginRequired();
|
|
311
|
+
this.options = options ?? {};
|
|
312
|
+
const cwd = this.options.cwd || process.cwd();
|
|
313
|
+
const coreConfig = {
|
|
314
|
+
name: "xbrowser",
|
|
315
|
+
version: "0.1.0",
|
|
316
|
+
description: "Browser automation CLI",
|
|
317
|
+
configDirName: ".xbrowser",
|
|
318
|
+
envPrefix: "XBROWSER",
|
|
319
|
+
pluginDirs: [
|
|
320
|
+
...DEFAULT_PLUGIN_DIRS,
|
|
321
|
+
resolve2(cwd, ".xcli/plugins")
|
|
322
|
+
]
|
|
323
|
+
};
|
|
324
|
+
this.core = new Core(coreConfig);
|
|
325
|
+
this.loader = this.core.loader;
|
|
326
|
+
}
|
|
327
|
+
getAPI() {
|
|
328
|
+
return this.loader.getAPI();
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Get the core instance for external use.
|
|
332
|
+
* @returns The xcli-core Core instance.
|
|
333
|
+
*/
|
|
334
|
+
getCore() {
|
|
335
|
+
return this.core;
|
|
336
|
+
}
|
|
337
|
+
getPlugin(id) {
|
|
338
|
+
return this.loader.getPlugin(id);
|
|
339
|
+
}
|
|
340
|
+
getPluginStatus(id) {
|
|
341
|
+
return this.loader.getPluginStatus(id);
|
|
342
|
+
}
|
|
343
|
+
getLoadedPlugins() {
|
|
344
|
+
return this.loader.getLoadedPlugins();
|
|
345
|
+
}
|
|
346
|
+
getPluginContract(siteName, commandName) {
|
|
347
|
+
const site = this.core.loader.getSite(siteName);
|
|
348
|
+
if (!site) return void 0;
|
|
349
|
+
const contract = buildPluginContract(site);
|
|
350
|
+
if (!commandName) return contract;
|
|
351
|
+
return contract.commands.find((command) => command.name === commandName);
|
|
352
|
+
}
|
|
353
|
+
async loadPlugin(pluginPath, id) {
|
|
354
|
+
return this.loader.loadPlugin(pluginPath, id);
|
|
355
|
+
}
|
|
356
|
+
async unloadPlugin(id) {
|
|
357
|
+
return this.loader.unloadPlugin(id);
|
|
358
|
+
}
|
|
359
|
+
async reloadPlugin(id) {
|
|
360
|
+
return this.loader.reloadPlugin(id);
|
|
361
|
+
}
|
|
362
|
+
async loadFromFunction(setup) {
|
|
363
|
+
return this.loader.loadFromFunction(setup);
|
|
364
|
+
}
|
|
365
|
+
async scanAndLoad() {
|
|
366
|
+
const cwd = this.options.cwd || process.cwd();
|
|
367
|
+
const globalDir = this.options.globalDir || resolve2(homedir(), ".xbrowser/plugins");
|
|
368
|
+
ensurePluginDependencies(globalDir);
|
|
369
|
+
const dirs = [
|
|
370
|
+
resolve2(cwd, ".xcli/plugins"),
|
|
371
|
+
resolve2(cwd, "../.xcli/plugins"),
|
|
372
|
+
this.options.userDir || resolve2(homedir(), ".xcli/plugins"),
|
|
373
|
+
globalDir
|
|
374
|
+
];
|
|
375
|
+
const loaded = [];
|
|
376
|
+
const seen = /* @__PURE__ */ new Set();
|
|
377
|
+
for (const dir of dirs) {
|
|
378
|
+
if (!existsSync3(dir)) continue;
|
|
379
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
380
|
+
for (const entry of entries) {
|
|
381
|
+
if (!entry.isDirectory()) continue;
|
|
382
|
+
if (seen.has(entry.name)) continue;
|
|
383
|
+
seen.add(entry.name);
|
|
384
|
+
const pluginDir = resolve2(dir, entry.name);
|
|
385
|
+
let indexPath = resolve2(pluginDir, "index.js");
|
|
386
|
+
if (!existsSync3(indexPath)) {
|
|
387
|
+
indexPath = resolve2(pluginDir, "index.ts");
|
|
388
|
+
}
|
|
389
|
+
if (!existsSync3(indexPath)) continue;
|
|
390
|
+
try {
|
|
391
|
+
if (!existsSync3(resolve2(pluginDir, "package.json"))) {
|
|
392
|
+
console.warn(`\u26A0\uFE0F Plugin "${entry.name}" has no package.json. Use "xbrowser create ${entry.name} --template static" for proper structure.`);
|
|
393
|
+
} else {
|
|
394
|
+
const metadata = PluginMetadataParser.parseFromPackageJson(pluginDir);
|
|
395
|
+
if (!metadata) {
|
|
396
|
+
console.warn(`\u26A0\uFE0F Plugin "${entry.name}" has package.json but no xbrowser metadata. Add { "xbrowser": { "description": "..." } } to package.json.`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
const instance = await this.loadPlugin(indexPath, entry.name);
|
|
400
|
+
loaded.push(instance);
|
|
401
|
+
} catch (err) {
|
|
402
|
+
if (process.env.XBROWSER_DEBUG) {
|
|
403
|
+
console.warn(`\u26A0\uFE0F Plugin "${entry.name}" load failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return loaded;
|
|
409
|
+
}
|
|
410
|
+
async unload() {
|
|
411
|
+
return this.loader.unload();
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
// src/utils/plugin-singleton.ts
|
|
416
|
+
var pluginLoader = null;
|
|
417
|
+
var pluginsScanned = false;
|
|
418
|
+
async function getPluginLoader() {
|
|
419
|
+
if (!pluginLoader) {
|
|
420
|
+
pluginLoader = new XBrowserPluginLoader();
|
|
421
|
+
}
|
|
422
|
+
if (!pluginsScanned) {
|
|
423
|
+
await pluginLoader.scanAndLoad();
|
|
424
|
+
pluginsScanned = true;
|
|
425
|
+
}
|
|
426
|
+
return pluginLoader;
|
|
427
|
+
}
|
|
428
|
+
function resetPluginLoader() {
|
|
429
|
+
pluginLoader = null;
|
|
430
|
+
pluginsScanned = false;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export {
|
|
434
|
+
getPluginLoader,
|
|
435
|
+
resetPluginLoader
|
|
436
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
killAllDaemonProcesses,
|
|
55
55
|
startDaemonProcess,
|
|
56
56
|
stopDaemonProcess
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-JPSFUFPG.js";
|
|
58
58
|
import {
|
|
59
59
|
errMsg
|
|
60
60
|
} from "./chunk-GDKLH7ZY.js";
|
|
@@ -949,10 +949,10 @@ var setCookieCommand = registerCommand({
|
|
|
949
949
|
description: "Set a cookie",
|
|
950
950
|
scope: "page",
|
|
951
951
|
parameters: z8.object({
|
|
952
|
-
name: z8.string(),
|
|
953
|
-
value: z8.string(),
|
|
954
|
-
domain: z8.string().optional(),
|
|
955
|
-
path: z8.string().optional(),
|
|
952
|
+
name: z8.coerce.string(),
|
|
953
|
+
value: z8.coerce.string(),
|
|
954
|
+
domain: z8.coerce.string().optional(),
|
|
955
|
+
path: z8.coerce.string().optional(),
|
|
956
956
|
expires: z8.number().optional(),
|
|
957
957
|
httpOnly: z8.boolean().optional(),
|
|
958
958
|
secure: z8.boolean().optional(),
|
|
@@ -7048,7 +7048,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7048
7048
|
params = result.data;
|
|
7049
7049
|
}
|
|
7050
7050
|
if (command.scope !== "cli" && !process.env.XBROWSER_DAEMON_WORKER) {
|
|
7051
|
-
const { forwardExec } = await import("./daemon-client-
|
|
7051
|
+
const { forwardExec } = await import("./daemon-client-XXKMJZZ7.js");
|
|
7052
7052
|
const result = await forwardExec(commandName, params, sessionName, extraOpts?.cdpEndpoint);
|
|
7053
7053
|
if (result) return result;
|
|
7054
7054
|
}
|
|
@@ -10272,6 +10272,18 @@ async function handlePlugin(args, options, mode) {
|
|
|
10272
10272
|
await (await getPluginLoader()).reloadPlugin(result.name);
|
|
10273
10273
|
} catch {
|
|
10274
10274
|
}
|
|
10275
|
+
try {
|
|
10276
|
+
const { daemonPing } = await import("./daemon-client-XXKMJZZ7.js");
|
|
10277
|
+
if (await daemonPing()) {
|
|
10278
|
+
await fetch("http://localhost:9224/rpc", {
|
|
10279
|
+
method: "POST",
|
|
10280
|
+
headers: { "Content-Type": "application/json" },
|
|
10281
|
+
body: JSON.stringify({ method: "plugins:reload", params: {} }),
|
|
10282
|
+
signal: AbortSignal.timeout(5e3)
|
|
10283
|
+
});
|
|
10284
|
+
}
|
|
10285
|
+
} catch {
|
|
10286
|
+
}
|
|
10275
10287
|
outputResult(
|
|
10276
10288
|
{ ok: true, name: result.name, source: result.source, path: result.path },
|
|
10277
10289
|
mode
|
|
@@ -10427,7 +10439,8 @@ async function handleRecord(args, options, mode) {
|
|
|
10427
10439
|
}
|
|
10428
10440
|
case "stop": {
|
|
10429
10441
|
const sessionName = options.session || "default";
|
|
10430
|
-
const
|
|
10442
|
+
const output = options.output || options.o;
|
|
10443
|
+
const result = await forwardRecordStop(sessionName, output);
|
|
10431
10444
|
if (!result.ok) {
|
|
10432
10445
|
outputError(String(result.error || "Failed to stop recording"));
|
|
10433
10446
|
return;
|
|
@@ -10436,6 +10449,7 @@ async function handleRecord(args, options, mode) {
|
|
|
10436
10449
|
ok: true,
|
|
10437
10450
|
message: "Recording stopped.",
|
|
10438
10451
|
sessionName,
|
|
10452
|
+
output: result.output || (output || SessionRecorder.getRecordingsDir(sessionName) + "/recording.json"),
|
|
10439
10453
|
actions: result.actions,
|
|
10440
10454
|
network: result.network,
|
|
10441
10455
|
durationMs: result.durationMs,
|
|
@@ -12549,7 +12563,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
12549
12563
|
}
|
|
12550
12564
|
const needsBrowser = cmdEntry.scope === "page" || cmdEntry.scope === "browser";
|
|
12551
12565
|
if (needsBrowser && !process.env.XBROWSER_DAEMON_WORKER) {
|
|
12552
|
-
const { forwardExec } = await import("./daemon-client-
|
|
12566
|
+
const { forwardExec } = await import("./daemon-client-XXKMJZZ7.js");
|
|
12553
12567
|
const userTimeout = typeof params.timeout === "number" && params.timeout > 0 ? params.timeout * 1e3 + 3e4 : void 0;
|
|
12554
12568
|
const result = await forwardExec(`${command}.${subCommand}`, params, sessionName, cdpEndpoint, userTimeout);
|
|
12555
12569
|
const resultData = result && typeof result === "object" && "data" in result ? result.data : void 0;
|
|
@@ -162,8 +162,10 @@ async function forwardNetworkInspect(sessionName, id) {
|
|
|
162
162
|
async function forwardRecordStart(session, url, cdpEndpoint) {
|
|
163
163
|
return rpcCall("record:start", { session, url, cdpEndpoint }, 15e3);
|
|
164
164
|
}
|
|
165
|
-
async function forwardRecordStop(session) {
|
|
166
|
-
|
|
165
|
+
async function forwardRecordStop(session, output) {
|
|
166
|
+
const params = { session };
|
|
167
|
+
if (output) params.output = output;
|
|
168
|
+
return rpcCall("record:stop", params, 1e4);
|
|
167
169
|
}
|
|
168
170
|
async function forwardRecordStatus(session) {
|
|
169
171
|
return rpcCall("record:status", { session }, 5e3);
|