@elementspanel/epanel-plugin-config 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2465 @@
1
+ /******/ (() => { // webpackBootstrap
2
+ /******/ "use strict";
3
+ /******/ var __webpack_modules__ = ({
4
+
5
+ /***/ "../common/src/array.ts"
6
+ (__unused_webpack_module, exports) {
7
+
8
+
9
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
10
+ exports.arrayUnique = arrayUnique;
11
+ function arrayUnique(arr, felidName) {
12
+ if (!felidName)
13
+ return Array.from(new Set(arr));
14
+ const map = new Map();
15
+ return arr.filter((v) => !map.has(v[felidName]) && map.set(v[felidName], v));
16
+ }
17
+
18
+
19
+ /***/ },
20
+
21
+ /***/ "../common/src/docker_utils.ts"
22
+ (__unused_webpack_module, exports) {
23
+
24
+
25
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
26
+ exports.normalizeDockerArchitecture = normalizeDockerArchitecture;
27
+ exports.normalizeDockerOS = normalizeDockerOS;
28
+ exports.normalizeDockerPlatform = normalizeDockerPlatform;
29
+ /**
30
+ * Normalizes Docker platform architecture names to standard format
31
+ * @param architecture The architecture string from Docker manifest
32
+ * @returns Normalized architecture string (amd64, arm64, arm, or original if not recognized)
33
+ */
34
+ function normalizeDockerArchitecture(architecture) {
35
+ if (!architecture) {
36
+ return "amd64"; // default fallback
37
+ }
38
+ // Return original if not one of the standard architectures
39
+ return architecture;
40
+ }
41
+ /**
42
+ * Normalizes Docker platform OS name
43
+ * @param os The OS string from Docker manifest
44
+ * @returns Normalized OS string (defaults to "linux" if not provided)
45
+ */
46
+ function normalizeDockerOS(os) {
47
+ return os || "linux";
48
+ }
49
+ /**
50
+ * Normalizes a Docker platform from manifest format to standard platform string
51
+ * @param platform Platform object from Docker manifest with os and architecture
52
+ * @returns Normalized platform string in format "os/arch" (e.g., "linux/amd64")
53
+ */
54
+ function normalizeDockerPlatform(platform) {
55
+ const os = normalizeDockerOS(platform.os);
56
+ const arch = normalizeDockerArchitecture(platform.architecture);
57
+ return `${os}/${arch}`;
58
+ }
59
+
60
+
61
+ /***/ },
62
+
63
+ /***/ "../common/src/global_variable.ts"
64
+ (__unused_webpack_module, exports) {
65
+
66
+
67
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
68
+ class GlobalVariable {
69
+ static map = new Map();
70
+ static set(k, v) {
71
+ GlobalVariable.map.set(k, v);
72
+ }
73
+ static get(k, def) {
74
+ if (GlobalVariable.map.has(k)) {
75
+ return GlobalVariable.map.get(k);
76
+ }
77
+ else {
78
+ return def;
79
+ }
80
+ }
81
+ static del(k) {
82
+ return GlobalVariable.map.delete(k);
83
+ }
84
+ }
85
+ exports["default"] = GlobalVariable;
86
+
87
+
88
+ /***/ },
89
+
90
+ /***/ "../common/src/index.ts"
91
+ (__unused_webpack_module, exports, __webpack_require__) {
92
+
93
+
94
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
95
+ if (k2 === undefined) k2 = k;
96
+ var desc = Object.getOwnPropertyDescriptor(m, k);
97
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
98
+ desc = { enumerable: true, get: function() { return m[k]; } };
99
+ }
100
+ Object.defineProperty(o, k2, desc);
101
+ }) : (function(o, m, k, k2) {
102
+ if (k2 === undefined) k2 = k;
103
+ o[k2] = m[k];
104
+ }));
105
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
106
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
107
+ };
108
+ var __importDefault = (this && this.__importDefault) || function (mod) {
109
+ return (mod && mod.__esModule) ? mod : { "default": mod };
110
+ };
111
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
112
+ exports.StorageSubsystem = exports.MCServerStatus = exports.InstanceStreamListener = exports.GlobalVariable = exports.normalizeDockerPlatform = exports.normalizeDockerOS = exports.normalizeDockerArchitecture = exports.removeTrail = exports.uninstallNpmPlugin = exports.npmPluginRoots = exports.listNpmPlugins = exports.installNpmPlugin = exports.currentProjectRoot = exports.currentPackageRoot = exports.sortPlugins = exports.resolvePluginEntry = exports.readPluginManifest = exports.createFrontendPluginMetadata = exports.discoverPluginsFromRoots = exports.discoverPlugins = exports.arrayUnique = exports.toText = exports.toNumber = exports.toBoolean = exports.supposeValue = exports.isEmpty = exports.configureEntityParams = exports.systemInfo = exports.QueryWrapper = exports.QueryMapWrapper = exports.MySqlSource = exports.LocalFileSource = exports.killProcess = exports.ProcessWrapper = void 0;
113
+ const global_variable_1 = __importDefault(__webpack_require__("../common/src/global_variable.ts"));
114
+ exports.GlobalVariable = global_variable_1.default;
115
+ const instance_stream_1 = __importDefault(__webpack_require__("../common/src/instance_stream.ts"));
116
+ exports.InstanceStreamListener = instance_stream_1.default;
117
+ const system_storage_1 = __importDefault(__webpack_require__("../common/src/system_storage.ts"));
118
+ exports.StorageSubsystem = system_storage_1.default;
119
+ const mcping_1 = __importDefault(__webpack_require__("../common/src/mcping.ts"));
120
+ exports.MCServerStatus = mcping_1.default;
121
+ var process_tools_1 = __webpack_require__("../common/src/process_tools.ts");
122
+ Object.defineProperty(exports, "ProcessWrapper", ({ enumerable: true, get: function () { return process_tools_1.ProcessWrapper; } }));
123
+ Object.defineProperty(exports, "killProcess", ({ enumerable: true, get: function () { return process_tools_1.killProcess; } }));
124
+ var query_wrapper_1 = __webpack_require__("../common/src/query_wrapper.ts");
125
+ Object.defineProperty(exports, "LocalFileSource", ({ enumerable: true, get: function () { return query_wrapper_1.LocalFileSource; } }));
126
+ Object.defineProperty(exports, "MySqlSource", ({ enumerable: true, get: function () { return query_wrapper_1.MySqlSource; } }));
127
+ Object.defineProperty(exports, "QueryMapWrapper", ({ enumerable: true, get: function () { return query_wrapper_1.QueryMapWrapper; } }));
128
+ Object.defineProperty(exports, "QueryWrapper", ({ enumerable: true, get: function () { return query_wrapper_1.QueryWrapper; } }));
129
+ var system_info_1 = __webpack_require__("../common/src/system_info.ts");
130
+ Object.defineProperty(exports, "systemInfo", ({ enumerable: true, get: function () { return system_info_1.systemInfo; } }));
131
+ var typecheck_1 = __webpack_require__("../common/src/typecheck.ts");
132
+ Object.defineProperty(exports, "configureEntityParams", ({ enumerable: true, get: function () { return typecheck_1.configureEntityParams; } }));
133
+ Object.defineProperty(exports, "isEmpty", ({ enumerable: true, get: function () { return typecheck_1.isEmpty; } }));
134
+ Object.defineProperty(exports, "supposeValue", ({ enumerable: true, get: function () { return typecheck_1.supposeValue; } }));
135
+ Object.defineProperty(exports, "toBoolean", ({ enumerable: true, get: function () { return typecheck_1.toBoolean; } }));
136
+ Object.defineProperty(exports, "toNumber", ({ enumerable: true, get: function () { return typecheck_1.toNumber; } }));
137
+ Object.defineProperty(exports, "toText", ({ enumerable: true, get: function () { return typecheck_1.toText; } }));
138
+ var array_1 = __webpack_require__("../common/src/array.ts");
139
+ Object.defineProperty(exports, "arrayUnique", ({ enumerable: true, get: function () { return array_1.arrayUnique; } }));
140
+ __exportStar(__webpack_require__("../common/src/plugin_contract.ts"), exports);
141
+ __exportStar(__webpack_require__("../common/src/plugin_overrides.ts"), exports);
142
+ __exportStar(__webpack_require__("../common/src/plugin_lifecycle.ts"), exports);
143
+ __exportStar(__webpack_require__("../common/src/plugin_revision.ts"), exports);
144
+ __exportStar(__webpack_require__("../common/src/runtime_paths.ts"), exports);
145
+ var plugin_manifest_1 = __webpack_require__("../common/src/plugin_manifest.ts");
146
+ Object.defineProperty(exports, "discoverPlugins", ({ enumerable: true, get: function () { return plugin_manifest_1.discoverPlugins; } }));
147
+ Object.defineProperty(exports, "discoverPluginsFromRoots", ({ enumerable: true, get: function () { return plugin_manifest_1.discoverPluginsFromRoots; } }));
148
+ Object.defineProperty(exports, "createFrontendPluginMetadata", ({ enumerable: true, get: function () { return plugin_manifest_1.createFrontendPluginMetadata; } }));
149
+ Object.defineProperty(exports, "readPluginManifest", ({ enumerable: true, get: function () { return plugin_manifest_1.readPluginManifest; } }));
150
+ Object.defineProperty(exports, "resolvePluginEntry", ({ enumerable: true, get: function () { return plugin_manifest_1.resolvePluginEntry; } }));
151
+ Object.defineProperty(exports, "sortPlugins", ({ enumerable: true, get: function () { return plugin_manifest_1.sortPlugins; } }));
152
+ var plugin_npm_1 = __webpack_require__("../common/src/plugin_npm.ts");
153
+ Object.defineProperty(exports, "currentPackageRoot", ({ enumerable: true, get: function () { return plugin_npm_1.currentPackageRoot; } }));
154
+ Object.defineProperty(exports, "currentProjectRoot", ({ enumerable: true, get: function () { return plugin_npm_1.currentProjectRoot; } }));
155
+ Object.defineProperty(exports, "installNpmPlugin", ({ enumerable: true, get: function () { return plugin_npm_1.installNpmPlugin; } }));
156
+ Object.defineProperty(exports, "listNpmPlugins", ({ enumerable: true, get: function () { return plugin_npm_1.listNpmPlugins; } }));
157
+ Object.defineProperty(exports, "npmPluginRoots", ({ enumerable: true, get: function () { return plugin_npm_1.npmPluginRoots; } }));
158
+ Object.defineProperty(exports, "uninstallNpmPlugin", ({ enumerable: true, get: function () { return plugin_npm_1.uninstallNpmPlugin; } }));
159
+ var string_utils_1 = __webpack_require__("../common/src/string_utils.ts");
160
+ Object.defineProperty(exports, "removeTrail", ({ enumerable: true, get: function () { return string_utils_1.removeTrail; } }));
161
+ var docker_utils_1 = __webpack_require__("../common/src/docker_utils.ts");
162
+ Object.defineProperty(exports, "normalizeDockerArchitecture", ({ enumerable: true, get: function () { return docker_utils_1.normalizeDockerArchitecture; } }));
163
+ Object.defineProperty(exports, "normalizeDockerOS", ({ enumerable: true, get: function () { return docker_utils_1.normalizeDockerOS; } }));
164
+ Object.defineProperty(exports, "normalizeDockerPlatform", ({ enumerable: true, get: function () { return docker_utils_1.normalizeDockerPlatform; } }));
165
+
166
+
167
+ /***/ },
168
+
169
+ /***/ "../common/src/instance_stream.ts"
170
+ (__unused_webpack_module, exports) {
171
+
172
+
173
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
174
+ // Application instance data stream forwarding adapter
175
+ class InstanceStreamListener {
176
+ listenMap = new Map();
177
+ disconnectHandlers = new Map();
178
+ requestForward(socket, instanceUuid) {
179
+ const sockets = this.listenMap.get(instanceUuid) ?? [];
180
+ if (sockets.some((listener) => listener.id === socket.id))
181
+ return;
182
+ sockets.push(socket);
183
+ this.listenMap.set(instanceUuid, sockets);
184
+ if (!this.disconnectHandlers.has(socket)) {
185
+ const disconnect = () => {
186
+ for (const uuid of this.listenMap.keys())
187
+ this.cannelForward(socket, uuid);
188
+ };
189
+ this.disconnectHandlers.set(socket, disconnect);
190
+ socket.once("disconnect", disconnect);
191
+ }
192
+ }
193
+ cannelForward(socket, instanceUuid) {
194
+ const remaining = this.listenMap
195
+ .get(instanceUuid)
196
+ ?.filter((listener) => listener.id !== socket.id);
197
+ if (remaining?.length)
198
+ this.listenMap.set(instanceUuid, remaining);
199
+ else
200
+ this.listenMap.delete(instanceUuid);
201
+ if (![...this.listenMap.values()].some((sockets) => sockets.includes(socket))) {
202
+ const disconnect = this.disconnectHandlers.get(socket);
203
+ if (disconnect)
204
+ socket.off("disconnect", disconnect);
205
+ this.disconnectHandlers.delete(socket);
206
+ }
207
+ }
208
+ forward(instanceUuid, data) {
209
+ this.forwardViaCallback(instanceUuid, (socket) => socket.emit("instance/stdout", data));
210
+ }
211
+ forwardViaCallback(instanceUuid, callback) {
212
+ for (const socket of this.listenMap.get(instanceUuid) ?? []) {
213
+ if (socket.connected)
214
+ callback(socket);
215
+ else
216
+ this.cannelForward(socket, instanceUuid);
217
+ }
218
+ }
219
+ hasListenInstance(instanceUuid) {
220
+ return this.listenMap.get(instanceUuid)?.some((socket) => socket.connected) ?? false;
221
+ }
222
+ }
223
+ exports["default"] = InstanceStreamListener;
224
+
225
+
226
+ /***/ },
227
+
228
+ /***/ "../common/src/mcping.ts"
229
+ (__unused_webpack_module, exports, __webpack_require__) {
230
+
231
+
232
+ var __importDefault = (this && this.__importDefault) || function (mod) {
233
+ return (mod && mod.__esModule) ? mod : { "default": mod };
234
+ };
235
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
236
+ const net_1 = __importDefault(__webpack_require__("net"));
237
+ class PingMinecraftServer {
238
+ port;
239
+ host;
240
+ status;
241
+ client;
242
+ pending;
243
+ constructor(port, host) {
244
+ this.port = port;
245
+ this.host = host;
246
+ this.status = this.offlineStatus();
247
+ }
248
+ offlineStatus() {
249
+ return {
250
+ online: false,
251
+ host: this.host,
252
+ port: this.port,
253
+ version: "",
254
+ motd: "",
255
+ current_players: 0,
256
+ max_players: 0,
257
+ latency: 0
258
+ };
259
+ }
260
+ getStatus() {
261
+ if (this.pending)
262
+ return this.pending;
263
+ this.status = this.offlineStatus();
264
+ this.pending = new Promise((resolve, reject) => {
265
+ const startedAt = Date.now();
266
+ const client = net_1.default.connect({ host: this.host, port: this.port });
267
+ this.client = client;
268
+ let received = Buffer.alloc(0);
269
+ let settled = false;
270
+ const finish = (error) => {
271
+ if (settled)
272
+ return;
273
+ settled = true;
274
+ clearTimeout(timeout);
275
+ client.destroy();
276
+ this.client = undefined;
277
+ if (error)
278
+ reject(error);
279
+ else
280
+ resolve(this.status);
281
+ };
282
+ const timeout = setTimeout(() => finish(new Error("Minecraft status request timed out")), 15000);
283
+ client.once("connect", () => client.write(Buffer.from([0xfe, 0x01])));
284
+ client.on("data", (chunk) => {
285
+ if (settled)
286
+ return;
287
+ try {
288
+ received = Buffer.concat([received, chunk]);
289
+ // Legacy status packets contain an unsigned UTF-16 character count.
290
+ if (received.length > 3 + 0xffff * 2)
291
+ throw new Error("Minecraft status response is too large");
292
+ if (received.length < 3)
293
+ return;
294
+ if (received[0] !== 0xff)
295
+ throw new Error("Invalid Minecraft status packet");
296
+ const length = 3 + received.readUInt16BE(1) * 2;
297
+ if (received.length < length)
298
+ return;
299
+ const fields = Buffer.from(received.subarray(3, length))
300
+ .swap16()
301
+ .toString("utf16le")
302
+ .split("\0");
303
+ if (fields.length !== 6 ||
304
+ fields[0] !== "§1" ||
305
+ !/^\d+$/.test(fields[4]) ||
306
+ !/^\d+$/.test(fields[5])) {
307
+ throw new Error("Invalid Minecraft status response");
308
+ }
309
+ const currentPlayers = Number(fields[4]);
310
+ const maxPlayers = Number(fields[5]);
311
+ if (!Number.isSafeInteger(currentPlayers) || !Number.isSafeInteger(maxPlayers)) {
312
+ throw new Error("Invalid Minecraft player count");
313
+ }
314
+ this.status = {
315
+ host: this.host,
316
+ port: this.port,
317
+ online: true,
318
+ version: fields[2],
319
+ motd: fields[3],
320
+ current_players: currentPlayers,
321
+ max_players: maxPlayers,
322
+ latency: Date.now() - startedAt
323
+ };
324
+ finish();
325
+ }
326
+ catch (error) {
327
+ finish(error instanceof Error ? error : new Error(String(error)));
328
+ }
329
+ });
330
+ client.once("error", finish);
331
+ client.once("close", () => finish(received.length ? new Error("Incomplete Minecraft status response") : undefined));
332
+ }).finally(() => {
333
+ this.pending = undefined;
334
+ });
335
+ return this.pending;
336
+ }
337
+ asyncStatus() {
338
+ return this.getStatus();
339
+ }
340
+ }
341
+ exports["default"] = PingMinecraftServer;
342
+
343
+
344
+ /***/ },
345
+
346
+ /***/ "../common/src/plugin_contract.ts"
347
+ (__unused_webpack_module, exports) {
348
+
349
+
350
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
351
+ exports.PLUGIN_SDK_VERSION = exports.PLUGIN_API_VERSION = void 0;
352
+ exports.validatePluginCompatibility = validatePluginCompatibility;
353
+ exports.resolvePluginDependencyOrder = resolvePluginDependencyOrder;
354
+ exports.createPluginQueue = createPluginQueue;
355
+ exports.validatePluginSettings = validatePluginSettings;
356
+ /** Versioned contract shared by the host, plugin compiler and package registry. */
357
+ exports.PLUGIN_API_VERSION = 1;
358
+ exports.PLUGIN_SDK_VERSION = "1.0.0";
359
+ /** Omitted metadata denotes a legacy package. Unknown versions never execute. */
360
+ function validatePluginCompatibility(value) {
361
+ if (value === undefined)
362
+ return;
363
+ if (!value || typeof value !== "object" || Array.isArray(value))
364
+ throw new Error("Invalid ElementsPanel plugin compatibility metadata.");
365
+ const contract = value;
366
+ if (contract.api !== exports.PLUGIN_API_VERSION || (contract.sdk !== undefined && contract.sdk !== 1))
367
+ throw new Error(`Unsupported ElementsPanel plugin API/SDK: ${JSON.stringify(value)}`);
368
+ }
369
+ /**
370
+ * Stable topological ordering shared by plugin hosts.
371
+ *
372
+ * Invalid nodes are kept out of `ordered`: direct missing dependencies are
373
+ * reported first, then anything depending on an invalid node, and finally
374
+ * cycles (plus nodes blocked behind a cycle). Independent nodes retain the
375
+ * caller's priority/id comparator.
376
+ */
377
+ function resolvePluginDependencyOrder(items, options) {
378
+ const sorted = [...items].sort(options.compare);
379
+ const byId = new Map(sorted.map((item) => [options.id(item), item]));
380
+ const dependencies = new Map(sorted.map((item) => [
381
+ options.id(item),
382
+ [...new Set(options.dependencies(item) || [])].filter(Boolean)
383
+ ]));
384
+ const issues = new Map();
385
+ for (const item of sorted) {
386
+ const id = options.id(item);
387
+ const missing = dependencies.get(id).filter((dependency) => !byId.has(dependency));
388
+ if (!missing.length)
389
+ continue;
390
+ issues.set(id, {
391
+ type: "missing-dependency",
392
+ dependencies: missing,
393
+ message: `Missing frontend plugin dependencies: ${missing.join(", ")}`
394
+ });
395
+ }
396
+ const markUnavailable = () => {
397
+ let changed = false;
398
+ for (const item of sorted) {
399
+ const id = options.id(item);
400
+ if (issues.has(id))
401
+ continue;
402
+ const unavailable = dependencies.get(id).filter((dependency) => issues.has(dependency));
403
+ if (!unavailable.length)
404
+ continue;
405
+ issues.set(id, {
406
+ type: "dependency-unavailable",
407
+ dependencies: unavailable,
408
+ message: `Frontend plugin dependencies are unavailable: ${unavailable.join(", ")}`
409
+ });
410
+ changed = true;
411
+ }
412
+ return changed;
413
+ };
414
+ while (markUnavailable()) {
415
+ // Propagate direct graph failures before sorting the remaining nodes.
416
+ }
417
+ const candidates = sorted.filter((item) => !issues.has(options.id(item)));
418
+ const candidateIds = new Set(candidates.map(options.id));
419
+ const indegree = new Map(candidates.map((item) => [options.id(item), 0]));
420
+ const dependents = new Map();
421
+ for (const item of candidates) {
422
+ const id = options.id(item);
423
+ for (const dependency of dependencies.get(id)) {
424
+ if (!candidateIds.has(dependency))
425
+ continue;
426
+ indegree.set(id, indegree.get(id) + 1);
427
+ const bucket = dependents.get(dependency) || [];
428
+ bucket.push(item);
429
+ dependents.set(dependency, bucket);
430
+ }
431
+ }
432
+ const ready = candidates.filter((item) => indegree.get(options.id(item)) === 0);
433
+ ready.sort(options.compare);
434
+ const ordered = [];
435
+ while (ready.length) {
436
+ const item = ready.shift();
437
+ const id = options.id(item);
438
+ ordered.push(item);
439
+ for (const dependent of dependents.get(id) || []) {
440
+ const dependentId = options.id(dependent);
441
+ const next = indegree.get(dependentId) - 1;
442
+ indegree.set(dependentId, next);
443
+ if (next === 0) {
444
+ ready.push(dependent);
445
+ ready.sort(options.compare);
446
+ }
447
+ }
448
+ }
449
+ const unresolved = candidates.filter((item) => !ordered.includes(item));
450
+ if (unresolved.length) {
451
+ const unresolvedIds = new Set(unresolved.map(options.id));
452
+ for (const item of unresolved) {
453
+ const id = options.id(item);
454
+ const blockedBy = dependencies.get(id).filter((dependency) => unresolvedIds.has(dependency));
455
+ issues.set(id, {
456
+ type: "dependency-cycle",
457
+ dependencies: blockedBy,
458
+ message: `Frontend plugin dependency cycle: ${[id, ...blockedBy].join(" -> ")}`
459
+ });
460
+ }
461
+ }
462
+ return { ordered, issues };
463
+ }
464
+ /** A queue survives failures, so a failed change cannot block subsequent repairs. */
465
+ function createPluginQueue() {
466
+ let tail = Promise.resolve();
467
+ return (operation) => {
468
+ const next = tail.then(operation);
469
+ tail = next.catch(() => undefined);
470
+ return next;
471
+ };
472
+ }
473
+ /** Validate the form contract on the server before invoking plugin-owned validation. */
474
+ function validatePluginSettings(fields, values) {
475
+ if (!values || typeof values !== "object" || Array.isArray(values))
476
+ throw new Error("Invalid plugin settings.");
477
+ const input = values;
478
+ for (const field of fields) {
479
+ if (!field.key || field.type === "link")
480
+ continue;
481
+ const value = input[field.key];
482
+ if (value === undefined || value === null) {
483
+ if (field.required)
484
+ throw new Error(`${field.title || field.key}: required`);
485
+ continue;
486
+ }
487
+ if (field.type === "list") {
488
+ if (!Array.isArray(value) || value.length > (field.maxItems ?? 100))
489
+ throw new Error(`${field.title || field.key}: invalid list`);
490
+ for (const item of value)
491
+ validatePluginSettings(field.fields || [], item);
492
+ continue;
493
+ }
494
+ const valid = field.type === "number"
495
+ ? typeof value === "number" &&
496
+ Number.isFinite(value) &&
497
+ (field.min === undefined || value >= field.min) &&
498
+ (field.max === undefined || value <= field.max)
499
+ : field.type === "boolean"
500
+ ? typeof value === "boolean"
501
+ : field.type === "select"
502
+ ? field.options?.some((option) => option.value === value)
503
+ : typeof value === "string";
504
+ if (!valid)
505
+ throw new Error(`${field.title || field.key}: invalid value`);
506
+ }
507
+ }
508
+
509
+
510
+ /***/ },
511
+
512
+ /***/ "../common/src/plugin_lifecycle.ts"
513
+ (__unused_webpack_module, exports) {
514
+
515
+
516
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
517
+ exports.trackPluginDisposal = trackPluginDisposal;
518
+ /** Cordis 3 starts async disposers without awaiting them. Track them per host. */
519
+ function trackPluginDisposal(ctx) {
520
+ const pending = new Set();
521
+ const failures = [];
522
+ const tracked = new WeakSet();
523
+ const scopes = new WeakSet();
524
+ const observe = (scope) => {
525
+ if (scopes.has(scope))
526
+ return;
527
+ scopes.add(scope);
528
+ const reset = scope.reset;
529
+ scope.reset = function () {
530
+ this.disposables = this.disposables.map((dispose) => {
531
+ if (tracked.has(dispose))
532
+ return dispose;
533
+ const wrapper = (...args) => {
534
+ let result;
535
+ try {
536
+ result = dispose(...args);
537
+ }
538
+ catch (error) {
539
+ failures.push(error);
540
+ throw error;
541
+ }
542
+ if (result && typeof result.then === "function") {
543
+ const task = Promise.resolve(result)
544
+ .catch((error) => {
545
+ failures.push(error);
546
+ })
547
+ .finally(() => pending.delete(task));
548
+ pending.add(task);
549
+ }
550
+ return result;
551
+ };
552
+ // Cordis marks persistent disposers with a symbol; preserve its semantics.
553
+ for (const key of Object.getOwnPropertySymbols(dispose))
554
+ Object.defineProperty(wrapper, key, Object.getOwnPropertyDescriptor(dispose, key));
555
+ tracked.add(wrapper);
556
+ return wrapper;
557
+ });
558
+ return reset.call(this);
559
+ };
560
+ };
561
+ ctx.on("internal/runtime", observe);
562
+ ctx.on("internal/fork", observe);
563
+ return async () => {
564
+ do {
565
+ await ctx.events.flush();
566
+ await Promise.all([...pending]);
567
+ } while (pending.size);
568
+ if (failures.length) {
569
+ const errors = failures.splice(0);
570
+ throw new Error(`Plugin cleanup failed: ${errors.map(String).join("; ")}`);
571
+ }
572
+ };
573
+ }
574
+
575
+
576
+ /***/ },
577
+
578
+ /***/ "../common/src/plugin_manifest.ts"
579
+ (__unused_webpack_module, exports, __webpack_require__) {
580
+
581
+
582
+ var __importDefault = (this && this.__importDefault) || function (mod) {
583
+ return (mod && mod.__esModule) ? mod : { "default": mod };
584
+ };
585
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
586
+ exports.createFrontendPluginMetadata = createFrontendPluginMetadata;
587
+ exports.readPluginManifest = readPluginManifest;
588
+ exports.resolvePluginEntry = resolvePluginEntry;
589
+ exports.discoverPlugins = discoverPlugins;
590
+ exports.discoverPluginsFromRoots = discoverPluginsFromRoots;
591
+ exports.sortPlugins = sortPlugins;
592
+ const node_fs_1 = __importDefault(__webpack_require__("node:fs"));
593
+ const node_path_1 = __importDefault(__webpack_require__("node:path"));
594
+ function stringArray(value) {
595
+ if (!Array.isArray(value))
596
+ return undefined;
597
+ const result = value.filter((item) => typeof item === "string" && !!item.trim());
598
+ return result.length ? [...new Set(result.map((item) => item.trim()))] : undefined;
599
+ }
600
+ /**
601
+ * Redacts a full plugin manifest to the fields a browser is allowed to see.
602
+ * Backend `config` may contain credentials, so only `frontendConfig` crosses
603
+ * this boundary.
604
+ */
605
+ function createFrontendPluginMetadata(manifest, extras = {}) {
606
+ const frontendInject = stringArray(manifest.frontendInject);
607
+ return {
608
+ id: manifest.id,
609
+ ...(manifest.version === undefined ? {} : { version: manifest.version }),
610
+ ...(manifest.description === undefined ? {} : { description: manifest.description }),
611
+ ...(manifest.priority === undefined ? {} : { priority: manifest.priority }),
612
+ ...(manifest.elements === undefined ? {} : { elements: manifest.elements }),
613
+ ...(frontendInject === undefined ? {} : { frontendInject }),
614
+ ...(manifest.frontendImmediate === undefined
615
+ ? {}
616
+ : { frontendImmediate: Boolean(manifest.frontendImmediate) }),
617
+ ...(manifest.frontendRequired === undefined
618
+ ? {}
619
+ : { frontendRequired: Boolean(manifest.frontendRequired) }),
620
+ ...(manifest.frontendConfig === undefined ? {} : { config: manifest.frontendConfig }),
621
+ ...(extras.restartRequired === undefined ? {} : extras)
622
+ };
623
+ }
624
+ const MANIFEST_FILES = ["plugin.json", "manifest.json", "package.json"];
625
+ /** Reads the manifest of one plugin directory, or null if it has none. */
626
+ function readPluginManifest(directory, onWarning, fallbackId = node_path_1.default.basename(directory)) {
627
+ for (const file of MANIFEST_FILES) {
628
+ const filePath = node_path_1.default.join(directory, file);
629
+ if (!node_fs_1.default.existsSync(filePath))
630
+ continue;
631
+ try {
632
+ const value = JSON.parse(node_fs_1.default.readFileSync(filePath, "utf8"));
633
+ if (!value || typeof value !== "object" || Array.isArray(value))
634
+ return null;
635
+ const id = typeof value.id === "string" ? value.id : fallbackId;
636
+ if (!id.trim())
637
+ return null;
638
+ return { ...value, id: id.trim() };
639
+ }
640
+ catch (error) {
641
+ onWarning?.(`Failed to read plugin manifest: ${filePath}`, error);
642
+ return null;
643
+ }
644
+ }
645
+ return null;
646
+ }
647
+ /**
648
+ * Resolves one plugin's entry module. Returns null when the manifest names an
649
+ * entry that does not exist, or when none of the fallback candidates is present.
650
+ * Paths that would escape the plugin directory are rejected.
651
+ */
652
+ function resolvePluginEntry(directory, manifest, options) {
653
+ const configured = options.entryFields
654
+ .map((field) => manifest[field])
655
+ .find((entry) => typeof entry === "string" && entry.length > 0);
656
+ const candidates = configured ? [configured] : options.entryCandidates ?? [];
657
+ const root = node_path_1.default.resolve(directory);
658
+ for (const candidate of candidates) {
659
+ const entry = node_path_1.default.resolve(directory, candidate);
660
+ if (entry.startsWith(`${root}${node_path_1.default.sep}`) && node_fs_1.default.existsSync(entry))
661
+ return entry;
662
+ }
663
+ return null;
664
+ }
665
+ function discoverPluginDirectory(directory, options, folder = node_path_1.default.basename(directory)) {
666
+ // Npm packages expose a side directory; its name is not the public plugin id.
667
+ const manifest = readPluginManifest(directory, options.onWarning, folder);
668
+ if (!manifest)
669
+ return null;
670
+ if (manifest.enabled === false && !options.includeDisabled)
671
+ return null;
672
+ const entry = resolvePluginEntry(directory, manifest, options);
673
+ if (!entry && options.entryFields.some((field) => typeof manifest[field] === "string")) {
674
+ options.onWarning?.(`Plugin "${manifest.id}" has no valid entry module.`);
675
+ return null;
676
+ }
677
+ return { manifest, directory, folder, entry: entry ?? undefined };
678
+ }
679
+ /**
680
+ * Lists the usable plugins below `root`, in load order: ascending `priority`,
681
+ * then by id so the order is stable. Duplicate ids are dropped, and disabled
682
+ * ones too unless `includeDisabled` says otherwise. A plugin whose manifest names an entry that cannot be resolved is
683
+ * dropped too; one that names none keeps `entry` undefined, because a plugin may
684
+ * legitimately contribute to only one side of the panel.
685
+ */
686
+ function discoverPlugins(root, options) {
687
+ if (!node_fs_1.default.existsSync(root))
688
+ return [];
689
+ const plugins = [];
690
+ const seenIds = new Set();
691
+ for (const item of node_fs_1.default.readdirSync(root, { withFileTypes: true })) {
692
+ if (!item.isDirectory())
693
+ continue;
694
+ const directory = node_path_1.default.join(root, item.name);
695
+ const plugin = discoverPluginDirectory(directory, options, item.name);
696
+ if (!plugin || seenIds.has(plugin.manifest.id)) {
697
+ if (plugin && seenIds.has(plugin.manifest.id)) {
698
+ options.onWarning?.(`Ignoring duplicate plugin id: ${plugin.manifest.id}`);
699
+ }
700
+ continue;
701
+ }
702
+ seenIds.add(plugin.manifest.id);
703
+ plugins.push(plugin);
704
+ }
705
+ return sortPlugins(plugins);
706
+ }
707
+ /**
708
+ * Discovers plugins from several roots while keeping the first root's plugin
709
+ * when ids collide. The built-in root is intentionally passed first by the
710
+ * panel and daemon loaders, so an installed market plugin cannot shadow a
711
+ * built-in npm dependency. A root can also force an installed project plugin
712
+ * into the disabled state; the user-owned override layer may enable it later.
713
+ */
714
+ function discoverPluginsFromRoots(roots, options) {
715
+ const discovered = [];
716
+ const seenIds = new Set();
717
+ for (const root of roots) {
718
+ const isDirectPlugin = MANIFEST_FILES.some((file) => node_fs_1.default.existsSync(node_path_1.default.join(root.directory, file)));
719
+ const plugins = isDirectPlugin
720
+ ? [discoverPluginDirectory(root.directory, options, root.folder)]
721
+ : discoverPlugins(root.directory, options);
722
+ for (const plugin of plugins) {
723
+ if (!plugin)
724
+ continue;
725
+ const manifest = root.defaultEnabled === false ? { ...plugin.manifest, enabled: false } : plugin.manifest;
726
+ if (manifest.enabled === false && !options.includeDisabled)
727
+ continue;
728
+ if (seenIds.has(plugin.manifest.id)) {
729
+ options.onWarning?.(`Ignoring duplicate plugin id: ${plugin.manifest.id}`);
730
+ continue;
731
+ }
732
+ seenIds.add(plugin.manifest.id);
733
+ discovered.push({
734
+ ...plugin,
735
+ manifest,
736
+ ...(root.folder ? { folder: root.folder } : {})
737
+ });
738
+ }
739
+ }
740
+ return sortPlugins(discovered);
741
+ }
742
+ /** Load order: ascending `priority`, then by id so it never depends on the filesystem. */
743
+ function sortPlugins(plugins) {
744
+ return plugins.sort((a, b) => (Number(a.manifest.priority) || 0) - (Number(b.manifest.priority) || 0) ||
745
+ a.manifest.id.localeCompare(b.manifest.id));
746
+ }
747
+
748
+
749
+ /***/ },
750
+
751
+ /***/ "../common/src/plugin_npm.ts"
752
+ (__unused_webpack_module, exports, __webpack_require__) {
753
+
754
+
755
+ var __importDefault = (this && this.__importDefault) || function (mod) {
756
+ return (mod && mod.__esModule) ? mod : { "default": mod };
757
+ };
758
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
759
+ exports.currentPackageRoot = currentPackageRoot;
760
+ exports.currentProjectRoot = currentProjectRoot;
761
+ exports.npmPluginRoots = npmPluginRoots;
762
+ exports.listNpmPlugins = listNpmPlugins;
763
+ exports.installNpmPlugin = installNpmPlugin;
764
+ exports.uninstallNpmPlugin = uninstallNpmPlugin;
765
+ const child_process_1 = __webpack_require__("child_process");
766
+ const fs_extra_1 = __importDefault(__webpack_require__("fs-extra"));
767
+ const path_1 = __importDefault(__webpack_require__("path"));
768
+ const module_1 = __webpack_require__("module");
769
+ const PLUGIN_ID = /^[a-z][a-z0-9_-]{1,63}$/;
770
+ const PACKAGE_NAME = /^(?:@[a-z0-9_.-]+\/)?epanel-plugin-[a-z0-9_.-]+$/;
771
+ const VERSION = /^[0-9][^\s/]{0,63}$/;
772
+ function readJson(filename) {
773
+ try {
774
+ const parsed = JSON.parse(fs_extra_1.default.readFileSync(filename, "utf8"));
775
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
776
+ ? parsed
777
+ : null;
778
+ }
779
+ catch (error) {
780
+ if (error?.code === "ENOENT")
781
+ return null;
782
+ throw error;
783
+ }
784
+ }
785
+ function objectValue(value) {
786
+ return value && typeof value === "object" && !Array.isArray(value)
787
+ ? value
788
+ : undefined;
789
+ }
790
+ function stringValue(value) {
791
+ return typeof value === "string" ? value : "";
792
+ }
793
+ function currentPackageRoot() {
794
+ return path_1.default.resolve(process.env.EPANEL_PACKAGE_ROOT || process.cwd());
795
+ }
796
+ function currentProjectRoot(packageRoot = currentPackageRoot()) {
797
+ // The npm worker sets EPANEL_PROJECT_ROOT to the installation directory. In a
798
+ // development checkout the runtime package sits directly under the project.
799
+ if (process.env.EPANEL_PROJECT_ROOT)
800
+ return path_1.default.resolve(process.env.EPANEL_PROJECT_ROOT);
801
+ const scope = path_1.default.resolve(packageRoot, "..");
802
+ const nodeModules = path_1.default.dirname(scope);
803
+ if (path_1.default.basename(nodeModules) === "node_modules" && path_1.default.basename(scope).startsWith("@"))
804
+ return path_1.default.dirname(nodeModules);
805
+ return scope;
806
+ }
807
+ function npmPluginRoots(options = {}) {
808
+ const packageRoot = path_1.default.resolve(options.packageRoot ?? currentPackageRoot());
809
+ return {
810
+ packageRoot,
811
+ projectRoot: path_1.default.resolve(options.projectRoot ?? currentProjectRoot(packageRoot))
812
+ };
813
+ }
814
+ function projectManifestFile(projectRoot) {
815
+ return path_1.default.join(projectRoot, "package.json");
816
+ }
817
+ function readProjectManifest(projectRoot) {
818
+ return (readJson(projectManifestFile(projectRoot)) ?? {
819
+ private: true,
820
+ dependencies: {},
821
+ elementspanel: { plugins: {} }
822
+ });
823
+ }
824
+ function writeProjectManifest(projectRoot, manifest) {
825
+ fs_extra_1.default.writeFileSync(projectManifestFile(projectRoot), `${JSON.stringify(manifest, null, 2)}\n`);
826
+ }
827
+ function dependenciesOf(manifest) {
828
+ return objectValue(manifest?.dependencies) ?? {};
829
+ }
830
+ function mappingsOf(manifest) {
831
+ return objectValue(objectValue(manifest?.elementspanel)?.plugins) ?? {};
832
+ }
833
+ function pluginRecord(pluginId, name, source, packageRoot, directory, folder, version) {
834
+ return { pluginId, name, version, sides: [], directory, folder, packageRoot, source };
835
+ }
836
+ function readPackageEntry(packageRoot, name) {
837
+ const hostRequire = (0, module_1.createRequire)(path_1.default.join(packageRoot, "package.json"));
838
+ const manifestPath = hostRequire.resolve(`${name}/package.json`);
839
+ const metadata = readJson(manifestPath);
840
+ if (!metadata)
841
+ throw new Error(`Missing package metadata for ${name}.`);
842
+ return { root: path_1.default.dirname(manifestPath), metadata };
843
+ }
844
+ function sideDirectory(packageRoot, metadata, side) {
845
+ const elements = objectValue(metadata.elementspanel);
846
+ const definition = objectValue(elements?.[side]);
847
+ const directory = stringValue(definition?.directory);
848
+ if (!definition || !directory)
849
+ return undefined;
850
+ const target = path_1.default.resolve(packageRoot, directory);
851
+ const relative = path_1.default.relative(packageRoot, target);
852
+ if (relative === "" || relative.startsWith(`..${path_1.default.sep}`) || path_1.default.isAbsolute(relative))
853
+ return undefined;
854
+ return target;
855
+ }
856
+ function recordFromPackage(pluginId, name, source, packageRoot, metadata, side) {
857
+ const elements = objectValue(metadata.elementspanel);
858
+ if (!elements || elements.id !== pluginId)
859
+ return undefined;
860
+ const version = stringValue(metadata.version);
861
+ const sides = ["panel", "daemon"].filter((item) => Boolean(sideDirectory(packageRoot, metadata, item)));
862
+ if (!sides.length)
863
+ return undefined;
864
+ const target = side ?? sides[0];
865
+ if (!sides.includes(target))
866
+ return undefined;
867
+ const directory = sideDirectory(packageRoot, metadata, target);
868
+ const folder = stringValue(objectValue(elements[target])?.folder);
869
+ if (!directory || !folder)
870
+ return undefined;
871
+ return {
872
+ ...pluginRecord(pluginId, name, source, packageRoot, directory, folder, version),
873
+ sides
874
+ };
875
+ }
876
+ /**
877
+ * Every npm plugin the runtime can load for a side, built-in and project-installed.
878
+ *
879
+ * Built-in mappings win over a project mapping for the same id: a third-party
880
+ * package must not shadow a plugin that ships with the runtime. A project package
881
+ * that does not describe the requested side is skipped instead of failing, which
882
+ * is what lets a two-sided project share one dependency list between sides.
883
+ */
884
+ function listNpmPlugins(side, options = {}) {
885
+ const { packageRoot, projectRoot } = npmPluginRoots(options);
886
+ const host = readJson(path_1.default.join(packageRoot, "package.json"));
887
+ if (!host)
888
+ throw new Error(`Missing runtime package metadata: ${packageRoot}`);
889
+ const project = path_1.default.resolve(projectRoot) === path_1.default.resolve(packageRoot) ? null : readProjectManifest(projectRoot);
890
+ const hostMappings = mappingsOf(host);
891
+ const projectMappings = mappingsOf(project);
892
+ const mappings = new Map();
893
+ for (const [pluginId, name] of Object.entries(projectMappings)) {
894
+ if (!PLUGIN_ID.test(pluginId) || typeof name !== "string" || !PACKAGE_NAME.test(name))
895
+ continue;
896
+ mappings.set(pluginId, { name, source: "project" });
897
+ }
898
+ for (const [pluginId, name] of Object.entries(hostMappings)) {
899
+ if (!PLUGIN_ID.test(pluginId) || typeof name !== "string" || !PACKAGE_NAME.test(name))
900
+ continue;
901
+ mappings.set(pluginId, { name, source: "bundled" });
902
+ }
903
+ const hostDependencies = dependenciesOf(host);
904
+ const projectDependencies = dependenciesOf(project);
905
+ const folders = new Set();
906
+ const records = [];
907
+ for (const [pluginId, mapping] of mappings) {
908
+ const declared = hostDependencies[mapping.name] ?? projectDependencies[mapping.name];
909
+ if (declared === undefined) {
910
+ if (options.skipInvalid)
911
+ continue;
912
+ throw new Error(`Missing npm plugin dependency: ${mapping.name}. Run npm install in the project directory.`);
913
+ }
914
+ let entry;
915
+ try {
916
+ entry = readPackageEntry(packageRoot, mapping.name);
917
+ }
918
+ catch (error) {
919
+ if (options.skipInvalid)
920
+ continue;
921
+ throw error;
922
+ }
923
+ const record = recordFromPackage(pluginId, mapping.name, mapping.source, entry.root, entry.metadata, side);
924
+ if (!record) {
925
+ if (options.skipInvalid || mapping.source === "project")
926
+ continue;
927
+ throw new Error(`Invalid ${side ?? "plugin"} entry in npm plugin ${mapping.name}.`);
928
+ }
929
+ const folder = record.folder;
930
+ if (folders.has(folder)) {
931
+ if (options.skipInvalid)
932
+ continue;
933
+ throw new Error(`Duplicate npm plugin asset directory: ${folder}.`);
934
+ }
935
+ folders.add(folder);
936
+ records.push(record);
937
+ }
938
+ return records;
939
+ }
940
+ function runNpm(args, cwd) {
941
+ return new Promise((resolve, reject) => {
942
+ const npmCLI = process.env.npm_execpath;
943
+ const invocation = npmCLI && /npm(?:-cli)?\.(?:c?js|mjs)$/.test(npmCLI)
944
+ ? { command: process.execPath, args: [npmCLI, ...args], shell: false }
945
+ : process.platform === "win32"
946
+ ? {
947
+ command: process.env.ComSpec || "cmd.exe",
948
+ args: ["/d", "/s", "/c", `npm ${args.map((item) => `"${item.replace(/"/g, '""')}"`).join(" ")}`],
949
+ shell: false
950
+ }
951
+ : { command: "npm", args, shell: false };
952
+ const child = (0, child_process_1.spawn)(invocation.command, invocation.args, {
953
+ cwd,
954
+ shell: invocation.shell,
955
+ windowsHide: true,
956
+ stdio: ["ignore", "pipe", "pipe"],
957
+ env: { ...process.env, npm_config_update_notifier: "false" }
958
+ });
959
+ let output = "";
960
+ const append = (data) => {
961
+ output += data.toString();
962
+ if (output.length > 16000)
963
+ output = output.slice(-16000);
964
+ };
965
+ child.stdout?.on("data", append);
966
+ child.stderr?.on("data", append);
967
+ const timer = setTimeout(() => {
968
+ child.kill("SIGKILL");
969
+ reject(new Error(`npm ${args[0] ?? ""} timed out.`));
970
+ }, 10 * 60 * 1000);
971
+ timer.unref?.();
972
+ child.once("error", (error) => {
973
+ clearTimeout(timer);
974
+ reject(error);
975
+ });
976
+ child.once("close", (code) => {
977
+ clearTimeout(timer);
978
+ if (code === 0)
979
+ return resolve(output);
980
+ reject(new Error(`npm ${args[0] ?? ""} failed (${code}): ${output.trim()}`));
981
+ });
982
+ });
983
+ }
984
+ const operations = new Map();
985
+ function withProjectLock(projectRoot, operation) {
986
+ const key = process.platform === "win32" ? projectRoot.toLowerCase() : projectRoot;
987
+ const previous = operations.get(key) ?? Promise.resolve();
988
+ const result = previous.then(operation);
989
+ const settled = result.then(() => { }, () => { });
990
+ operations.set(key, settled);
991
+ return result.finally(() => {
992
+ if (operations.get(key) === settled)
993
+ operations.delete(key);
994
+ });
995
+ }
996
+ /**
997
+ * Installs (or updates) one npm plugin as a project dependency.
998
+ *
999
+ * The version is pinned with `--save-exact`: the panel performs updates, so a
1000
+ * later bare `npm install` must not silently move the plugin to another release.
1001
+ */
1002
+ async function installNpmPlugin(options) {
1003
+ const { name, pluginId, version, side } = options;
1004
+ if (!PLUGIN_ID.test(pluginId))
1005
+ throw new Error(`Invalid plugin id: ${pluginId}`);
1006
+ if (!PACKAGE_NAME.test(name))
1007
+ throw new Error(`Invalid npm plugin package: ${name}`);
1008
+ if (!VERSION.test(version))
1009
+ throw new Error(`Invalid plugin version: ${version}`);
1010
+ const { packageRoot, projectRoot } = npmPluginRoots(options);
1011
+ return withProjectLock(projectRoot, async () => {
1012
+ const host = readJson(path_1.default.join(packageRoot, "package.json"));
1013
+ if (!host)
1014
+ throw new Error(`Missing runtime package metadata: ${packageRoot}`);
1015
+ if (mappingsOf(host)[pluginId] !== undefined)
1016
+ throw new Error(`BUNDLED:${pluginId}`);
1017
+ const existing = mappingsOf(readProjectManifest(projectRoot))[pluginId];
1018
+ if (existing !== undefined && existing !== name)
1019
+ throw new Error(`Plugin id ${pluginId} is already installed as ${String(existing)}.`);
1020
+ await fs_extra_1.default.ensureDir(projectRoot);
1021
+ await runNpm(["install", `${name}@${version}`, "--save", "--save-exact", "--no-fund", "--no-audit", "--ignore-scripts"], projectRoot);
1022
+ let entry;
1023
+ try {
1024
+ entry = readPackageEntry(packageRoot, name);
1025
+ }
1026
+ catch (error) {
1027
+ await runNpm(["uninstall", name, "--no-fund", "--no-audit", "--ignore-scripts"], projectRoot).catch(() => { });
1028
+ throw error;
1029
+ }
1030
+ const record = recordFromPackage(pluginId, name, "project", entry.root, entry.metadata, side);
1031
+ if (!record) {
1032
+ await runNpm(["uninstall", name, "--no-fund", "--no-audit", "--ignore-scripts"], projectRoot).catch(() => { });
1033
+ throw new Error(`The package ${name} does not provide a ${side} plugin named ${pluginId}.`);
1034
+ }
1035
+ if (record.version !== version) {
1036
+ await runNpm(["uninstall", name, "--no-fund", "--no-audit", "--ignore-scripts"], projectRoot).catch(() => { });
1037
+ throw new Error(`The installed package ${name} is ${record.version}, expected ${version}.`);
1038
+ }
1039
+ const manifest = readProjectManifest(projectRoot);
1040
+ const dependencies = { ...dependenciesOf(manifest), [name]: version };
1041
+ const elementspanel = { ...(objectValue(manifest.elementspanel) ?? {}) };
1042
+ const plugins = { ...mappingsOf(manifest), [pluginId]: name };
1043
+ manifest.dependencies = dependencies;
1044
+ manifest.elementspanel = { ...elementspanel, plugins };
1045
+ writeProjectManifest(projectRoot, manifest);
1046
+ return record;
1047
+ });
1048
+ }
1049
+ /** Removes one project-installed npm plugin. Built-in packages are left alone. */
1050
+ async function uninstallNpmPlugin(pluginId, options = {}) {
1051
+ if (!PLUGIN_ID.test(pluginId))
1052
+ throw new Error(`Invalid plugin id: ${pluginId}`);
1053
+ const { packageRoot, projectRoot } = npmPluginRoots(options);
1054
+ return withProjectLock(projectRoot, async () => {
1055
+ const host = readJson(path_1.default.join(packageRoot, "package.json"));
1056
+ if (host && mappingsOf(host)[pluginId] !== undefined)
1057
+ throw new Error(`BUNDLED:${pluginId}`);
1058
+ const manifest = readProjectManifest(projectRoot);
1059
+ const name = mappingsOf(manifest)[pluginId];
1060
+ if (typeof name !== "string" || !PACKAGE_NAME.test(name))
1061
+ return null;
1062
+ const record = listNpmPlugins(undefined, { packageRoot, projectRoot, skipInvalid: true }).find((item) => item.pluginId === pluginId);
1063
+ await runNpm(["uninstall", name, "--no-fund", "--no-audit", "--ignore-scripts"], projectRoot);
1064
+ const plugins = { ...mappingsOf(manifest) };
1065
+ delete plugins[pluginId];
1066
+ const elementspanel = { ...(objectValue(manifest.elementspanel) ?? {}) };
1067
+ if (Object.keys(plugins).length)
1068
+ elementspanel.plugins = plugins;
1069
+ else
1070
+ delete elementspanel.plugins;
1071
+ manifest.elementspanel = elementspanel;
1072
+ writeProjectManifest(projectRoot, manifest);
1073
+ return record ?? null;
1074
+ });
1075
+ }
1076
+
1077
+
1078
+ /***/ },
1079
+
1080
+ /***/ "../common/src/plugin_overrides.ts"
1081
+ (__unused_webpack_module, exports, __webpack_require__) {
1082
+
1083
+
1084
+ var __importDefault = (this && this.__importDefault) || function (mod) {
1085
+ return (mod && mod.__esModule) ? mod : { "default": mod };
1086
+ };
1087
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1088
+ exports.pluginOverridesPath = pluginOverridesPath;
1089
+ exports.readPluginOverrides = readPluginOverrides;
1090
+ exports.validatePluginOverride = validatePluginOverride;
1091
+ exports.applyPluginOverrides = applyPluginOverrides;
1092
+ exports.writePluginOverride = writePluginOverride;
1093
+ const node_fs_1 = __importDefault(__webpack_require__("node:fs"));
1094
+ const node_path_1 = __importDefault(__webpack_require__("node:path"));
1095
+ const node_crypto_1 = __webpack_require__("node:crypto");
1096
+ function pluginOverridesPath() {
1097
+ return node_path_1.default.resolve(process.cwd(), "data", "plugin-overrides.json");
1098
+ }
1099
+ /** Package defaults stay immutable; only the user's last layer lives in data. */
1100
+ function readPluginOverrides(filename = pluginOverridesPath()) {
1101
+ let raw;
1102
+ try {
1103
+ raw = node_fs_1.default.readFileSync(filename, "utf8");
1104
+ }
1105
+ catch (error) {
1106
+ if (error.code === "ENOENT")
1107
+ return { version: 1, plugins: {} };
1108
+ throw error;
1109
+ }
1110
+ const data = JSON.parse(raw);
1111
+ if (data?.version !== 1 ||
1112
+ !data.plugins ||
1113
+ typeof data.plugins !== "object" ||
1114
+ Array.isArray(data.plugins))
1115
+ throw new Error("Invalid plugin-overrides.json");
1116
+ for (const entry of Object.values(data.plugins))
1117
+ validatePluginOverride(entry);
1118
+ return data;
1119
+ }
1120
+ function validatePluginOverride(value) {
1121
+ if (!value || typeof value !== "object" || Array.isArray(value))
1122
+ throw new Error("Invalid plugin override.");
1123
+ const entry = value;
1124
+ if (entry.enabled !== undefined && typeof entry.enabled !== "boolean")
1125
+ throw new Error("Invalid plugin enablement.");
1126
+ if (entry.config !== undefined &&
1127
+ (!entry.config || typeof entry.config !== "object" || Array.isArray(entry.config)))
1128
+ throw new Error("Plugin config must be an object.");
1129
+ if (Object.keys(value).some((key) => !["enabled", "config"].includes(key)))
1130
+ throw new Error("Unknown plugin override field.");
1131
+ }
1132
+ function applyPluginOverrides(plugins, filename = pluginOverridesPath()) {
1133
+ const overrides = readPluginOverrides(filename).plugins;
1134
+ return plugins.map((plugin) => ({
1135
+ ...plugin,
1136
+ manifest: {
1137
+ ...plugin.manifest,
1138
+ ...(Object.prototype.hasOwnProperty.call(overrides, plugin.manifest.id)
1139
+ ? overrides[plugin.manifest.id]
1140
+ : {})
1141
+ }
1142
+ }));
1143
+ }
1144
+ /** Call inside the host's change queue; rename keeps readers off partial JSON. */
1145
+ function writePluginOverride(id, patch, filename = pluginOverridesPath()) {
1146
+ validatePluginOverride(patch);
1147
+ if (!id || ["__proto__", "constructor", "prototype"].includes(id))
1148
+ throw new Error("Invalid plugin id.");
1149
+ const data = readPluginOverrides(filename);
1150
+ data.plugins[id] = {
1151
+ ...(Object.prototype.hasOwnProperty.call(data.plugins, id) ? data.plugins[id] : {}),
1152
+ ...patch
1153
+ };
1154
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(filename), { recursive: true });
1155
+ const temporary = `${filename}.${(0, node_crypto_1.randomBytes)(8).toString("hex")}.tmp`;
1156
+ try {
1157
+ node_fs_1.default.writeFileSync(temporary, `${JSON.stringify(data, null, 2)}\n`, { mode: 0o600, flag: "wx" });
1158
+ node_fs_1.default.renameSync(temporary, filename);
1159
+ }
1160
+ finally {
1161
+ node_fs_1.default.rmSync(temporary, { force: true });
1162
+ }
1163
+ }
1164
+
1165
+
1166
+ /***/ },
1167
+
1168
+ /***/ "../common/src/plugin_revision.ts"
1169
+ (__unused_webpack_module, exports, __webpack_require__) {
1170
+
1171
+
1172
+ var __importDefault = (this && this.__importDefault) || function (mod) {
1173
+ return (mod && mod.__esModule) ? mod : { "default": mod };
1174
+ };
1175
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1176
+ exports.pluginDirectoryRevision = pluginDirectoryRevision;
1177
+ const node_fs_1 = __importDefault(__webpack_require__("node:fs"));
1178
+ const node_path_1 = __importDefault(__webpack_require__("node:path"));
1179
+ const node_crypto_1 = __webpack_require__("node:crypto");
1180
+ const cache = new Map();
1181
+ /** One revision addresses the complete module graph, including CSS and nested chunks. */
1182
+ function pluginDirectoryRevision(directory) {
1183
+ const files = [];
1184
+ const walk = (root) => {
1185
+ if (!node_fs_1.default.existsSync(root))
1186
+ return;
1187
+ for (const item of node_fs_1.default
1188
+ .readdirSync(root, { withFileTypes: true })
1189
+ .sort((a, b) => a.name.localeCompare(b.name))) {
1190
+ const absolute = node_path_1.default.join(root, item.name);
1191
+ if (item.isDirectory())
1192
+ walk(absolute);
1193
+ else if (item.isFile())
1194
+ files.push({
1195
+ relative: node_path_1.default.relative(directory, absolute),
1196
+ absolute,
1197
+ stat: node_fs_1.default.statSync(absolute)
1198
+ });
1199
+ }
1200
+ };
1201
+ walk(directory);
1202
+ const signature = JSON.stringify(files.map(({ relative, stat }) => [relative, stat.size, stat.mtimeMs, stat.ctimeMs]));
1203
+ const previous = cache.get(directory);
1204
+ if (previous?.signature === signature)
1205
+ return previous.revision;
1206
+ const hash = (0, node_crypto_1.createHash)("sha256");
1207
+ for (const file of files)
1208
+ hash
1209
+ .update(file.relative)
1210
+ .update("\0")
1211
+ .update(new Uint8Array(node_fs_1.default.readFileSync(file.absolute)))
1212
+ .update("\0");
1213
+ const revision = hash.digest("hex").slice(0, 24);
1214
+ if (cache.size > 1024)
1215
+ cache.clear();
1216
+ cache.set(directory, { signature, revision });
1217
+ return revision;
1218
+ }
1219
+
1220
+
1221
+ /***/ },
1222
+
1223
+ /***/ "../common/src/process_tools.ts"
1224
+ (__unused_webpack_module, exports, __webpack_require__) {
1225
+
1226
+
1227
+ var __importDefault = (this && this.__importDefault) || function (mod) {
1228
+ return (mod && mod.__esModule) ? mod : { "default": mod };
1229
+ };
1230
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1231
+ exports.ProcessWrapper = exports.StartError = void 0;
1232
+ exports.killProcess = killProcess;
1233
+ const child_process_1 = __webpack_require__("child_process");
1234
+ const os_1 = __importDefault(__webpack_require__("os"));
1235
+ const child_process_2 = __importDefault(__webpack_require__("child_process"));
1236
+ const path_1 = __importDefault(__webpack_require__("path"));
1237
+ const events_1 = __importDefault(__webpack_require__("events"));
1238
+ const iconv_lite_1 = __importDefault(__webpack_require__("iconv-lite"));
1239
+ const process_1 = __importDefault(__webpack_require__("process"));
1240
+ class StartError extends Error {
1241
+ }
1242
+ exports.StartError = StartError;
1243
+ class ProcessWrapper extends events_1.default {
1244
+ file;
1245
+ args;
1246
+ cwd;
1247
+ timeout;
1248
+ code;
1249
+ option;
1250
+ process;
1251
+ pid;
1252
+ lastExitCode;
1253
+ completed = false;
1254
+ errMsg = {
1255
+ timeoutErr: "task timeout!",
1256
+ exitErr: "task error!",
1257
+ startErr: "task start error!"
1258
+ };
1259
+ constructor(file, args, cwd, timeout = 0, code = "utf-8", option = {}) {
1260
+ super();
1261
+ this.file = file;
1262
+ this.args = args;
1263
+ this.cwd = cwd;
1264
+ this.timeout = timeout;
1265
+ this.code = code;
1266
+ this.option = option;
1267
+ }
1268
+ setErrMsg(errMsg) {
1269
+ this.errMsg = errMsg;
1270
+ }
1271
+ start() {
1272
+ if (this.process)
1273
+ return Promise.reject(new Error("The process is already running"));
1274
+ return new Promise((resolve, reject) => {
1275
+ const stdoutDecoder = iconv_lite_1.default.decodeStream(this.code);
1276
+ const stderrDecoder = iconv_lite_1.default.decodeStream(this.code);
1277
+ let timeTask;
1278
+ let settled = false;
1279
+ let spawned = false;
1280
+ const settle = (error) => {
1281
+ if (settled)
1282
+ return;
1283
+ settled = true;
1284
+ if (timeTask)
1285
+ clearTimeout(timeTask);
1286
+ if (error)
1287
+ reject(error);
1288
+ else
1289
+ resolve(true);
1290
+ };
1291
+ const subProcess = child_process_2.default.spawn(this.file, this.args, {
1292
+ stdio: "pipe",
1293
+ windowsHide: true,
1294
+ cwd: path_1.default.normalize(this.cwd),
1295
+ ...this.option
1296
+ });
1297
+ this.process = subProcess;
1298
+ this.pid = subProcess.pid;
1299
+ this.lastExitCode = undefined;
1300
+ this.completed = false;
1301
+ const fail = (error) => {
1302
+ if (settled)
1303
+ return;
1304
+ settle(error);
1305
+ if (subProcess.pid && subProcess.exitCode === null && subProcess.signalCode === null) {
1306
+ try {
1307
+ killProcess(subProcess.pid, subProcess);
1308
+ }
1309
+ catch (error) {
1310
+ console.error("[ProcessWrapper kill error]", error);
1311
+ }
1312
+ }
1313
+ };
1314
+ subProcess.once("spawn", () => {
1315
+ spawned = true;
1316
+ this.emit("start", subProcess.pid);
1317
+ });
1318
+ for (const [source, decoder] of [
1319
+ [subProcess.stdout, stdoutDecoder],
1320
+ [subProcess.stderr, stderrDecoder]
1321
+ ]) {
1322
+ decoder.on("data", (text) => this.emit("data", text));
1323
+ decoder.on("error", fail);
1324
+ source.on("error", fail);
1325
+ source.pipe(decoder);
1326
+ }
1327
+ subProcess.stdin.on("error", fail);
1328
+ subProcess.once("error", (error) => {
1329
+ fail(new Error(`${spawned ? this.errMsg.exitErr : this.errMsg.startErr} ${error.message}`));
1330
+ });
1331
+ subProcess.once("exit", (code) => {
1332
+ this.lastExitCode = code;
1333
+ this.emit("exit", code);
1334
+ });
1335
+ // `close` follows the final stdout/stderr data and also follows a failed spawn.
1336
+ subProcess.once("close", (code) => {
1337
+ this.lastExitCode = code;
1338
+ this.completed = true;
1339
+ this.process = undefined;
1340
+ settle(code === 0 ? undefined : new Error(this.errMsg.exitErr));
1341
+ });
1342
+ if (this.timeout > 0) {
1343
+ timeTask = setTimeout(() => {
1344
+ fail(new Error(this.errMsg.timeoutErr));
1345
+ }, 1000 * this.timeout);
1346
+ }
1347
+ });
1348
+ }
1349
+ getPid() {
1350
+ return this.process?.pid;
1351
+ }
1352
+ write(data) {
1353
+ return this.process?.stdin?.write(iconv_lite_1.default.encode(data, this.code));
1354
+ }
1355
+ kill() {
1356
+ if (this.process?.pid)
1357
+ killProcess(this.process?.pid, this.process);
1358
+ }
1359
+ status() {
1360
+ return this.completed;
1361
+ }
1362
+ exitCode() {
1363
+ return this.process?.exitCode ?? this.lastExitCode;
1364
+ }
1365
+ }
1366
+ exports.ProcessWrapper = ProcessWrapper;
1367
+ function killProcess(pid, process, signal) {
1368
+ const numericPid = Number(pid);
1369
+ if (!Number.isSafeInteger(numericPid) || numericPid <= 0) {
1370
+ throw new Error("Invalid process ID");
1371
+ }
1372
+ try {
1373
+ if (os_1.default.platform() === "win32") {
1374
+ (0, child_process_1.execFileSync)("taskkill", ["/PID", String(numericPid), "/T", "/F"], { windowsHide: true });
1375
+ return true;
1376
+ }
1377
+ return process_1.default.kill(numericPid, signal || "SIGKILL");
1378
+ }
1379
+ catch (err) {
1380
+ return signal ? process.kill(signal) : process.kill("SIGKILL");
1381
+ }
1382
+ }
1383
+
1384
+
1385
+ /***/ },
1386
+
1387
+ /***/ "../common/src/query_wrapper.ts"
1388
+ (__unused_webpack_module, exports) {
1389
+
1390
+
1391
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1392
+ exports.QueryWrapper = exports.LocalFileSource = exports.MySqlSource = exports.QueryMapWrapper = void 0;
1393
+ function paginate(data, page, pageSize) {
1394
+ if (!Number.isSafeInteger(page) || page < 1 || !Number.isSafeInteger(pageSize) || pageSize < 1) {
1395
+ throw new RangeError("page and pageSize must be positive integers");
1396
+ }
1397
+ const start = (page - 1) * pageSize;
1398
+ return {
1399
+ page,
1400
+ pageSize,
1401
+ maxPage: Math.ceil(data.length / pageSize),
1402
+ total: data.length,
1403
+ data: data.slice(start, start + pageSize)
1404
+ };
1405
+ }
1406
+ // Provide the MAP query interface used by the routing layer
1407
+ class QueryMapWrapper {
1408
+ map;
1409
+ constructor(map) {
1410
+ this.map = map;
1411
+ }
1412
+ select(condition) {
1413
+ const result = [];
1414
+ this.map.forEach((v) => {
1415
+ if (condition(v))
1416
+ result.push(v);
1417
+ });
1418
+ return result;
1419
+ }
1420
+ page(data, page = 1, pageSize = 10) {
1421
+ return paginate(data, page, pageSize);
1422
+ }
1423
+ }
1424
+ exports.QueryMapWrapper = QueryMapWrapper;
1425
+ // MYSQL data source
1426
+ class MySqlSource {
1427
+ data;
1428
+ constructor(data) {
1429
+ this.data = data;
1430
+ }
1431
+ selectPage(condition, page, pageSize) {
1432
+ return {
1433
+ page,
1434
+ pageSize,
1435
+ maxPage: 0,
1436
+ total: 0,
1437
+ data: []
1438
+ };
1439
+ }
1440
+ select(condition) {
1441
+ return [];
1442
+ }
1443
+ update(condition, data) { }
1444
+ delete(condition) { }
1445
+ insert(data) { }
1446
+ }
1447
+ exports.MySqlSource = MySqlSource;
1448
+ // local file data source (embedded microdatabase)
1449
+ class LocalFileSource {
1450
+ data;
1451
+ constructor(data) {
1452
+ this.data = data;
1453
+ }
1454
+ selectPage(condition, page = 1, pageSize = 10) {
1455
+ return this.page(this.select(condition), page, pageSize);
1456
+ }
1457
+ page(data, page = 1, pageSize = 10) {
1458
+ return paginate(data, page, pageSize);
1459
+ }
1460
+ select(condition = {}) {
1461
+ const result = [];
1462
+ const entries = Object.entries(condition);
1463
+ this.data.forEach((v) => {
1464
+ for (const [key, targetValue] of entries) {
1465
+ const dataValue = v[key];
1466
+ if (typeof targetValue === "string" &&
1467
+ targetValue.startsWith("%") &&
1468
+ targetValue.endsWith("%")) {
1469
+ if (typeof dataValue !== "string" || !dataValue.includes(targetValue.slice(1, -1)))
1470
+ return;
1471
+ }
1472
+ else if (targetValue !== dataValue) {
1473
+ return;
1474
+ }
1475
+ }
1476
+ result.push(v);
1477
+ });
1478
+ return result;
1479
+ }
1480
+ update(condition, data) { }
1481
+ delete(condition) { }
1482
+ insert(data) { }
1483
+ }
1484
+ exports.LocalFileSource = LocalFileSource;
1485
+ // Provide the unified data query interface used by the routing layer
1486
+ class QueryWrapper {
1487
+ dataSource;
1488
+ constructor(dataSource) {
1489
+ this.dataSource = dataSource;
1490
+ }
1491
+ selectPage(condition, page = 1, pageSize = 10) {
1492
+ return this.dataSource.selectPage(condition, page, pageSize);
1493
+ }
1494
+ }
1495
+ exports.QueryWrapper = QueryWrapper;
1496
+
1497
+
1498
+ /***/ },
1499
+
1500
+ /***/ "../common/src/runtime_paths.ts"
1501
+ (__unused_webpack_module, exports, __webpack_require__) {
1502
+
1503
+
1504
+ var __importDefault = (this && this.__importDefault) || function (mod) {
1505
+ return (mod && mod.__esModule) ? mod : { "default": mod };
1506
+ };
1507
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1508
+ exports.isPackagedRuntime = isPackagedRuntime;
1509
+ exports.packagePath = packagePath;
1510
+ exports.packagePluginRoots = packagePluginRoots;
1511
+ exports.isPackagePlugin = isPackagePlugin;
1512
+ exports.bundledToolPath = bundledToolPath;
1513
+ exports.ensureExecutable = ensureExecutable;
1514
+ const node_fs_1 = __importDefault(__webpack_require__("node:fs"));
1515
+ const node_os_1 = __importDefault(__webpack_require__("node:os"));
1516
+ const node_path_1 = __importDefault(__webpack_require__("node:path"));
1517
+ /** Set by the npm worker before any application module is loaded. */
1518
+ function isPackagedRuntime() {
1519
+ return Boolean(process.env.EPANEL_PACKAGE_ROOT);
1520
+ }
1521
+ /** Code and bundled assets belong to the package; cwd always belongs to the user. */
1522
+ function packagePath(...parts) {
1523
+ return node_path_1.default.resolve(process.env.EPANEL_PACKAGE_ROOT || process.cwd(), ...parts);
1524
+ }
1525
+ function npmPluginRoots(side) {
1526
+ // The same resolver is used by the CLI before the bundled application starts.
1527
+ // A development checkout keeps it in the repository, beside the side packages,
1528
+ // so project-installed npm plugins work there as well.
1529
+ const runtimeRequire = eval("require");
1530
+ const resolver = isPackagedRuntime()
1531
+ ? packagePath("runtime/npm-plugins.cjs")
1532
+ : node_path_1.default.resolve(packagePath(), "..", "scripts", "runtime", "npm-plugins.cjs");
1533
+ // A development process may run from a fixture directory without a runtime
1534
+ // package; there is nothing to resolve in that case.
1535
+ if (!node_fs_1.default.existsSync(node_path_1.default.join(packagePath(), "package.json")))
1536
+ return [];
1537
+ if (!isPackagedRuntime() && !node_fs_1.default.existsSync(resolver))
1538
+ return [];
1539
+ const { resolveNpmPlugins } = runtimeRequire(resolver);
1540
+ return resolveNpmPlugins(packagePath(), side, { skipInvalid: true }).map((record) => ({
1541
+ directory: record.directory,
1542
+ ...(record.folder ? { folder: record.folder } : {}),
1543
+ // Built-in packages are part of the runtime's default configuration. A
1544
+ // project dependency is installed but remains dormant until the user
1545
+ // enables it through the plugin configuration service.
1546
+ defaultEnabled: record.defaultEnabled !== false
1547
+ }));
1548
+ }
1549
+ function packagePluginRoots(side) {
1550
+ const roots = isPackagedRuntime()
1551
+ ? []
1552
+ : [{ directory: packagePath("plugins"), defaultEnabled: true }];
1553
+ // Project-installed npm plugins load in development too: they are dependencies
1554
+ // of the development project, not of the published runtime package.
1555
+ roots.push(...npmPluginRoots(side));
1556
+ return roots;
1557
+ }
1558
+ function isPackagePlugin(directory) {
1559
+ return npmPluginRoots().some((root) => {
1560
+ const relative = node_path_1.default.relative(root.directory, node_path_1.default.resolve(directory));
1561
+ return (relative === "" ||
1562
+ (!relative.startsWith(`..${node_path_1.default.sep}`) && relative !== ".." && !node_path_1.default.isAbsolute(relative)));
1563
+ });
1564
+ }
1565
+ function bundledToolPath(name) {
1566
+ const platform = node_os_1.default.platform();
1567
+ const arch = node_os_1.default.arch();
1568
+ const suffix = platform === "win32" ? ".exe" : "";
1569
+ const filename = `${name}_${platform}_${arch}${suffix}`;
1570
+ // Published daemon packages download native tools into the installation's
1571
+ // writable lib directory on first start. Development checkouts use the same
1572
+ // repository-local sibling of data and logs.
1573
+ const root = isPackagedRuntime() ? node_path_1.default.resolve(process.cwd(), "lib") : packagePath("lib");
1574
+ const primary = node_path_1.default.join(root, filename);
1575
+ // Zip-Tools names its Intel macOS binary "amd64", while Node reports "x64".
1576
+ if (!node_fs_1.default.existsSync(primary) && name === "file_zip" && platform === "darwin" && arch === "x64") {
1577
+ const alternate = node_path_1.default.join(root, "file_zip_darwin_amd64");
1578
+ if (node_fs_1.default.existsSync(alternate))
1579
+ return alternate;
1580
+ }
1581
+ return primary;
1582
+ }
1583
+ /** Published binaries already carry executable permissions, including in read-only installs. */
1584
+ function ensureExecutable(filename) {
1585
+ if (node_os_1.default.platform() === "win32")
1586
+ return;
1587
+ try {
1588
+ node_fs_1.default.accessSync(filename, node_fs_1.default.constants.X_OK);
1589
+ }
1590
+ catch {
1591
+ node_fs_1.default.chmodSync(filename, 0o755);
1592
+ }
1593
+ }
1594
+
1595
+
1596
+ /***/ },
1597
+
1598
+ /***/ "../common/src/string_utils.ts"
1599
+ (__unused_webpack_module, exports) {
1600
+
1601
+
1602
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1603
+ exports.removeTrail = removeTrail;
1604
+ function removeTrail(origin, trail) {
1605
+ if (origin.endsWith(trail)) {
1606
+ return origin.slice(0, origin.length - trail.length);
1607
+ }
1608
+ else {
1609
+ return origin;
1610
+ }
1611
+ }
1612
+
1613
+
1614
+ /***/ },
1615
+
1616
+ /***/ "../common/src/system_info.ts"
1617
+ (__unused_webpack_module, exports, __webpack_require__) {
1618
+
1619
+
1620
+ var __importDefault = (this && this.__importDefault) || function (mod) {
1621
+ return (mod && mod.__esModule) ? mod : { "default": mod };
1622
+ };
1623
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1624
+ exports.systemInfo = systemInfo;
1625
+ const os_1 = __importDefault(__webpack_require__("os"));
1626
+ const os_utils_1 = __importDefault(__webpack_require__("os-utils"));
1627
+ const fs_1 = __importDefault(__webpack_require__("fs"));
1628
+ // System details are updated every time
1629
+ const info = {
1630
+ type: os_1.default.type(),
1631
+ hostname: os_1.default.hostname(),
1632
+ platform: os_1.default.platform(),
1633
+ release: os_1.default.release(),
1634
+ uptime: os_1.default.uptime(),
1635
+ cwd: process.cwd(),
1636
+ loadavg: os_1.default.loadavg(),
1637
+ freemem: 0,
1638
+ cpuUsage: 0,
1639
+ memUsage: 0,
1640
+ totalmem: 0,
1641
+ processCpu: 0,
1642
+ processMem: 0
1643
+ };
1644
+ let previousCpu = process.cpuUsage();
1645
+ let previousTime = process.hrtime.bigint();
1646
+ // periodically refresh the cache
1647
+ function refresh() {
1648
+ const now = process.hrtime.bigint();
1649
+ const cpu = process.cpuUsage();
1650
+ const elapsed = Number(now - previousTime) / 1000;
1651
+ info.processCpu =
1652
+ elapsed > 0 ? (cpu.user - previousCpu.user + cpu.system - previousCpu.system) / elapsed : 0;
1653
+ previousCpu = cpu;
1654
+ previousTime = now;
1655
+ info.uptime = os_1.default.uptime();
1656
+ info.loadavg = os_1.default.loadavg();
1657
+ info.processMem = process.memoryUsage().rss;
1658
+ if (os_1.default.platform() === "linux") {
1659
+ return setLinuxSystemInfo();
1660
+ }
1661
+ return otherSystemInfo();
1662
+ }
1663
+ refresh();
1664
+ setInterval(refresh, 3000).unref();
1665
+ function otherSystemInfo() {
1666
+ info.freemem = os_1.default.freemem();
1667
+ info.totalmem = os_1.default.totalmem();
1668
+ info.memUsage = (os_1.default.totalmem() - os_1.default.freemem()) / os_1.default.totalmem();
1669
+ os_utils_1.default.cpuUsage((p) => (info.cpuUsage = p));
1670
+ }
1671
+ function setLinuxSystemInfo() {
1672
+ try {
1673
+ // read memory data based on /proc/meminfo
1674
+ const data = fs_1.default.readFileSync("/proc/meminfo", { encoding: "utf-8" });
1675
+ const list = data.split("\n");
1676
+ const infoTable = {};
1677
+ list.forEach((line) => {
1678
+ const kv = line.split(":");
1679
+ if (kv.length === 2) {
1680
+ const k = kv[0].replace(/ /gim, "").replace(/\t/gim, "").trim().toLowerCase();
1681
+ let v = kv[1].replace(/ /gim, "").replace(/\t/gim, "").trim().toLowerCase();
1682
+ v = v.replace(/kb/gim, "").replace(/mb/gim, "").replace(/gb/gim, "");
1683
+ let vNumber = parseInt(v);
1684
+ if (isNaN(vNumber))
1685
+ vNumber = 0;
1686
+ infoTable[k] = vNumber;
1687
+ }
1688
+ });
1689
+ const memAvailable = infoTable["memavailable"] ?? infoTable["memfree"];
1690
+ const memTotal = infoTable["memtotal"];
1691
+ info.freemem = memAvailable * 1024;
1692
+ info.totalmem = memTotal * 1024;
1693
+ info.memUsage = (info.totalmem - info.freemem) / info.totalmem;
1694
+ os_utils_1.default.cpuUsage((p) => (info.cpuUsage = p));
1695
+ }
1696
+ catch (error) {
1697
+ // If the reading is wrong, the default general reading method is automatically used
1698
+ otherSystemInfo();
1699
+ }
1700
+ }
1701
+ function systemInfo() {
1702
+ return info;
1703
+ }
1704
+
1705
+
1706
+ /***/ },
1707
+
1708
+ /***/ "../common/src/system_storage.ts"
1709
+ (__unused_webpack_module, exports, __webpack_require__) {
1710
+
1711
+
1712
+ var __importDefault = (this && this.__importDefault) || function (mod) {
1713
+ return (mod && mod.__esModule) ? mod : { "default": mod };
1714
+ };
1715
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1716
+ const crypto_1 = __webpack_require__("crypto");
1717
+ const path_1 = __importDefault(__webpack_require__("path"));
1718
+ const fs_extra_1 = __importDefault(__webpack_require__("fs-extra"));
1719
+ class StorageSubsystem {
1720
+ static DATA_PATH = path_1.default.resolve(process.cwd(), "data");
1721
+ static INDEX_PATH = path_1.default.join(StorageSubsystem.DATA_PATH, "Index");
1722
+ resolvePath(name) {
1723
+ if (typeof name !== "string" ||
1724
+ !name ||
1725
+ /[\0:]/.test(name) ||
1726
+ path_1.default.isAbsolute(name) ||
1727
+ path_1.default.win32.isAbsolute(name)) {
1728
+ throw new Error(`Invalid storage path: ${name}`);
1729
+ }
1730
+ const target = path_1.default.resolve(StorageSubsystem.DATA_PATH, name);
1731
+ const relative = path_1.default.relative(StorageSubsystem.DATA_PATH, target);
1732
+ if (!relative ||
1733
+ relative === ".." ||
1734
+ relative.startsWith(`..${path_1.default.sep}`) ||
1735
+ path_1.default.isAbsolute(relative)) {
1736
+ throw new Error(`Invalid storage path: ${name}`);
1737
+ }
1738
+ const realRelative = path_1.default.relative(this.realPath(StorageSubsystem.DATA_PATH), this.realPath(target));
1739
+ if (realRelative === ".." ||
1740
+ realRelative.startsWith(`..${path_1.default.sep}`) ||
1741
+ path_1.default.isAbsolute(realRelative)) {
1742
+ throw new Error(`Invalid storage path: ${name}`);
1743
+ }
1744
+ return target;
1745
+ }
1746
+ realPath(target) {
1747
+ if (fs_extra_1.default.existsSync(target))
1748
+ return fs_extra_1.default.realpathSync(target);
1749
+ const parent = path_1.default.dirname(target);
1750
+ return parent === target ? target : path_1.default.join(this.realPath(parent), path_1.default.basename(target));
1751
+ }
1752
+ entityPath(category, uuid) {
1753
+ if (typeof uuid !== "string" || !uuid || /[\\/\0:]/.test(uuid) || uuid.includes("..")) {
1754
+ throw new Error(`UUID ${uuid} does not conform to specification`);
1755
+ }
1756
+ this.resolvePath(category);
1757
+ return path_1.default.join(category, `${uuid}.json`);
1758
+ }
1759
+ writeFile(name, data) {
1760
+ const targetPath = this.resolvePath(name);
1761
+ fs_extra_1.default.ensureDirSync(path_1.default.dirname(targetPath));
1762
+ const temporaryPath = `${targetPath}.${(0, crypto_1.randomUUID)()}.tmp`;
1763
+ const mode = fs_extra_1.default.existsSync(targetPath) ? fs_extra_1.default.statSync(targetPath).mode : 0o600;
1764
+ try {
1765
+ fs_extra_1.default.writeFileSync(temporaryPath, data, { encoding: "utf8", flag: "wx", mode });
1766
+ fs_extra_1.default.renameSync(temporaryPath, targetPath);
1767
+ }
1768
+ finally {
1769
+ if (fs_extra_1.default.existsSync(temporaryPath))
1770
+ fs_extra_1.default.unlinkSync(temporaryPath);
1771
+ }
1772
+ }
1773
+ readFile(name) {
1774
+ return fs_extra_1.default.readFileSync(this.resolvePath(name), "utf8");
1775
+ }
1776
+ readDir(dirName) {
1777
+ const targetPath = this.resolvePath(dirName);
1778
+ if (!fs_extra_1.default.existsSync(targetPath))
1779
+ return [];
1780
+ return fs_extra_1.default.readdirSync(targetPath).map((name) => path_1.default.join(dirName, name));
1781
+ }
1782
+ deleteFile(name) {
1783
+ fs_extra_1.default.removeSync(this.resolvePath(name));
1784
+ }
1785
+ fileExists(name) {
1786
+ return fs_extra_1.default.existsSync(this.resolvePath(name));
1787
+ }
1788
+ store(category, uuid, object) {
1789
+ this.writeFile(this.entityPath(category, uuid), JSON.stringify(object, null, 4));
1790
+ }
1791
+ // Restore declared fields while keeping defaults for fields absent in older data.
1792
+ defineAttr(target, object) {
1793
+ for (const key of Object.keys(target)) {
1794
+ if (!Object.prototype.hasOwnProperty.call(object, key) || object[key] === undefined)
1795
+ continue;
1796
+ const value = object[key];
1797
+ if (value !== null &&
1798
+ typeof value === "object" &&
1799
+ !Array.isArray(value) &&
1800
+ target[key] !== null &&
1801
+ typeof target[key] === "object" &&
1802
+ !Array.isArray(target[key])) {
1803
+ this.defineAttr(target[key], value);
1804
+ }
1805
+ else {
1806
+ target[key] = value;
1807
+ }
1808
+ }
1809
+ return target;
1810
+ }
1811
+ load(category, classz, uuid) {
1812
+ const file = this.entityPath(category, uuid);
1813
+ if (!this.fileExists(file))
1814
+ return null;
1815
+ const object = JSON.parse(this.readFile(file));
1816
+ if (!object || typeof object !== "object" || Array.isArray(object)) {
1817
+ throw new Error(`Invalid stored entity: ${file}`);
1818
+ }
1819
+ return this.defineAttr(new classz(), object);
1820
+ }
1821
+ list(category) {
1822
+ const directory = this.resolvePath(category);
1823
+ if (!fs_extra_1.default.existsSync(directory))
1824
+ return [];
1825
+ return fs_extra_1.default
1826
+ .readdirSync(directory, { withFileTypes: true })
1827
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
1828
+ .map((entry) => entry.name.slice(0, -5));
1829
+ }
1830
+ delete(category, uuid) {
1831
+ this.deleteFile(this.entityPath(category, uuid));
1832
+ }
1833
+ }
1834
+ exports["default"] = StorageSubsystem;
1835
+
1836
+
1837
+ /***/ },
1838
+
1839
+ /***/ "../common/src/typecheck.ts"
1840
+ (__unused_webpack_module, exports) {
1841
+
1842
+
1843
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1844
+ exports.configureEntityParams = configureEntityParams;
1845
+ exports.toText = toText;
1846
+ exports.toNumber = toNumber;
1847
+ exports.toBoolean = toBoolean;
1848
+ exports.isEmpty = isEmpty;
1849
+ exports.supposeValue = supposeValue;
1850
+ function configureEntityParams(self, args, key, typeFn) {
1851
+ const selfDefaultValue = self[key] ?? null;
1852
+ const v = args[key] != null ? args[key] : selfDefaultValue;
1853
+ if (typeFn === Number) {
1854
+ if (v === "" || v == null) {
1855
+ self[key] = null;
1856
+ }
1857
+ else {
1858
+ if (isNaN(Number(v)))
1859
+ throw new Error(`ConfigureEntityParams Error: Expected type to be Number, but got ${typeof v}`);
1860
+ self[key] = Number(v);
1861
+ }
1862
+ return;
1863
+ }
1864
+ if (typeFn === String) {
1865
+ if (v == null) {
1866
+ self[key] = null;
1867
+ }
1868
+ else {
1869
+ self[key] = String(v);
1870
+ }
1871
+ return;
1872
+ }
1873
+ if (typeFn === Boolean) {
1874
+ if (v == null) {
1875
+ self[key] = false;
1876
+ }
1877
+ else {
1878
+ self[key] = Boolean(v);
1879
+ }
1880
+ return;
1881
+ }
1882
+ if (typeFn) {
1883
+ self[key] = typeFn(v);
1884
+ }
1885
+ else {
1886
+ self[key] = v;
1887
+ }
1888
+ }
1889
+ function toText(v) {
1890
+ if (isEmpty(v))
1891
+ return null;
1892
+ return String(v);
1893
+ }
1894
+ function toNumber(v) {
1895
+ if (isEmpty(v))
1896
+ return null;
1897
+ if (isNaN(Number(v)))
1898
+ return null;
1899
+ return Number(v);
1900
+ }
1901
+ function toBoolean(v) {
1902
+ if (isEmpty(v))
1903
+ return null;
1904
+ return Boolean(v);
1905
+ }
1906
+ function isEmpty(v) {
1907
+ return v === null || v === undefined;
1908
+ }
1909
+ function supposeValue(v, def) {
1910
+ if (isEmpty(v))
1911
+ return def;
1912
+ return v;
1913
+ }
1914
+
1915
+
1916
+ /***/ },
1917
+
1918
+ /***/ "./plugins/config/src/backend/settings.ts"
1919
+ (__unused_webpack_module, exports, __webpack_require__) {
1920
+
1921
+
1922
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1923
+ exports.SettingsFormService = void 0;
1924
+ const mcsmanager_common_1 = __webpack_require__("../common/src/index.ts");
1925
+ const cordis_1 = __webpack_require__("cordis");
1926
+ const cosmokit_1 = __webpack_require__("cosmokit");
1927
+ /** Configuration declarations are owned by the daemon config plugin. */
1928
+ class SettingsFormService extends cordis_1.Service {
1929
+ declarations = [];
1930
+ constructor(ctx) {
1931
+ super(ctx, "settingsForm", true);
1932
+ }
1933
+ declare(declaration) {
1934
+ const entry = { id: this.ctx.name, declaration };
1935
+ return this.ctx.effect(() => {
1936
+ this.declarations.push(entry);
1937
+ return () => (0, cosmokit_1.remove)(this.declarations, entry);
1938
+ });
1939
+ }
1940
+ declared() {
1941
+ return this.declarations.map((entry) => entry.id);
1942
+ }
1943
+ async read(id) {
1944
+ const entry = this.declarations.find((item) => item.id === id);
1945
+ if (!entry) {
1946
+ const schema = this.ctx.plugins.configuration(id);
1947
+ return schema.fields.length ? schema : null;
1948
+ }
1949
+ return { id, fields: entry.declaration.fields(), values: await entry.declaration.read() };
1950
+ }
1951
+ async write(id, values) {
1952
+ const entry = this.declarations.find((item) => item.id === id);
1953
+ if (!entry)
1954
+ return (await this.ctx.plugins.configure(id, values)).result;
1955
+ return this.ctx.plugins.runExclusive(async () => {
1956
+ (0, mcsmanager_common_1.validatePluginSettings)(entry.declaration.fields(), values);
1957
+ await entry.declaration.write(values);
1958
+ return {
1959
+ saved: true,
1960
+ application: entry.declaration.restartRequired ? "restart-required" : "applied"
1961
+ };
1962
+ });
1963
+ }
1964
+ }
1965
+ exports.SettingsFormService = SettingsFormService;
1966
+
1967
+
1968
+ /***/ },
1969
+
1970
+ /***/ "./plugins/config/src/i18n/index.ts"
1971
+ (__unused_webpack_module, exports, __webpack_require__) {
1972
+
1973
+
1974
+ var __importDefault = (this && this.__importDefault) || function (mod) {
1975
+ return (mod && mod.__esModule) ? mod : { "default": mod };
1976
+ };
1977
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1978
+ exports.localeMessages = void 0;
1979
+ const de_DE_json_1 = __importDefault(__webpack_require__("./plugins/config/src/i18n/de_DE.json"));
1980
+ const en_US_json_1 = __importDefault(__webpack_require__("./plugins/config/src/i18n/en_US.json"));
1981
+ const es_ES_json_1 = __importDefault(__webpack_require__("./plugins/config/src/i18n/es_ES.json"));
1982
+ const fr_FR_json_1 = __importDefault(__webpack_require__("./plugins/config/src/i18n/fr_FR.json"));
1983
+ const ja_JP_json_1 = __importDefault(__webpack_require__("./plugins/config/src/i18n/ja_JP.json"));
1984
+ const ko_KR_json_1 = __importDefault(__webpack_require__("./plugins/config/src/i18n/ko_KR.json"));
1985
+ const pt_BR_json_1 = __importDefault(__webpack_require__("./plugins/config/src/i18n/pt_BR.json"));
1986
+ const ru_RU_json_1 = __importDefault(__webpack_require__("./plugins/config/src/i18n/ru_RU.json"));
1987
+ const th_TH_json_1 = __importDefault(__webpack_require__("./plugins/config/src/i18n/th_TH.json"));
1988
+ const tr_TR_json_1 = __importDefault(__webpack_require__("./plugins/config/src/i18n/tr_TR.json"));
1989
+ const zh_CN_json_1 = __importDefault(__webpack_require__("./plugins/config/src/i18n/zh_CN.json"));
1990
+ const zh_TW_json_1 = __importDefault(__webpack_require__("./plugins/config/src/i18n/zh_TW.json"));
1991
+ // The refusals this plugin's protocol handlers throw. The panel surfaces them
1992
+ // verbatim on its plugin manager page, so they are translated here rather than
1993
+ // in the panel: this daemon knows which of its own plugins may not be switched
1994
+ // off remotely, and it answers in the language it was told to speak.
1995
+ exports.localeMessages = {
1996
+ de_de: de_DE_json_1.default,
1997
+ en_us: en_US_json_1.default,
1998
+ es_es: es_ES_json_1.default,
1999
+ fr_fr: fr_FR_json_1.default,
2000
+ ja_jp: ja_JP_json_1.default,
2001
+ ko_kr: ko_KR_json_1.default,
2002
+ pt_br: pt_BR_json_1.default,
2003
+ ru_ru: ru_RU_json_1.default,
2004
+ th_th: th_TH_json_1.default,
2005
+ tr_tr: tr_TR_json_1.default,
2006
+ zh_cn: zh_CN_json_1.default,
2007
+ zh_tw: zh_TW_json_1.default
2008
+ };
2009
+
2010
+
2011
+ /***/ },
2012
+
2013
+ /***/ "events"
2014
+ (module) {
2015
+
2016
+ module.exports = require("events");
2017
+
2018
+ /***/ },
2019
+
2020
+ /***/ "fs-extra"
2021
+ (module) {
2022
+
2023
+ module.exports = require("fs-extra");
2024
+
2025
+ /***/ },
2026
+
2027
+ /***/ "iconv-lite"
2028
+ (module) {
2029
+
2030
+ module.exports = require("iconv-lite");
2031
+
2032
+ /***/ },
2033
+
2034
+ /***/ "os-utils"
2035
+ (module) {
2036
+
2037
+ module.exports = require("os-utils");
2038
+
2039
+ /***/ },
2040
+
2041
+ /***/ "cordis"
2042
+ (module) {
2043
+
2044
+ module.exports = require("cordis");
2045
+
2046
+ /***/ },
2047
+
2048
+ /***/ "cosmokit"
2049
+ (module) {
2050
+
2051
+ module.exports = require("cosmokit");
2052
+
2053
+ /***/ },
2054
+
2055
+ /***/ "child_process"
2056
+ (module) {
2057
+
2058
+ module.exports = require("child_process");
2059
+
2060
+ /***/ },
2061
+
2062
+ /***/ "crypto"
2063
+ (module) {
2064
+
2065
+ module.exports = require("crypto");
2066
+
2067
+ /***/ },
2068
+
2069
+ /***/ "fs"
2070
+ (module) {
2071
+
2072
+ module.exports = require("fs");
2073
+
2074
+ /***/ },
2075
+
2076
+ /***/ "module"
2077
+ (module) {
2078
+
2079
+ module.exports = require("module");
2080
+
2081
+ /***/ },
2082
+
2083
+ /***/ "net"
2084
+ (module) {
2085
+
2086
+ module.exports = require("net");
2087
+
2088
+ /***/ },
2089
+
2090
+ /***/ "node:crypto"
2091
+ (module) {
2092
+
2093
+ module.exports = require("node:crypto");
2094
+
2095
+ /***/ },
2096
+
2097
+ /***/ "node:fs"
2098
+ (module) {
2099
+
2100
+ module.exports = require("node:fs");
2101
+
2102
+ /***/ },
2103
+
2104
+ /***/ "node:os"
2105
+ (module) {
2106
+
2107
+ module.exports = require("node:os");
2108
+
2109
+ /***/ },
2110
+
2111
+ /***/ "node:path"
2112
+ (module) {
2113
+
2114
+ module.exports = require("node:path");
2115
+
2116
+ /***/ },
2117
+
2118
+ /***/ "os"
2119
+ (module) {
2120
+
2121
+ module.exports = require("os");
2122
+
2123
+ /***/ },
2124
+
2125
+ /***/ "path"
2126
+ (module) {
2127
+
2128
+ module.exports = require("path");
2129
+
2130
+ /***/ },
2131
+
2132
+ /***/ "process"
2133
+ (module) {
2134
+
2135
+ module.exports = require("process");
2136
+
2137
+ /***/ },
2138
+
2139
+ /***/ "./plugins/config/src/i18n/de_DE.json"
2140
+ (module) {
2141
+
2142
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_DAEMON_PLUGIN_ESSENTIAL_DISABLE":"Der Webserver kann nicht aus der Ferne deaktiviert werden: Das Panel würde die Verbindung verlieren, über die es diesen Daemon verwaltet. Setze enabled auf dem Host in seiner plugin.json auf false.","TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED":"Eine Plugin-ID ist erforderlich.","TXT_CODE_DAEMON_PLUGIN_SELF_DISABLE":"Die Plugin-Verwaltung kann sich nicht selbst deaktivieren: Das Panel würde die Events verlieren, mit denen es diesen Daemon verwaltet. Setze enabled auf dem Host in seiner plugin.json auf false."}');
2143
+
2144
+ /***/ },
2145
+
2146
+ /***/ "./plugins/config/src/i18n/en_US.json"
2147
+ (module) {
2148
+
2149
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_DAEMON_PLUGIN_ESSENTIAL_DISABLE":"The web server cannot be disabled remotely: the panel would lose the connection it manages this daemon over. Set enabled to false in its plugin.json on the host instead.","TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED":"A plugin id is required.","TXT_CODE_DAEMON_PLUGIN_SELF_DISABLE":"Plugin management cannot disable itself: the panel would lose the events it administers this daemon with. Set enabled to false in its plugin.json on the host instead."}');
2150
+
2151
+ /***/ },
2152
+
2153
+ /***/ "./plugins/config/src/i18n/es_ES.json"
2154
+ (module) {
2155
+
2156
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_DAEMON_PLUGIN_ESSENTIAL_DISABLE":"El servidor web no puede desactivarse de forma remota: el panel perdería la conexión con la que administra este demonio. Establece enabled en false en su plugin.json en el host.","TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED":"Se requiere un id de complemento.","TXT_CODE_DAEMON_PLUGIN_SELF_DISABLE":"La gestión de complementos no puede desactivarse a sí misma: el panel perdería los eventos con los que administra este demonio. Establece enabled en false en su plugin.json en el host."}');
2157
+
2158
+ /***/ },
2159
+
2160
+ /***/ "./plugins/config/src/i18n/fr_FR.json"
2161
+ (module) {
2162
+
2163
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_DAEMON_PLUGIN_ESSENTIAL_DISABLE":"Le serveur web ne peut pas être désactivé à distance : le panneau perdrait la connexion par laquelle il administre ce démon. Définissez enabled à false dans son plugin.json sur l hôte.","TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED":"Un identifiant d extension est requis.","TXT_CODE_DAEMON_PLUGIN_SELF_DISABLE":"Le gestionnaire d extensions ne peut pas se désactiver lui-même : le panneau perdrait les événements par lesquels il administre ce démon. Définissez enabled à false dans son plugin.json sur l hôte."}');
2164
+
2165
+ /***/ },
2166
+
2167
+ /***/ "./plugins/config/src/i18n/ja_JP.json"
2168
+ (module) {
2169
+
2170
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_DAEMON_PLUGIN_ESSENTIAL_DISABLE":"Web サーバーはリモートから無効にできません。パネルがこのデーモンを管理している接続を失います。ホスト上で plugin.json の enabled を false に設定してください。","TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED":"プラグイン ID を指定してください。","TXT_CODE_DAEMON_PLUGIN_SELF_DISABLE":"プラグイン管理は自身を無効にできません。パネルがこのデーモンを管理しているイベントを失います。ホスト上で plugin.json の enabled を false に設定してください。"}');
2171
+
2172
+ /***/ },
2173
+
2174
+ /***/ "./plugins/config/src/i18n/ko_KR.json"
2175
+ (module) {
2176
+
2177
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_DAEMON_PLUGIN_ESSENTIAL_DISABLE":"웹 서버는 원격으로 비활성화할 수 없습니다. 패널이 이 데몬을 관리하는 연결을 잃게 됩니다. 호스트에서 plugin.json의 enabled 를 false 로 설정하세요.","TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED":"플러그인 ID가 필요합니다.","TXT_CODE_DAEMON_PLUGIN_SELF_DISABLE":"플러그인 관리는 자신을 비활성화할 수 없습니다. 패널이 이 데몬을 관리하는 이벤트를 잃게 됩니다. 호스트에서 plugin.json의 enabled 를 false 로 설정하세요."}');
2178
+
2179
+ /***/ },
2180
+
2181
+ /***/ "./plugins/config/src/i18n/pt_BR.json"
2182
+ (module) {
2183
+
2184
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_DAEMON_PLUGIN_ESSENTIAL_DISABLE":"O servidor web não pode ser desativado remotamente: o painel perderia a conexão pela qual administra este daemon. Defina enabled como false no plugin.json dele no host.","TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED":"É necessário informar o id do plugin.","TXT_CODE_DAEMON_PLUGIN_SELF_DISABLE":"O gerenciador de plugins não pode desativar a si mesmo: o painel perderia os eventos pelos quais administra este daemon. Defina enabled como false no plugin.json dele no host."}');
2185
+
2186
+ /***/ },
2187
+
2188
+ /***/ "./plugins/config/src/i18n/ru_RU.json"
2189
+ (module) {
2190
+
2191
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_DAEMON_PLUGIN_ESSENTIAL_DISABLE":"Веб-сервер нельзя отключить удалённо: панель потеряет соединение, через которое управляет этим демоном. Укажите enabled: false в его plugin.json на хосте.","TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED":"Требуется идентификатор плагина.","TXT_CODE_DAEMON_PLUGIN_SELF_DISABLE":"Менеджер плагинов не может отключить сам себя: панель потеряет события, через которые управляет этим демоном. Укажите enabled: false в его plugin.json на хосте."}');
2192
+
2193
+ /***/ },
2194
+
2195
+ /***/ "./plugins/config/src/i18n/th_TH.json"
2196
+ (module) {
2197
+
2198
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_DAEMON_PLUGIN_ESSENTIAL_DISABLE":"ไม่สามารถปิดใช้งานเว็บเซิร์ฟเวอร์จากระยะไกลได้ แผงควบคุมจะเสียการเชื่อมต่อที่ใช้จัดการดีมอนนี้ กรุณาตั้ง enabled เป็น false ใน plugin.json ของมันบนเครื่องโฮสต์","TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED":"ต้องระบุรหัสปลั๊กอิน","TXT_CODE_DAEMON_PLUGIN_SELF_DISABLE":"ตัวจัดการปลั๊กอินปิดใช้งานตัวเองไม่ได้ แผงควบคุมจะเสียอีเวนต์ที่ใช้จัดการดีมอนนี้ กรุณาตั้ง enabled เป็น false ใน plugin.json ของมันบนเครื่องโฮสต์"}');
2199
+
2200
+ /***/ },
2201
+
2202
+ /***/ "./plugins/config/src/i18n/tr_TR.json"
2203
+ (module) {
2204
+
2205
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_DAEMON_PLUGIN_ESSENTIAL_DISABLE":"Web sunucusu uzaktan devre dışı bırakılamaz: panel bu daemon u yönettiği bağlantıyı kaybeder. Bunun yerine ana makinede plugin.json dosyasında enabled değerini false yapın.","TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED":"Bir eklenti kimliği gerekli.","TXT_CODE_DAEMON_PLUGIN_SELF_DISABLE":"Eklenti yöneticisi kendisini devre dışı bırakamaz: panel bu daemon u yönettiği olayları kaybeder. Bunun yerine ana makinede plugin.json dosyasında enabled değerini false yapın."}');
2206
+
2207
+ /***/ },
2208
+
2209
+ /***/ "./plugins/config/src/i18n/zh_CN.json"
2210
+ (module) {
2211
+
2212
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_DAEMON_PLUGIN_ESSENTIAL_DISABLE":"Web 服务器不能远程禁用:面板会失去用来管理这个守护进程的连接。请在宿主机上把它的 plugin.json 里的 enabled 设为 false。","TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED":"必须提供插件 ID。","TXT_CODE_DAEMON_PLUGIN_SELF_DISABLE":"插件管理无法禁用自己:面板会失去用来管理这个守护进程的事件。请在宿主机上把它的 plugin.json 里的 enabled 设为 false。"}');
2213
+
2214
+ /***/ },
2215
+
2216
+ /***/ "./plugins/config/src/i18n/zh_TW.json"
2217
+ (module) {
2218
+
2219
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_DAEMON_PLUGIN_ESSENTIAL_DISABLE":"Web 伺服器不能遠端停用:面板會失去用來管理這個守護行程的連線。請在主機上把它的 plugin.json 裡的 enabled 設為 false。","TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED":"必須提供外掛 ID。","TXT_CODE_DAEMON_PLUGIN_SELF_DISABLE":"外掛管理無法停用自己:面板會失去用來管理這個守護行程的事件。請在主機上把它的 plugin.json 裡的 enabled 設為 false。"}');
2220
+
2221
+ /***/ }
2222
+
2223
+ /******/ });
2224
+ /************************************************************************/
2225
+ /******/ // The module cache
2226
+ /******/ var __webpack_module_cache__ = {};
2227
+ /******/
2228
+ /******/ // The require function
2229
+ /******/ function __webpack_require__(moduleId) {
2230
+ /******/ // Check if module is in cache
2231
+ /******/ var cachedModule = __webpack_module_cache__[moduleId];
2232
+ /******/ if (cachedModule !== undefined) {
2233
+ /******/ return cachedModule.exports;
2234
+ /******/ }
2235
+ /******/ // Create a new module (and put it into the cache)
2236
+ /******/ var module = __webpack_module_cache__[moduleId] = {
2237
+ /******/ // no module.id needed
2238
+ /******/ // no module.loaded needed
2239
+ /******/ exports: {}
2240
+ /******/ };
2241
+ /******/
2242
+ /******/ // Execute the module function
2243
+ /******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
2244
+ /******/
2245
+ /******/ // Return the exports of the module
2246
+ /******/ return module.exports;
2247
+ /******/ }
2248
+ /******/
2249
+ /************************************************************************/
2250
+ var __webpack_exports__ = {};
2251
+ // This entry needs to be wrapped in an IIFE because it uses a non-standard name for the exports (exports).
2252
+ (() => {
2253
+ var exports = __webpack_exports__;
2254
+
2255
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2256
+ exports.inject = void 0;
2257
+ exports.apply = apply;
2258
+ const mcsmanager_common_1 = __webpack_require__("../common/src/index.ts");
2259
+ const i18n_1 = __webpack_require__("./plugins/config/src/i18n/index.ts");
2260
+ const settings_1 = __webpack_require__("./plugins/config/src/backend/settings.ts");
2261
+ // Daemon side of the plugin manager. The daemon core owns the mechanism —
2262
+ // `ctx.plugins` lists what is installed and persists the enable switch — and
2263
+ // this plugin only exposes it over the protocol, because the panel's plugin
2264
+ // manager page is what drives it.
2265
+ //
2266
+ // Every event is reachable only over an authenticated top-level session: the
2267
+ // daemon's auth middleware rejects every event other than "auth" and "stream"
2268
+ // until the panel has presented the daemon key.
2269
+ /** Turning this plugin off would remove the events the request came in over. */
2270
+ const SELF = "config";
2271
+ /**
2272
+ * Turning the web server off would drop the connection the request arrived on,
2273
+ * and with it every event that could turn it back on.
2274
+ */
2275
+ const ESSENTIAL = new Set(["i18n", "storage", "runtime", "server"]);
2276
+ const PLUGIN_GROUPS_CATEGORY = "PluginGroups";
2277
+ const PLUGIN_GROUPS_ID = "config";
2278
+ class StoredPluginGroups {
2279
+ groups = [];
2280
+ }
2281
+ function normalizePluginGroups(input) {
2282
+ if (!Array.isArray(input))
2283
+ throw new Error("Plugin groups must be an array.");
2284
+ if (input.length > 100)
2285
+ throw new Error("Too many plugin groups.");
2286
+ return input.map((value) => {
2287
+ if (!value || typeof value !== "object")
2288
+ throw new Error("Invalid plugin group.");
2289
+ const group = value;
2290
+ const id = String(group.id ?? "").trim();
2291
+ const name = String(group.name ?? "").trim();
2292
+ if (!id || id.length > 128 || !name || name.length > 200) {
2293
+ throw new Error("Invalid plugin group.");
2294
+ }
2295
+ if (!Array.isArray(group.pluginIds) ||
2296
+ group.pluginIds.length > 2000 ||
2297
+ group.pluginIds.some((pluginId) => typeof pluginId !== "string")) {
2298
+ throw new Error("Invalid plugin group plugins.");
2299
+ }
2300
+ return {
2301
+ id,
2302
+ name,
2303
+ pluginIds: [...new Set(group.pluginIds.map((pluginId) => pluginId.trim()).filter(Boolean))]
2304
+ };
2305
+ });
2306
+ }
2307
+ exports.inject = ["protocol", "i18n", "plugins", "storage", "overview"];
2308
+ function apply(ctx) {
2309
+ ctx.i18n.define(i18n_1.localeMessages);
2310
+ ctx.plugin(settings_1.SettingsFormService);
2311
+ const settingsForm = ctx.get("settingsForm");
2312
+ if (!settingsForm)
2313
+ throw new Error("Daemon settings form service is unavailable.");
2314
+ // The panel's node page compares daemon plugins against its own inventory, so
2315
+ // the id/version pairs ride along on the overview payload it already polls.
2316
+ // `dual` marks a two-sided plugin: if its panel half is absent there, the node
2317
+ // page reports the plugin as missing rather than daemon-only by design.
2318
+ // Best-effort: hosts without an overview service (tests, minimal daemons)
2319
+ // keep loading this plugin without the extra payload.
2320
+ ctx.overview?.provide(() => ({
2321
+ plugins: ctx.plugins
2322
+ .inventory()
2323
+ .map((record) => ({ id: record.id, version: record.version, dual: record.dual }))
2324
+ }));
2325
+ // Unloads and deletes one installed plugin package. What may be removed —
2326
+ // essential plugins, development sources — is the loader's own decision; its
2327
+ // refusal arrives as a protocol error the panel's page reports.
2328
+ ctx.protocol.on("plugin/remove", async (routerCtx, data) => {
2329
+ try {
2330
+ const id = String(data?.id ?? "");
2331
+ if (!id)
2332
+ throw new Error(ctx.i18n.$t("TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED"));
2333
+ await ctx.plugins.remove(id);
2334
+ ctx.protocol.response(routerCtx, { removed: true });
2335
+ }
2336
+ catch (error) {
2337
+ ctx.protocol.responseError(routerCtx, error);
2338
+ }
2339
+ });
2340
+ ctx.protocol.on("plugin/capabilities", (routerCtx) => {
2341
+ ctx.protocol.response(routerCtx, { api: mcsmanager_common_1.PLUGIN_API_VERSION });
2342
+ });
2343
+ ctx.protocol.on("plugin/list", (routerCtx) => {
2344
+ const declared = new Set(settingsForm.declared());
2345
+ ctx.protocol.response(routerCtx, ctx.plugins.inventory().map((record) => ({
2346
+ ...record,
2347
+ // So the panel can say "no configurable options" without a round trip.
2348
+ hasSettings: declared.has(record.id)
2349
+ })));
2350
+ });
2351
+ ctx.protocol.on("plugin/enabled", async (routerCtx, data) => {
2352
+ try {
2353
+ const payload = (data ?? {});
2354
+ const id = String(payload.id ?? "");
2355
+ const enabled = Boolean(payload.enabled);
2356
+ if (!id)
2357
+ throw new Error(ctx.i18n.$t("TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED"));
2358
+ if (!enabled && id === SELF) {
2359
+ throw new Error(ctx.i18n.$t("TXT_CODE_DAEMON_PLUGIN_SELF_DISABLE"));
2360
+ }
2361
+ if (!enabled && ESSENTIAL.has(id)) {
2362
+ throw new Error(ctx.i18n.$t("TXT_CODE_DAEMON_PLUGIN_ESSENTIAL_DISABLE"));
2363
+ }
2364
+ ctx.protocol.response(routerCtx, await ctx.plugins.setEnabled(id, enabled));
2365
+ }
2366
+ catch (error) {
2367
+ // The panel turns this into the error its page reports; an unhandled throw
2368
+ // from an async handler would reach nobody, because `emitRouter` only
2369
+ // catches the synchronous part.
2370
+ ctx.protocol.responseError(routerCtx, error);
2371
+ }
2372
+ });
2373
+ // A plugin installed from the market is an npm dependency of the daemon's
2374
+ // installation project. npm runs on the machine that will load the plugin, so
2375
+ // the panel only names the package and the version.
2376
+ ctx.protocol.on("plugin/npm/install", async (routerCtx, data) => {
2377
+ try {
2378
+ const payload = (data ?? {});
2379
+ const record = await (0, mcsmanager_common_1.installNpmPlugin)({
2380
+ name: String(payload.name ?? ""),
2381
+ pluginId: String(payload.pluginId ?? ""),
2382
+ version: String(payload.version ?? ""),
2383
+ side: "daemon"
2384
+ });
2385
+ ctx.protocol.response(routerCtx, record);
2386
+ }
2387
+ catch (error) {
2388
+ ctx.protocol.responseError(routerCtx, error);
2389
+ }
2390
+ });
2391
+ ctx.protocol.on("plugin/npm/uninstall", async (routerCtx, data) => {
2392
+ try {
2393
+ const pluginId = String(data?.pluginId ?? "");
2394
+ const removed = await (0, mcsmanager_common_1.uninstallNpmPlugin)(pluginId);
2395
+ ctx.protocol.response(routerCtx, { removed, pluginId });
2396
+ }
2397
+ catch (error) {
2398
+ ctx.protocol.responseError(routerCtx, error);
2399
+ }
2400
+ });
2401
+ // Re-scan the plugin directories for a plugin that arrived while the daemon
2402
+ // was running. The panel reconnects after this response so protocol handlers
2403
+ // from a newly loaded plugin are bound to a fresh socket.
2404
+ ctx.protocol.on("plugin/reload", async (routerCtx) => {
2405
+ try {
2406
+ await ctx.plugins.reload();
2407
+ ctx.protocol.response(routerCtx, ctx.plugins.inventory());
2408
+ }
2409
+ catch (error) {
2410
+ ctx.protocol.responseError(routerCtx, error);
2411
+ }
2412
+ });
2413
+ // The configuration of one of this daemon's plugins, described by the plugin
2414
+ // itself. The panel renders the description; nothing about the form lives
2415
+ // there, which is the only way a daemon plugin can have a settings page at all.
2416
+ ctx.protocol.on("plugin/config", async (routerCtx, data) => {
2417
+ try {
2418
+ const id = String(data?.id ?? "");
2419
+ if (!id)
2420
+ throw new Error(ctx.i18n.$t("TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED"));
2421
+ ctx.protocol.response(routerCtx, await settingsForm.read(id));
2422
+ }
2423
+ catch (error) {
2424
+ ctx.protocol.responseError(routerCtx, error);
2425
+ }
2426
+ });
2427
+ ctx.protocol.on("plugin/config/write", async (routerCtx, data) => {
2428
+ try {
2429
+ const payload = (data ?? {});
2430
+ const id = String(payload.id ?? "");
2431
+ if (!id)
2432
+ throw new Error(ctx.i18n.$t("TXT_CODE_DAEMON_PLUGIN_ID_REQUIRED"));
2433
+ const values = (payload.values ?? {});
2434
+ ctx.protocol.response(routerCtx, await settingsForm.write(id, values));
2435
+ }
2436
+ catch (error) {
2437
+ ctx.protocol.responseError(routerCtx, error);
2438
+ }
2439
+ });
2440
+ ctx.protocol.on("plugin/groups", (routerCtx) => {
2441
+ try {
2442
+ const stored = ctx.storage.load(PLUGIN_GROUPS_CATEGORY, StoredPluginGroups, PLUGIN_GROUPS_ID);
2443
+ ctx.protocol.response(routerCtx, stored?.groups ?? []);
2444
+ }
2445
+ catch (error) {
2446
+ ctx.protocol.responseError(routerCtx, error);
2447
+ }
2448
+ });
2449
+ ctx.protocol.on("plugin/groups/write", (routerCtx, data) => {
2450
+ try {
2451
+ const groups = normalizePluginGroups(data?.groups);
2452
+ ctx.storage.store(PLUGIN_GROUPS_CATEGORY, PLUGIN_GROUPS_ID, { groups });
2453
+ ctx.protocol.response(routerCtx, groups);
2454
+ }
2455
+ catch (error) {
2456
+ ctx.protocol.responseError(routerCtx, error);
2457
+ }
2458
+ });
2459
+ }
2460
+
2461
+ })();
2462
+
2463
+ module.exports = __webpack_exports__;
2464
+ /******/ })()
2465
+ ;