@elementspanel/epanel-plugin-instance 1.0.5 → 1.0.6

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.
@@ -226,9 +226,26 @@ function commandStringToArray(cmd) {
226
226
 
227
227
  Object.defineProperty(exports, "__esModule", ({ value: true }));
228
228
  exports.ChildProcessAdapter = void 0;
229
+ exports.instanceProcessHost = instanceProcessHost;
230
+ exports.spawnInstanceProcess = spawnInstanceProcess;
229
231
  exports.waitForSpawn = waitForSpawn;
230
232
  const events_1 = __webpack_require__("events");
231
233
  const mcsmanager_common_1 = __webpack_require__("../common/src/index.ts");
234
+ const INSTANCE_HOST = Symbol.for("elementspanel.instance-process-host");
235
+ function instanceProcessHost() {
236
+ return globalThis[INSTANCE_HOST];
237
+ }
238
+ function spawnInstanceProcess(instance, fallback, file, args, options, metadata = {}) {
239
+ const host = instanceProcessHost();
240
+ return host
241
+ ? host.spawn(instance.instanceUuid, file, args, options, {
242
+ startTimestamp: instance.startTimestamp,
243
+ startCount: instance.startCount,
244
+ autoRestartCount: instance.autoRestartCount,
245
+ ...metadata
246
+ })
247
+ : fallback(file, args, options);
248
+ }
232
249
  /** spawn() reports failures asynchronously, including a missing executable. */
233
250
  function waitForSpawn(process) {
234
251
  return new Promise((resolve, reject) => {
@@ -266,7 +283,9 @@ class ChildProcessAdapter extends events_1.EventEmitter {
266
283
  super();
267
284
  this.child = child;
268
285
  this.pid = child.pid;
269
- child.once("close", () => { this.closed = true; });
286
+ child.once("close", () => {
287
+ this.closed = true;
288
+ });
270
289
  // close follows the final stdout/stderr data; exit can arrive before it.
271
290
  child.on("close", this.onExit);
272
291
  child.on("error", this.onError);
@@ -292,6 +311,9 @@ class ChildProcessAdapter extends events_1.EventEmitter {
292
311
  if (this.pid)
293
312
  return (0, mcsmanager_common_1.killProcess)(this.pid, this.child, signal);
294
313
  }
314
+ snapshot(metadata) {
315
+ this.child.setMetadata?.(metadata);
316
+ }
295
317
  destroy() {
296
318
  if (this.disposed)
297
319
  return;
@@ -326,6 +348,7 @@ class ChildProcessAdapter extends events_1.EventEmitter {
326
348
  this.child.stdin?.destroy();
327
349
  this.child.stdout?.destroy();
328
350
  this.child.stderr?.destroy();
351
+ this.child.release?.();
329
352
  }
330
353
  }
331
354
  }
@@ -1030,20 +1053,19 @@ class GeneralStartCommand extends start_1.default {
1030
1053
  instance.println("INFO", (0, i18n_1.$t)("TXT_CODE_ba09da46", { name: runAsConfig.runAsName }));
1031
1054
  }
1032
1055
  // create child process
1033
- const subProcess = (0, child_process_1.spawn)(commandExeFile, commandParameters, {
1056
+ const subProcess = (0, process_adapter_1.spawnInstanceProcess)(instance, child_process_1.spawn, commandExeFile, commandParameters, {
1034
1057
  ...runAsConfig,
1035
1058
  cwd: instance.absoluteCwdPath(),
1036
1059
  stdio: "pipe",
1037
1060
  windowsHide: true,
1038
1061
  env: instance.generateEnv(),
1039
- // Do not detach the child process;
1040
- // otherwise, an abnormal exit of the parent process may cause the child process to continue running,
1041
- // leading to an abnormal instance state.
1042
1062
  detached: false
1043
1063
  });
1044
1064
  const processAdapter = new process_adapter_1.ChildProcessAdapter(subProcess);
1045
1065
  try {
1046
1066
  await (0, process_adapter_1.waitForSpawn)(subProcess);
1067
+ processAdapter.pid = subProcess.pid;
1068
+ subProcess.setMetadata?.({ pid: subProcess.pid });
1047
1069
  instance.started(processAdapter);
1048
1070
  }
1049
1071
  catch (error) {
@@ -1470,17 +1492,14 @@ class PtyStartCommand extends start_1.default {
1470
1492
  }
1471
1493
  instance.println("INFO", "> " + commandList.join(" "));
1472
1494
  // create pty child process
1473
- const subProcess = (0, child_process_1.spawn)(const_1.PTY_PATH, ptyParameter, {
1495
+ const subProcess = (0, process_adapter_1.spawnInstanceProcess)(instance, child_process_1.spawn, const_1.PTY_PATH, ptyParameter, {
1474
1496
  ...runAsConfig,
1475
1497
  cwd: path_1.default.dirname(const_1.PTY_PATH),
1476
1498
  stdio: "pipe",
1477
1499
  windowsHide: true,
1478
1500
  env: instance.generateEnv(),
1479
- // Do not detach the child process;
1480
- // otherwise, an abnormal exit of the parent process may cause the child process to continue running,
1481
- // leading to an abnormal instance state.
1482
1501
  detached: false
1483
- });
1502
+ }, { pipeName });
1484
1503
  const processAdapter = new GoPtyProcessAdapter(subProcess, subProcess.pid, pipeName);
1485
1504
  try {
1486
1505
  const [, config] = await Promise.all([
@@ -1488,6 +1507,7 @@ class PtyStartCommand extends start_1.default {
1488
1507
  this.readPtySubProcessConfig(subProcess)
1489
1508
  ]);
1490
1509
  processAdapter.pid = config.pid;
1510
+ subProcess.setMetadata?.({ pid: config.pid });
1491
1511
  if (subProcess.exitCode !== null || subProcess.signalCode !== null)
1492
1512
  throw new StartupError((0, i18n_1.$t)("TXT_CODE_pty_start.instanceStartErr"));
1493
1513
  instance.started(processAdapter);
@@ -1547,6 +1567,17 @@ class AbsStartCommand extends command_1.default {
1547
1567
  // Claim the state before the first await so two starts cannot both proceed.
1548
1568
  instance.setLock(true);
1549
1569
  instance.status(instance_1.default.STATUS_STARTING);
1570
+ const starting = this.start(instance);
1571
+ instance.starting = starting;
1572
+ try {
1573
+ return await starting;
1574
+ }
1575
+ finally {
1576
+ instance.starting = undefined;
1577
+ instance.setLock(false);
1578
+ }
1579
+ }
1580
+ async start(instance) {
1550
1581
  try {
1551
1582
  if (!fs_extra_1.default.existsSync(instance.absoluteCwdPath())) {
1552
1583
  await fs_extra_1.default.mkdirs(instance.absoluteCwdPath());
@@ -1595,9 +1626,6 @@ class AbsStartCommand extends command_1.default {
1595
1626
  instance.startTimestamp = 0;
1596
1627
  instance.failure(error);
1597
1628
  }
1598
- finally {
1599
- instance.setLock(false);
1600
- }
1601
1629
  }
1602
1630
  }
1603
1631
  exports["default"] = AbsStartCommand;
@@ -2138,6 +2166,7 @@ class Instance extends events_1.EventEmitter {
2138
2166
  };
2139
2167
  watchers = new Map();
2140
2168
  process;
2169
+ starting;
2141
2170
  outputLoopTask;
2142
2171
  outputBuffer;
2143
2172
  stopping;
@@ -2336,9 +2365,22 @@ class Instance extends events_1.EventEmitter {
2336
2365
  }
2337
2366
  // function that must be executed after the instance starts
2338
2367
  // Trigger the open event and bind the data and exit events, etc.
2339
- started(process) {
2340
- this.startTimestamp = this.config.lastDatetime = Date.now();
2341
- this.userRequestedStop = false;
2368
+ started(process, previous) {
2369
+ this.startTimestamp = this.config.lastDatetime = previous?.startTimestamp || Date.now();
2370
+ if (previous) {
2371
+ this.startCount = previous.startCount;
2372
+ this.autoRestartCount = previous.autoRestartCount;
2373
+ }
2374
+ this.userRequestedStop = previous?.userRequestedStop ?? false;
2375
+ if (this.userRequestedStop)
2376
+ this.ignoreEventTaskOnce();
2377
+ process.snapshot?.({
2378
+ pid: Number(process.pid),
2379
+ startTimestamp: this.startTimestamp,
2380
+ startCount: this.startCount,
2381
+ autoRestartCount: this.autoRestartCount,
2382
+ userRequestedStop: this.userRequestedStop
2383
+ });
2342
2384
  const outputCode = this.config.terminalOption.pty ? "utf-8" : this.config.oe;
2343
2385
  process.on("data", (text) => {
2344
2386
  this.pushOutput(iconv_lite_1.default.decode(text, outputCode));
@@ -2442,6 +2484,7 @@ class Instance extends events_1.EventEmitter {
2442
2484
  this.stopOutputLoop();
2443
2485
  this.reloading = true;
2444
2486
  this.watchers.clear();
2487
+ await this.starting?.catch(() => { });
2445
2488
  await this.lifeCycleTaskManager.clearLifeCycleTask();
2446
2489
  }
2447
2490
  async resumeAfterReload(dispatcher) {
@@ -2479,6 +2522,7 @@ class Instance extends events_1.EventEmitter {
2479
2522
  }
2480
2523
  setUserRequestedStop(value) {
2481
2524
  this.userRequestedStop = value;
2525
+ this.process?.snapshot?.({ userRequestedStop: value });
2482
2526
  }
2483
2527
  getUserRequestedStop() {
2484
2528
  return this.userRequestedStop;
@@ -2995,6 +3039,17 @@ function apply(ctx) {
2995
3039
  let preserving = false;
2996
3040
  let subsystem;
2997
3041
  let disposeSchedules;
3042
+ let initializing;
3043
+ ctx.on("process/starting", async () => {
3044
+ if (!initializing)
3045
+ throw new Error("Instance services failed to initialize.");
3046
+ await initializing;
3047
+ await subsystem?.waitForTakeover();
3048
+ });
3049
+ ctx.on("process/restarting", async () => {
3050
+ disposeSchedules?.();
3051
+ await subsystem?.prepareForRestart();
3052
+ });
2998
3053
  ctx.on("plugins/reloading", async () => {
2999
3054
  const inventory = ctx.get("plugins")?.inventory();
3000
3055
  preserving = inventory?.some((plugin) => plugin.id === "instance" && plugin.enabled) ?? true;
@@ -3009,7 +3064,11 @@ function apply(ctx) {
3009
3064
  // are available on the next turn rather than synchronously in this function.
3010
3065
  // Restoring instances before that point makes the legacy dispatcher read
3011
3066
  // missing `presets` and `instanceLifecycle` services and emit warnings.
3012
- ctx.inject(["tasks", "instanceLifecycle", "presets", "schedules"], async (instanceCtx) => {
3067
+ ctx.inject(["tasks", "instanceLifecycle", "presets", "schedules"], (instanceCtx) => {
3068
+ initializing = initialize(instanceCtx);
3069
+ return initializing;
3070
+ });
3071
+ async function initialize(instanceCtx) {
3013
3072
  // The modules are required only after the context has been set and the
3014
3073
  // instance registries are ready. Keeping these synchronous requires keeps
3015
3074
  // webpack's output self-contained and avoids a second copy of the context.
@@ -3063,7 +3122,12 @@ function apply(ctx) {
3063
3122
  await disposeInstanceEvents?.();
3064
3123
  InstanceControlSubsystem.dispose();
3065
3124
  router_1.routerApp.dispose();
3066
- if ((preserving || recovering) && !runtime.stopping && ctx.root.scope.isActive) {
3125
+ if (runtime.restarting) {
3126
+ // The launcher owns general/PTY processes; Docker is reattached at
3127
+ // startup. Disposing adapters here would terminate those live owners.
3128
+ await InstanceSubsystem.prepareForRestart();
3129
+ }
3130
+ else if ((preserving || recovering) && !runtime.stopping && ctx.root.scope.isActive) {
3067
3131
  await InstanceSubsystem.suspendForReload();
3068
3132
  retain();
3069
3133
  }
@@ -3161,7 +3225,7 @@ function apply(ctx) {
3161
3225
  // Also allow recovery by enabling/retrying only the instance plugin.
3162
3226
  instanceCtx.on("ready", resume);
3163
3227
  }
3164
- });
3228
+ }
3165
3229
  }
3166
3230
 
3167
3231
 
@@ -5551,14 +5615,19 @@ function setPluginContext(value) {
5551
5615
  context: value,
5552
5616
  settings: value.settings,
5553
5617
  storage: value.storage,
5554
- stopping: false
5618
+ stopping: false,
5619
+ restarting: false
5555
5620
  };
5556
5621
  host[INSTANCE_RUNTIME] = runtime = state;
5622
+ host.on("process/restarting", () => {
5623
+ state.restarting = true;
5624
+ });
5557
5625
  host.on("dispose", async () => {
5558
5626
  state.stopping = true;
5559
5627
  // A replacement may fail before it can claim the retained processes.
5560
5628
  // The daemon still owns them and must stop them during real shutdown.
5561
- await state.handover?.shutdown();
5629
+ if (!state.restarting)
5630
+ await state.handover?.shutdown();
5562
5631
  state.handover = undefined;
5563
5632
  });
5564
5633
  }
@@ -7356,6 +7425,7 @@ const sleep_1 = __webpack_require__("../../plugins/instance/daemon/src/backend/u
7356
7425
  const log_1 = __importDefault(__webpack_require__("../../plugins/instance/daemon/src/backend/service/log.ts"));
7357
7426
  const system_instance_control_1 = __importDefault(__webpack_require__("../../plugins/instance/daemon/src/backend/service/system_instance_control.ts"));
7358
7427
  const takeover_container_1 = __importDefault(__webpack_require__("../../plugins/instance/daemon/src/backend/service/takeover_container.ts"));
7428
+ const takeover_processes_1 = __importDefault(__webpack_require__("../../plugins/instance/daemon/src/backend/service/takeover_processes.ts"));
7359
7429
  class InstanceSubsystem extends events_1.default {
7360
7430
  GLOBAL_INSTANCE = "__MCSM_GLOBAL_INSTANCE__";
7361
7431
  GLOBAL_INSTANCE_UUID = "global0001";
@@ -7464,14 +7534,26 @@ class InstanceSubsystem extends events_1.default {
7464
7534
  processType: globalConfig.processType
7465
7535
  }, true, // allow persistence
7466
7536
  this.GLOBAL_INSTANCE_UUID);
7467
- this.takeover = (0, takeover_container_1.default)(() => !this.startupCancelled).catch((error) => {
7468
- const reason = error.message || error;
7469
- if (typeof reason == "string" && reason.includes("connect ENOENT")) {
7470
- return;
7537
+ this.takeover = (async () => {
7538
+ await (0, takeover_processes_1.default)(() => !this.startupCancelled);
7539
+ try {
7540
+ await (0, takeover_container_1.default)(() => !this.startupCancelled);
7471
7541
  }
7472
- log_1.default.error(`${(0, i18n_1.$t)("TXT_CODE_8d4c8f7e")}: ${reason}`);
7473
- });
7474
- void this.takeover.then(() => this.autoStart());
7542
+ catch (error) {
7543
+ const reason = error.message || error;
7544
+ if (typeof reason == "string" && reason.includes("connect ENOENT"))
7545
+ return;
7546
+ log_1.default.error(`${(0, i18n_1.$t)("TXT_CODE_8d4c8f7e")}: ${reason}`);
7547
+ }
7548
+ })();
7549
+ void this.takeover
7550
+ .then(() => {
7551
+ // A worker replacement must also preserve intentionally stopped
7552
+ // instances, even when their next full launcher startup is automatic.
7553
+ if (process.env.EPANEL_RESTARTED !== "1")
7554
+ return this.autoStart();
7555
+ })
7556
+ .catch((error) => log_1.default.error(error));
7475
7557
  }
7476
7558
  createInstance(cfg, persistence = true, uuid) {
7477
7559
  const newUuid = uuid || (0, uuid_1.v4)().replace(/-/gim, "");
@@ -7546,6 +7628,22 @@ class InstanceSubsystem extends events_1.default {
7546
7628
  await Promise.all([...this.instances.values()].map((instance) => instance.suspendForReload()));
7547
7629
  await this.takeover;
7548
7630
  }
7631
+ async waitForTakeover() {
7632
+ await this.takeover;
7633
+ }
7634
+ async prepareForRestart() {
7635
+ await this.suspendForReload();
7636
+ for (const instance of this.instances.values()) {
7637
+ system_storage_1.default.store("InstanceConfig", instance.instanceUuid, instance.config);
7638
+ instance.process?.snapshot?.({
7639
+ pid: Number(instance.process.pid),
7640
+ startTimestamp: instance.startTimestamp,
7641
+ startCount: instance.startCount,
7642
+ autoRestartCount: instance.autoRestartCount,
7643
+ userRequestedStop: instance.getUserRequestedStop()
7644
+ });
7645
+ }
7646
+ }
7549
7647
  /** Transfer the existing owners, including ordinary child/PTY/Docker handles. */
7550
7648
  adoptInstances(instances) {
7551
7649
  this.startupCancelled = true;
@@ -8131,6 +8229,46 @@ async function takeoverContainer(isActive = () => true) {
8131
8229
  }
8132
8230
 
8133
8231
 
8232
+ /***/ },
8233
+
8234
+ /***/ "../../plugins/instance/daemon/src/backend/service/takeover_processes.ts"
8235
+ (__unused_webpack_module, exports, __webpack_require__) {
8236
+
8237
+ "use strict";
8238
+
8239
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8240
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8241
+ };
8242
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
8243
+ exports["default"] = takeoverProcesses;
8244
+ const process_adapter_1 = __webpack_require__("../../plugins/instance/daemon/src/backend/entity/commands/base/process_adapter.ts");
8245
+ const pty_start_1 = __webpack_require__("../../plugins/instance/daemon/src/backend/entity/commands/pty/pty_start.ts");
8246
+ const system_instance_1 = __importDefault(__webpack_require__("../../plugins/instance/daemon/src/backend/service/system_instance.ts"));
8247
+ /** Reattach the launcher's live pipes; do not execute the instance start command. */
8248
+ async function takeoverProcesses(isActive = () => true) {
8249
+ const host = (0, process_adapter_1.instanceProcessHost)();
8250
+ if (!host)
8251
+ return;
8252
+ for (const snapshot of await host.list()) {
8253
+ if (!isActive())
8254
+ return;
8255
+ const instance = system_instance_1.default.getInstance(snapshot.id);
8256
+ const child = host.connect(snapshot);
8257
+ const { pipeName, pid } = snapshot.metadata;
8258
+ const adapter = pipeName
8259
+ ? new pty_start_1.GoPtyProcessAdapter(child, pid, pipeName)
8260
+ : new process_adapter_1.ChildProcessAdapter(child);
8261
+ if (!instance || instance.process) {
8262
+ await adapter.destroy();
8263
+ continue;
8264
+ }
8265
+ instance.started(adapter, snapshot.metadata);
8266
+ if (pipeName)
8267
+ adapter.attachOutput();
8268
+ }
8269
+ }
8270
+
8271
+
8134
8272
  /***/ },
8135
8273
 
8136
8274
  /***/ "../../plugins/instance/daemon/src/backend/service/version_adapter.ts"
@@ -8634,6 +8772,7 @@ Object.defineProperty(exports, "arrayUnique", ({ enumerable: true, get: function
8634
8772
  __exportStar(__webpack_require__("../common/src/plugin_contract.ts"), exports);
8635
8773
  __exportStar(__webpack_require__("../common/src/plugin_overrides.ts"), exports);
8636
8774
  __exportStar(__webpack_require__("../common/src/plugin_lifecycle.ts"), exports);
8775
+ __exportStar(__webpack_require__("../common/src/process_restart.ts"), exports);
8637
8776
  __exportStar(__webpack_require__("../common/src/plugin_revision.ts"), exports);
8638
8777
  __exportStar(__webpack_require__("../common/src/plugin_modules.ts"), exports);
8639
8778
  __exportStar(__webpack_require__("../common/src/plugin_watch.ts"), exports);
@@ -10267,6 +10406,50 @@ function watchNpmPlugins(ctx, side, reload, changed) {
10267
10406
  }
10268
10407
 
10269
10408
 
10409
+ /***/ },
10410
+
10411
+ /***/ "../common/src/process_restart.ts"
10412
+ (__unused_webpack_module, exports) {
10413
+
10414
+ "use strict";
10415
+
10416
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
10417
+ exports.isProcessRestartPending = isProcessRestartPending;
10418
+ exports.requestProcessRestart = requestProcessRestart;
10419
+ const RESTART = Symbol.for("elementspanel.process.restart");
10420
+ function isProcessRestartPending(ctx) {
10421
+ return ctx.root[RESTART]?.pending === true;
10422
+ }
10423
+ /** The CLI retains instance processes and starts a fresh worker after this one exits. */
10424
+ function requestProcessRestart(ctx, settle, ready = async () => { }) {
10425
+ if (isProcessRestartPending(ctx))
10426
+ return true;
10427
+ if (process.env.EPANEL_SUPERVISED !== "1" || !process.connected || !process.send)
10428
+ return false;
10429
+ const state = { pending: true };
10430
+ ctx.root[RESTART] = state;
10431
+ const retainProcesses = () => new Promise((resolve, reject) => process.send({ type: "elementspanel:prepare-restart" }, (error) => error ? reject(error) : resolve()));
10432
+ setImmediate(() => {
10433
+ void (async () => {
10434
+ await ready();
10435
+ // Detach output consumers before cleanup so output and exits during the
10436
+ // restart are retained for the next daemon generation.
10437
+ await retainProcesses();
10438
+ await ctx.parallel("process/restarting");
10439
+ // A pending start can finish while restart hooks are awaiting it.
10440
+ await retainProcesses();
10441
+ await ctx.stop();
10442
+ await settle();
10443
+ process.send({ type: "elementspanel:restart" }, (error) => process.exit(error ? 1 : 0));
10444
+ })().catch((error) => {
10445
+ ctx.logger.error("Failed to restart the process:", error);
10446
+ process.exit(1);
10447
+ });
10448
+ });
10449
+ return true;
10450
+ }
10451
+
10452
+
10270
10453
  /***/ },
10271
10454
 
10272
10455
  /***/ "../common/src/process_tools.ts"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementspanel/epanel-plugin-instance",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Application instance and Java runtime management.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Csituka_D <694428809@qq.com> (https://github.com/JessDaodao)",
@@ -50,8 +50,8 @@
50
50
  }
51
51
  },
52
52
  "peerDependencies": {
53
- "@elementspanel/panel": "^1.0.2",
54
- "@elementspanel/daemon": "^1.0.2"
53
+ "@elementspanel/panel": "^1.0.3",
54
+ "@elementspanel/daemon": "^1.0.3"
55
55
  },
56
56
  "peerDependenciesMeta": {
57
57
  "@elementspanel/panel": {
@@ -9468,6 +9468,7 @@ Object.defineProperty(exports, "arrayUnique", ({ enumerable: true, get: function
9468
9468
  __exportStar(__webpack_require__("../common/src/plugin_contract.ts"), exports);
9469
9469
  __exportStar(__webpack_require__("../common/src/plugin_overrides.ts"), exports);
9470
9470
  __exportStar(__webpack_require__("../common/src/plugin_lifecycle.ts"), exports);
9471
+ __exportStar(__webpack_require__("../common/src/process_restart.ts"), exports);
9471
9472
  __exportStar(__webpack_require__("../common/src/plugin_revision.ts"), exports);
9472
9473
  __exportStar(__webpack_require__("../common/src/plugin_modules.ts"), exports);
9473
9474
  __exportStar(__webpack_require__("../common/src/plugin_watch.ts"), exports);
@@ -11134,6 +11135,51 @@ function watchNpmPlugins(ctx, side, reload, changed) {
11134
11135
  }
11135
11136
 
11136
11137
 
11138
+ /***/ },
11139
+
11140
+ /***/ "../common/src/process_restart.ts"
11141
+ (__unused_webpack_module, exports) {
11142
+
11143
+ "use strict";
11144
+
11145
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
11146
+ exports.isProcessRestartPending = isProcessRestartPending;
11147
+ exports.requestProcessRestart = requestProcessRestart;
11148
+ const RESTART = Symbol.for("elementspanel.process.restart");
11149
+ function isProcessRestartPending(ctx) {
11150
+ var _a;
11151
+ return ((_a = ctx.root[RESTART]) === null || _a === void 0 ? void 0 : _a.pending) === true;
11152
+ }
11153
+ /** The CLI retains instance processes and starts a fresh worker after this one exits. */
11154
+ function requestProcessRestart(ctx, settle, ready = async () => { }) {
11155
+ if (isProcessRestartPending(ctx))
11156
+ return true;
11157
+ if (process.env.EPANEL_SUPERVISED !== "1" || !process.connected || !process.send)
11158
+ return false;
11159
+ const state = { pending: true };
11160
+ ctx.root[RESTART] = state;
11161
+ const retainProcesses = () => new Promise((resolve, reject) => process.send({ type: "elementspanel:prepare-restart" }, (error) => error ? reject(error) : resolve()));
11162
+ setImmediate(() => {
11163
+ void (async () => {
11164
+ await ready();
11165
+ // Detach output consumers before cleanup so output and exits during the
11166
+ // restart are retained for the next daemon generation.
11167
+ await retainProcesses();
11168
+ await ctx.parallel("process/restarting");
11169
+ // A pending start can finish while restart hooks are awaiting it.
11170
+ await retainProcesses();
11171
+ await ctx.stop();
11172
+ await settle();
11173
+ process.send({ type: "elementspanel:restart" }, (error) => process.exit(error ? 1 : 0));
11174
+ })().catch((error) => {
11175
+ ctx.logger.error("Failed to restart the process:", error);
11176
+ process.exit(1);
11177
+ });
11178
+ });
11179
+ return true;
11180
+ }
11181
+
11182
+
11137
11183
  /***/ },
11138
11184
 
11139
11185
  /***/ "../common/src/process_tools.ts"