@camstack/system 1.2.310 → 1.2.312

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.
Files changed (68) hide show
  1. package/dist/addon-runner.js +6 -8
  2. package/dist/addon-runner.mjs +6 -8
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/autotrack/index.js +1 -1
  10. package/dist/builtins/autotrack/index.mjs +1 -1
  11. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  13. package/dist/builtins/camera-grid/index.js +1 -1
  14. package/dist/builtins/camera-grid/index.mjs +1 -1
  15. package/dist/builtins/composer/composer.addon.js +1 -1
  16. package/dist/builtins/composer/composer.addon.mjs +1 -1
  17. package/dist/builtins/console-logging/index.js +1 -1
  18. package/dist/builtins/console-logging/index.mjs +1 -1
  19. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  20. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  21. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  22. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  23. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  24. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  25. package/dist/builtins/hub-forwarder/index.js +1 -1
  26. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  27. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  28. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  29. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  30. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  31. package/dist/builtins/local-network/local-network.addon.js +1 -1
  32. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  33. package/dist/builtins/loki-logging/index.js +1 -1
  34. package/dist/builtins/loki-logging/index.mjs +1 -1
  35. package/dist/builtins/native-metrics/native-metrics.addon.js +10 -102
  36. package/dist/builtins/native-metrics/native-metrics.addon.mjs +4 -92
  37. package/dist/builtins/platform-probe/index.js +1 -1
  38. package/dist/builtins/platform-probe/index.mjs +1 -1
  39. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  40. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  41. package/dist/builtins/snapshot/index.js +1 -1
  42. package/dist/builtins/snapshot/index.mjs +1 -1
  43. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  44. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  45. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  46. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  47. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  48. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  49. package/dist/builtins/system-config/system-config.addon.js +1 -1
  50. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  51. package/dist/builtins/winston-logging/index.js +1 -1
  52. package/dist/builtins/winston-logging/index.mjs +1 -1
  53. package/dist/{child-cap-dispatch-BCJjUtXV.mjs → child-cap-dispatch-CHTiHbsv.mjs} +294 -10
  54. package/dist/{child-cap-dispatch-Be8e2cBP.js → child-cap-dispatch-Dyn_7ezH.js} +347 -9
  55. package/dist/{dist-B78pkrps.mjs → dist-Cj7pJmXq.mjs} +35 -4
  56. package/dist/{dist-Cy6DFFjs.js → dist-CkRwnhbo.js} +35 -4
  57. package/dist/index.js +88 -18
  58. package/dist/index.mjs +89 -20
  59. package/dist/kernel/index.d.ts +1 -0
  60. package/dist/kernel/moleculer/process-service.d.ts +9 -0
  61. package/dist/kernel/moleculer/runner-process-group.d.ts +92 -0
  62. package/dist/proc-stat-CbC55Kqi.mjs +92 -0
  63. package/dist/proc-stat-DL-WUOhl.js +127 -0
  64. package/dist/{resource-monitor-Vp0bPV3U.mjs → resource-monitor-B1v6E07R.mjs} +1 -1
  65. package/dist/{resource-monitor-LVE1BoLs.js → resource-monitor-DzNn_IZK.js} +5 -5
  66. package/dist/{retired-settings-keys-B4_hiWsm.mjs → retired-settings-keys-C1duPikd.mjs} +1 -1
  67. package/dist/{retired-settings-keys-D9sgD6Yg.js → retired-settings-keys-CGSuXhP8.js} +1 -1
  68. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-Cek0wNdY.js");
3
- const require_dist = require("./dist-Cy6DFFjs.js");
3
+ const require_dist = require("./dist-CkRwnhbo.js");
4
4
  const require_builtins_alerts_alerts_addon = require("./builtins/alerts/alerts.addon.js");
5
5
  require("./builtins/alerts/index.js");
6
6
  const require_builtins_composer_composer_addon = require("./builtins/composer/composer.addon.js");
@@ -29,8 +29,8 @@ require("./builtins/system-config/index.js");
29
29
  const require_builtins_winston_logging_index = require("./builtins/winston-logging/index.js");
30
30
  const require_event_loop_stall_monitor = require("./event-loop-stall-monitor-ey4mbofz.js");
31
31
  const require_tls$1 = require("./tls-D_wUeOTD.js");
32
- const require_child_cap_dispatch = require("./child-cap-dispatch-Be8e2cBP.js");
33
- const require_resource_monitor = require("./resource-monitor-LVE1BoLs.js");
32
+ const require_child_cap_dispatch = require("./child-cap-dispatch-Dyn_7ezH.js");
33
+ const require_resource_monitor = require("./resource-monitor-DzNn_IZK.js");
34
34
  const require_custom_action_registry = require("./custom-action-registry-acBY3mHA.js");
35
35
  let zod = require("zod");
36
36
  let node_crypto = require("node:crypto");
@@ -95015,7 +95015,10 @@ var CRASH_WINDOW_MS = 6e4;
95015
95015
  var MAX_CRASHES_IN_WINDOW = 5;
95016
95016
  function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parentUdsPath) {
95017
95017
  const spawnFn = deps?.spawnFn ?? node_child_process.spawn;
95018
+ const groupOps = deps?.processGroups ?? require_child_cap_dispatch.defaultProcessGroupOps;
95019
+ const runnersOwnGroups = require_child_cap_dispatch.runnerOwnsProcessGroup(deps?.platform ?? process.platform);
95018
95020
  const processes = /* @__PURE__ */ new Map();
95021
+ const pendingGroupReaps = /* @__PURE__ */ new Map();
95019
95022
  const runnerLocks = /* @__PURE__ */ new Map();
95020
95023
  function withRunnerLock(runnerId, fn) {
95021
95024
  const result = (runnerLocks.get(runnerId) ?? Promise.resolve()).then(fn, fn);
@@ -95055,6 +95058,49 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95055
95058
  }, data));
95056
95059
  }
95057
95060
  /**
95061
+ * One line per reap that found something, or that failed. An empty group is the
95062
+ * normal case after a clean stop and stays quiet at debug. The kernel cannot say
95063
+ * which device a child served — the runner id is the finest attribution it has.
95064
+ */
95065
+ function logGroupReap(runnerId, result) {
95066
+ const found = result.membersAtExit;
95067
+ const killed = result.killed ?? [];
95068
+ const line = `[${runnerId}] runner group reaped {pgid=${result.pgid}, members=${found === null ? "unreadable" : found.length}${found !== null && found.length > 0 ? ` (${require_child_cap_dispatch.summarizeMembers(found)})` : ""}, sigkilled=${killed.length}${killed.length > 0 ? ` (${require_child_cap_dispatch.summarizeMembers(killed)})` : ""}${result.error !== null ? `, error=${result.error}` : ""}}`;
95069
+ const logger = capturedBroker?.logger;
95070
+ if (result.error !== null || killed.length > 0) if (logger) logger.warn(line);
95071
+ else console.error(line);
95072
+ else if ((found?.length ?? 0) > 0 || found === null) logger?.info(line);
95073
+ else logger?.debug(line);
95074
+ }
95075
+ /**
95076
+ * Reap a runner's process group (D668): SIGTERM, a bounded grace, SIGKILL for what
95077
+ * remains. Memoized on the entry so the `exit` handler and a stop/restart that timed
95078
+ * out waiting for that exit share one reap, and registered in `pendingGroupReaps` so
95079
+ * the next spawn under the same id waits for it. Never rejects.
95080
+ */
95081
+ function reapRunnerGroup(entry) {
95082
+ if (entry.groupReap !== null) return entry.groupReap;
95083
+ if (!entry.ownsGroup) {
95084
+ entry.groupReap = Promise.resolve();
95085
+ return entry.groupReap;
95086
+ }
95087
+ const reap = require_child_cap_dispatch.reapProcessGroup(entry.pid, groupOps).then((result) => logGroupReap(entry.name, result)).catch((err) => {
95088
+ capturedBroker?.logger.error(`[${entry.name}] runner group reap threw: ${err instanceof Error ? err.message : String(err)}`);
95089
+ }).finally(() => {
95090
+ require_child_cap_dispatch.releaseRunnerGroup(entry.pid);
95091
+ if (pendingGroupReaps.get(entry.name) === tracked) pendingGroupReaps.delete(entry.name);
95092
+ });
95093
+ const previous = pendingGroupReaps.get(entry.name);
95094
+ const tracked = previous ? Promise.all([previous, reap]).then(() => void 0) : reap;
95095
+ pendingGroupReaps.set(entry.name, tracked);
95096
+ entry.groupReap = reap;
95097
+ return reap;
95098
+ }
95099
+ /** Resolves once every reap started for `runnerId` has finished. */
95100
+ async function awaitPredecessorReap(runnerId) {
95101
+ await pendingGroupReaps.get(runnerId);
95102
+ }
95103
+ /**
95058
95104
  * Spawn ONE runner subprocess (`addon-runner.js`) that hosts the
95059
95105
  * `addons` set sharing a single Moleculer broker. The normal case is
95060
95106
  * a size-1 `addons` array — one addon, one process; the media group
@@ -95103,8 +95149,9 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95103
95149
  "pipe",
95104
95150
  "pipe"
95105
95151
  ],
95106
- detached: false
95152
+ detached: runnersOwnGroups
95107
95153
  });
95154
+ const ownsGroup = runnersOwnGroups && typeof child.pid === "number" && child.pid > 1;
95108
95155
  const entry = {
95109
95156
  pid: child.pid,
95110
95157
  nodeId,
@@ -95115,16 +95162,23 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95115
95162
  startedAt: Date.now(),
95116
95163
  restartCount: 0,
95117
95164
  state: "starting",
95118
- child
95165
+ child,
95166
+ ownsGroup,
95167
+ groupReap: null
95119
95168
  };
95169
+ if (ownsGroup) require_child_cap_dispatch.registerRunnerGroup(entry.pid, {
95170
+ runnerId,
95171
+ kill: groupOps.kill
95172
+ });
95120
95173
  child.stdout?.on("data", (data) => {
95121
- if (data.toString().includes(`READY:${nodeId}`)) entry.state = "running";
95174
+ if (data.toString().includes(`READY:${nodeId}`) && entry.state === "starting") entry.state = "running";
95122
95175
  });
95123
95176
  child.stderr?.on("data", (data) => {
95124
95177
  process.stderr.write(`[${runnerId}] ${data.toString()}`);
95125
95178
  });
95126
95179
  child.on("exit", (code, signal) => {
95127
95180
  console.error(`[process-service EXIT] runner=${runnerId} code=${String(code)} signal=${String(signal)} state=${entry.state}`);
95181
+ const groupReap = reapRunnerGroup(entry);
95128
95182
  if (entry.state === "stopped") return;
95129
95183
  entry.state = "crashed";
95130
95184
  capturedBroker?.logger.error(`[${runnerId}] Process exited (code=${code}, signal=${signal})`);
@@ -95163,6 +95217,7 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95163
95217
  if (capturedBroker) forceMoleculerDisconnect(capturedBroker, entry.nodeId);
95164
95218
  setTimeout(() => {
95165
95219
  withRunnerLock(runnerId, async () => {
95220
+ await groupReap;
95166
95221
  if (processes.has(runnerId)) return;
95167
95222
  const restarted = spawnRunner(runnerId, addons, env);
95168
95223
  restarted.restartCount = entry.restartCount + 1;
@@ -95175,10 +95230,12 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95175
95230
  return {
95176
95231
  name: "$process",
95177
95232
  actions: {
95178
- spawnRunner: { handler(ctx) {
95233
+ spawnRunner: { async handler(ctx) {
95179
95234
  const { runnerId, addons, env } = ctx.params;
95180
95235
  if (processes.has(runnerId)) throw new Error(`Runner "${runnerId}" already running`);
95181
95236
  if (!Array.isArray(addons) || addons.length === 0) throw new Error(`spawnRunner requires non-empty addons array`);
95237
+ await awaitPredecessorReap(runnerId);
95238
+ if (processes.has(runnerId)) throw new Error(`Runner "${runnerId}" already running`);
95182
95239
  invalidateHeapProfiles(addons);
95183
95240
  const entry = spawnRunner(runnerId, addons, env);
95184
95241
  return {
@@ -95215,6 +95272,7 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95215
95272
  });
95216
95273
  });
95217
95274
  }
95275
+ await reapRunnerGroup(target);
95218
95276
  processes.delete(name);
95219
95277
  crashSupervisor.reset(name);
95220
95278
  forceMoleculerDisconnect(ctx.broker, deadNodeId);
@@ -95251,6 +95309,7 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95251
95309
  });
95252
95310
  });
95253
95311
  }
95312
+ await reapRunnerGroup(target);
95254
95313
  processes.delete(name);
95255
95314
  crashSupervisor.reset(name);
95256
95315
  forceMoleculerDisconnect(ctx.broker, deadNodeId);
@@ -95310,6 +95369,7 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95310
95369
  });
95311
95370
  });
95312
95371
  }
95372
+ await reapRunnerGroup(entry);
95313
95373
  processes.delete(name);
95314
95374
  crashSupervisor.reset(name);
95315
95375
  invalidateHeapProfiles(runnerAddons);
@@ -95360,19 +95420,28 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95360
95420
  started() {
95361
95421
  capturedBroker = this.broker;
95362
95422
  },
95363
- stopped() {
95364
- const survivors = [];
95365
- for (const entry of processes.values()) {
95423
+ async stopped() {
95424
+ const entries = [...processes.values()];
95425
+ await Promise.all(entries.map(async (entry) => {
95366
95426
  entry.state = "stopped";
95367
- entry.child.kill("SIGTERM");
95368
- survivors.push(entry);
95369
- }
95370
- setTimeout(() => {
95371
- for (const entry of survivors) if (entry.child.exitCode === null && entry.child.signalCode === null) {
95372
- capturedBroker?.logger.warn(`[${entry.name}] Did not exit on SIGTERM — sending SIGKILL`);
95373
- entry.child.kill("SIGKILL");
95427
+ if (!(entry.child.exitCode !== null || entry.child.signalCode !== null)) {
95428
+ entry.child.kill("SIGTERM");
95429
+ await new Promise((resolve) => {
95430
+ const timer = setTimeout(() => {
95431
+ if (entry.child.exitCode === null && entry.child.signalCode === null) {
95432
+ capturedBroker?.logger.warn(`[${entry.name}] Did not exit on SIGTERM — sending SIGKILL`);
95433
+ entry.child.kill("SIGKILL");
95434
+ }
95435
+ resolve();
95436
+ }, 3e3);
95437
+ entry.child.once("exit", () => {
95438
+ clearTimeout(timer);
95439
+ resolve();
95440
+ });
95441
+ });
95374
95442
  }
95375
- }, 3e3).unref();
95443
+ await reapRunnerGroup(entry);
95444
+ }));
95376
95445
  processes.clear();
95377
95446
  capturedBroker = null;
95378
95447
  }
@@ -96259,6 +96328,7 @@ exports.setHubConnected = require_child_cap_dispatch.setHubConnected;
96259
96328
  exports.setNodeEventInterest = require_child_cap_dispatch.setNodeEventInterest;
96260
96329
  exports.setWrapperActive = require_builtins_device_manager_device_manager_addon.setWrapperActive;
96261
96330
  exports.shouldReclaim = require_child_cap_dispatch.shouldReclaim;
96331
+ exports.signalAllRunnerGroups = require_child_cap_dispatch.signalAllRunnerGroups;
96262
96332
  exports.socketDirectionBytes = require_child_cap_dispatch.socketDirectionBytes;
96263
96333
  exports.socketDirectionMessages = require_child_cap_dispatch.socketDirectionMessages;
96264
96334
  exports.startEventLoopStallMonitor = require_event_loop_stall_monitor.startEventLoopStallMonitor;
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM$1, r as __exportAll, t as __commonJSMin$1 } from "./chunk-CNf5ZN-e.mjs";
2
- import { $n as parseJsonUnknown, $t as objectInputDeclaresRequiredDeviceId, At as isArrayOutputSchema, Bt as lifecycleJobSchema, D as RUNTIME_DEFAULTS, Gn as emitDownForOwnedCaps, Gt as looseSchema, Hn as asNumber, It as isVoidInput, Lt as kebabToCamel, Mn as DATAPLANE_SECRET_HEADER$1, Nn as DEVICE_SETTINGS_CONTRIBUTION_METHODS, Nt as isObjectInput, Pn as DEVICE_STATUS_METHOD, Qn as parseJsonObject, Qt as objectInputDeclaresAddonId, Rn as ReadinessRegistry, S as METHOD_ACCESS_MAP, Tt as extractNestedAddonId, Un as asString$1, Vn as asJsonObject$1, Wn as createEvent, Wt as logLevelAtMost, cn as resolveMethodAuth, dn as scopesAllowDeviceCap, er as readinessKey, ir as EventCategory$1, jt as isCollectionArrayMethod, kn as errMsg$1, nr as scopeKey, qn as expandCapMethods, rn as procedureAuthKey, t as ALL_CAPABILITY_DEFINITIONS, tr as resolveCapMount, un as scopesAllowAddon, y as LOG_LEVEL_RANK, z as addonSettingsCapability, zn as ReadinessTimeoutError } from "./dist-B78pkrps.mjs";
2
+ import { $n as parseJsonUnknown, $t as objectInputDeclaresRequiredDeviceId, At as isArrayOutputSchema, Bt as lifecycleJobSchema, D as RUNTIME_DEFAULTS, Gn as emitDownForOwnedCaps, Gt as looseSchema, Hn as asNumber, It as isVoidInput, Lt as kebabToCamel, Mn as DATAPLANE_SECRET_HEADER$1, Nn as DEVICE_SETTINGS_CONTRIBUTION_METHODS, Nt as isObjectInput, Pn as DEVICE_STATUS_METHOD, Qn as parseJsonObject, Qt as objectInputDeclaresAddonId, Rn as ReadinessRegistry, S as METHOD_ACCESS_MAP, Tt as extractNestedAddonId, Un as asString$1, Vn as asJsonObject$1, Wn as createEvent, Wt as logLevelAtMost, cn as resolveMethodAuth, dn as scopesAllowDeviceCap, er as readinessKey, ir as EventCategory$1, jt as isCollectionArrayMethod, kn as errMsg$1, nr as scopeKey, qn as expandCapMethods, rn as procedureAuthKey, t as ALL_CAPABILITY_DEFINITIONS, tr as resolveCapMount, un as scopesAllowAddon, y as LOG_LEVEL_RANK, z as addonSettingsCapability, zn as ReadinessTimeoutError } from "./dist-Cj7pJmXq.mjs";
3
3
  import { AlertCenterAddon } from "./builtins/alerts/alerts.addon.mjs";
4
4
  import "./builtins/alerts/index.mjs";
5
5
  import { ComposerAddon } from "./builtins/composer/composer.addon.mjs";
@@ -16,7 +16,7 @@ import "./builtins/liveness-monitor/index.mjs";
16
16
  import { LocalAuthAddon, a as require_safe_buffer, i as require_ms, n as ScopedTokenManager, o as ApiKeyManager, r as AuthManager, t as UserManager } from "./builtins/local-auth/local-auth.addon.mjs";
17
17
  import "./builtins/local-auth/index.mjs";
18
18
  import { LokiDestination, LokiLoggingAddon } from "./builtins/loki-logging/index.mjs";
19
- import NativeMetricsAddon, { a as NativeMetricsProvider } from "./builtins/native-metrics/native-metrics.addon.mjs";
19
+ import NativeMetricsAddon, { t as NativeMetricsProvider } from "./builtins/native-metrics/native-metrics.addon.mjs";
20
20
  import { FilesystemStorageAddon, t as FilesystemStorageProvider } from "./builtins/sqlite-storage/filesystem-storage.addon.mjs";
21
21
  import { SqliteSettingsAddon, o as SqliteSettingsBackend, s as prefixRange } from "./builtins/sqlite-storage/sqlite-settings.addon.mjs";
22
22
  import { ConfigStore, DeviceStore } from "./builtins/sqlite-storage/index.mjs";
@@ -28,8 +28,8 @@ import "./builtins/system-config/index.mjs";
28
28
  import { WinstonDestination, WinstonLoggingAddon } from "./builtins/winston-logging/index.mjs";
29
29
  import { _ as ensureModel, a as createFileDataPlaneHandler, c as parseRangeHeader, d as ModelDownloadService, g as downloadModel, h as downloadFile, i as startEventLoopStallMonitor, l as parseTokenizedUrl, m as deleteModelFromDisk, n as STALL_TICK_MS, o as contentTypeFor, p as collectModelFiles, r as attributeStall, s as createAuthenticatedFileServer, t as STALL_REPORT_MS, u as resolveFilePath, v as fetchJson, x as isModelDownloaded, y as getModelFilePath } from "./event-loop-stall-monitor-EXjzIxDz.mjs";
30
30
  import { C as registerLanHttpHandler, S as readLanHttpState, _ as DEFAULT_LAN_HTTP_PORT, a as writeTlsMode, b as bindPendingLanHttp, c as loadTlsCert, d as collectCertIdentity, f as CA_VALIDITY_DAYS, g as evaluateExistingCert, h as SERVER_AUTH_OID, i as writeExtraSans, l as reissueTlsLeaf, m as MAX_LEAF_VALIDITY_DAYS, n as readTlsAccessStatus, o as validateUploadedTls, p as LEAF_RENEWAL_WINDOW_DAYS, r as readTlsMode, s as ensureTlsCert, t as readExtraSans, u as CA_COMMON_NAME, v as allFamiliesListenHost, x as closeLanHttp, y as applyLanHttp } from "./tls-IM-v7JV4.mjs";
31
- import { $ as localEndpointPath, $t as shouldReclaim, A as createUdsEventBus, An as createSocketPlaneReader, At as createAddonDataPlaneFacility, B as LocalChildClient, Bn as CAP_ACTION_OVERFLOW_LABEL, Bt as RSS_BUDGET_RELEASE_RATIO, C as brokerTransportLink, Cn as MESH_QUEUE_TOP_N, Ct as installManifestNativeDeps, D as localProviderLink, Dn as SOCKET_PLANE_TOP_N, Dt as runNpm, E as ipcParentLink, En as EMPTY_SOCKET_PLANE_BASELINE, Et as resolveNpmInvocation, F as AGENT_CAP_FWD_SERVICE, Fn as createSocketDirectionCounters, Ft as HEAP_WATCH_WARN_RATIO, G as PARENT_ROUTED_PROVIDER_ADDON_ID, Gn as createCapActionCensus, Gt as createDeferredHeapWatchSink, H as UDS_NO_ROUTE_PREFIX, Hn as EMPTY_CAP_ACTION_READING, Ht as RUNNER_RSS_BUDGET_ENV, I as CapRouteResolver, In as recordSocketFrame, It as HUB_MAIN_HEAP_WATCH_LABEL, J as createLocalTransport, Jn as createEventPlaneMeter, Jt as describeRss, K as __resetCapUsageRegistryForTests, Kn as diffCapActionCensus, Kt as createLoopDelayMeter, L as callWithServiceDiscovery, Ln as sampleSocketDirection, Lt as HUB_MAIN_RSS_BUDGET_MB, M as createUdsLogger, Mn as formatSocketPlane, Mt as HEAP_RECLAIM_STEADY_STATE_INTERVAL_MS, N as createUdsLoggerWithControl, Nn as readSocketQueues, Nt as HEAP_RECLAIM_TRIGGER_MB, O as createParentUnownedCallHandler, On as SOCKET_QUEUE_TOP_N, Ot as createAddonPeerBytesFacility, P as AGENT_CAP_FWD_ACTION, Pn as EMPTY_SOCKET_DIRECTION, Pt as HEAP_WATCH_INTERVAL_MS, Q as PeerAnsweredError, Qt as reclaimIntervalMs, R as CapRouteError, Rn as socketDirectionBytes, Rt as HUB_RSS_BUDGET_ENV, S as brokerCallForCap, Sn as formatOutboundQueueVerdict, St as copyBundledNativeModules, T as deviceCapResolverLink, Tn as formatMeshQueue, Tt as buildNpmChildEnv, U as readMoleculerFanoutMode, Un as MAX_CAP_ACTIONS_PER_PEER, Ut as ZERO_LOOP_DELAY, V as LocalChildRegistry, Vn as CAP_ACTION_UNLABELLED, Vt as RUNNER_HEAP_WATCH_INTERVAL_MS, W as CapUsageRegistry, Wn as capActionLabel, Wt as buildHeapSample, X as UdsLocalTransportServer, Xn as diffEventPlane, Xt as nextRssBudgetState, Y as UdsLocalTransportClient, Yn as createEventPlaneReader, Yt as hubMainRssBudget, Z as SocketChannel, Zn as formatEventPlane, Zt as parseRssBudgetMb, _ as setNodeEventInterest, _n as selectReportedSpaces, _t as deserializeTypedArrays, a as adaptBrokerToCluster, an as ARRAY_BUFFER_CENSUS_THRESHOLD_MB, b as createKernelHwAccel, bn as OUTBOUND_QUEUE_THRESHOLD_BYTES, bt as CapabilityHandle, c as getWorkerDeviceRegistry, cn as createInspectorArrayBufferCensus, d as setHubConnected, dn as DECISIVE_HEAP_SPACES, dt as NATIVE_PROVIDER_SERVICE_INFIX, en as startHeapWatch, et as FrameDecoder, f as EVENT_TOPIC_PREFIX, fn as HEAP_SPACE_REPORT_MIN_MB, ft as capActionName, g as registerEventBusService, gn as readHeapSpaces, gt as parseCapAction, h as getMoleculerEventStats, hn as heapSpaceField, ht as capServiceName, i as installManifestPythonDeps, in as ARRAY_BUFFER_CENSUS_SUSTAIN_MS, it as createBrokerDeviceManagerApi, j as udsChildLogToWorkerEntry, jn as diffSocketPlane, jt as HEAP_RECLAIM_MIN_INTERVAL_MS, k as createUdsEventBridge, kn as createSocketPlaneMeter, kt as hostNodeIdOf, l as getOrInitReadinessRegistry, ln as formatArrayBufferCensus, lt as createAddonService, m as getBrokerEventBus, mn as formatHeapSpaces, mt as capBareAction, nn as strandedMb, nt as buildNativeCapProxy, o as createAddonContext, on as ARRAY_BUFFER_RELEASE_MIN_DROP_RATIO, p as clusterEventTopic, pn as emitHeapDiagnosticReport, pt as capActionSuffix, q as getCapUsageRegistry, qn as EVENT_PLANE_TOP_N, qt as createV8Reclaimer, r as installManifestSystemDeps, rn as ARRAY_BUFFER_CENSUS_COOLDOWN_MS, rt as buildUdsNativeCapProxy, s as createUdsAddonContext, sn as createArrayBufferCensusGate, st as mountNativeCapService, t as CAP_METHOD_NOT_IMPLEMENTED, tn as startRunnerHeapWatch, tt as encodeFrame, u as getOrInitReadinessRegistryForClient, un as isArrayBufferCensusReport, ut as validateProviderRegistrations, v as subscribePassthrough, vn as OUTBOUND_QUEUE_COOLDOWN_MS, vt as serializeTypedArrays, w as buildLinkChain, wn as createMeshQueueReader, wt as resolveAddonClass, x as resolveHwAccel, xn as createOutboundQueueGate, xt as CapabilityUnavailableError, y as AddonDepsManager, yn as OUTBOUND_QUEUE_SUSTAIN_MS, yt as DeviceRegistry, z as classifyCapRoute, zn as socketDirectionMessages, zt as RSS_BUDGET_REANNOUNCE_MIN_MS } from "./child-cap-dispatch-BCJjUtXV.mjs";
32
- import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-Vp0bPV3U.mjs";
31
+ import { $ as CapUsageRegistry, $n as capActionLabel, $t as buildHeapSample, A as resolveHwAccel, An as createOutboundQueueGate, At as CapabilityUnavailableError, B as udsChildLogToWorkerEntry, Bn as diffSocketPlane, Bt as HEAP_RECLAIM_MIN_INTERVAL_MS, C as getBrokerEventBus, Cn as formatHeapSpaces, Ct as capBareAction, D as subscribePassthrough, Dn as OUTBOUND_QUEUE_COOLDOWN_MS, Dt as serializeTypedArrays, E as setNodeEventInterest, En as selectReportedSpaces, Et as deserializeTypedArrays, F as ipcParentLink, Fn as EMPTY_SOCKET_PLANE_BASELINE, Ft as resolveNpmInvocation, G as CapRouteResolver, Gn as recordSocketFrame, Gt as HUB_MAIN_HEAP_WATCH_LABEL, H as createUdsLoggerWithControl, Hn as readSocketQueues, Ht as HEAP_RECLAIM_TRIGGER_MB, I as localProviderLink, In as SOCKET_PLANE_TOP_N, It as runNpm, J as classifyCapRoute, Jn as socketDirectionMessages, Jt as RSS_BUDGET_REANNOUNCE_MIN_MS, K as callWithServiceDiscovery, Kn as sampleSocketDirection, Kt as HUB_MAIN_RSS_BUDGET_MB, L as createParentUnownedCallHandler, Ln as SOCKET_QUEUE_TOP_N, Lt as createAddonPeerBytesFacility, M as brokerTransportLink, Mn as MESH_QUEUE_TOP_N, Mt as installManifestNativeDeps, N as buildLinkChain, Nn as createMeshQueueReader, Nt as resolveAddonClass, O as AddonDepsManager, On as OUTBOUND_QUEUE_SUSTAIN_MS, Ot as DeviceRegistry, P as deviceCapResolverLink, Pn as formatMeshQueue, Pt as buildNpmChildEnv, Q as readMoleculerFanoutMode, Qn as MAX_CAP_ACTIONS_PER_PEER, Qt as ZERO_LOOP_DELAY, R as createUdsEventBridge, Rn as createSocketPlaneMeter, Rt as hostNodeIdOf, S as clusterEventTopic, Sn as emitHeapDiagnosticReport, St as capActionSuffix, T as registerEventBusService, Tn as readHeapSpaces, Tt as parseCapAction, U as AGENT_CAP_FWD_ACTION, Un as EMPTY_SOCKET_DIRECTION, Ut as HEAP_WATCH_INTERVAL_MS, V as createUdsLogger, Vn as formatSocketPlane, Vt as HEAP_RECLAIM_STEADY_STATE_INTERVAL_MS, W as AGENT_CAP_FWD_SERVICE, Wn as createSocketDirectionCounters, Wt as HEAP_WATCH_WARN_RATIO, X as LocalChildRegistry, Xn as CAP_ACTION_UNLABELLED, Xt as RUNNER_HEAP_WATCH_INTERVAL_MS, Y as LocalChildClient, Yn as CAP_ACTION_OVERFLOW_LABEL, Yt as RSS_BUDGET_RELEASE_RATIO, Z as UDS_NO_ROUTE_PREFIX, Zn as EMPTY_CAP_ACTION_READING, Zt as RUNNER_RSS_BUDGET_ENV, _ as getWorkerDeviceRegistry, _n as createInspectorArrayBufferCensus, a as reapProcessGroup, an as nextRssBudgetState, ar as diffEventPlane, at as UdsLocalTransportServer, b as setHubConnected, bn as DECISIVE_HEAP_SPACES, bt as NATIVE_PROVIDER_SERVICE_INFIX, c as runnerOwnsProcessGroup, cn as shouldReclaim, ct as localEndpointPath, d as summarizeMembers, dn as strandedMb, dt as buildNativeCapProxy, en as createDeferredHeapWatchSink, er as createCapActionCensus, et as PARENT_ROUTED_PROVIDER_ADDON_ID, f as installManifestSystemDeps, fn as ARRAY_BUFFER_CENSUS_COOLDOWN_MS, ft as buildUdsNativeCapProxy, g as createUdsAddonContext, gn as createArrayBufferCensusGate, gt as mountNativeCapService, h as createAddonContext, hn as ARRAY_BUFFER_RELEASE_MIN_DROP_RATIO, in as hubMainRssBudget, ir as createEventPlaneReader, it as UdsLocalTransportClient, j as brokerCallForCap, jn as formatOutboundQueueVerdict, jt as copyBundledNativeModules, k as createKernelHwAccel, kn as OUTBOUND_QUEUE_THRESHOLD_BYTES, kt as CapabilityHandle, l as signalAllRunnerGroups, ln as startHeapWatch, lt as FrameDecoder, m as adaptBrokerToCluster, mn as ARRAY_BUFFER_CENSUS_THRESHOLD_MB, nn as createV8Reclaimer, nr as EVENT_PLANE_TOP_N, nt as getCapUsageRegistry, o as registerRunnerGroup, on as parseRssBudgetMb, or as formatEventPlane, ot as SocketChannel, p as installManifestPythonDeps, pn as ARRAY_BUFFER_CENSUS_SUSTAIN_MS, pt as createBrokerDeviceManagerApi, q as CapRouteError, qn as socketDirectionBytes, qt as HUB_RSS_BUDGET_ENV, r as defaultProcessGroupOps, rn as describeRss, rr as createEventPlaneMeter, rt as createLocalTransport, s as releaseRunnerGroup, sn as reclaimIntervalMs, st as PeerAnsweredError, t as CAP_METHOD_NOT_IMPLEMENTED, tn as createLoopDelayMeter, tr as diffCapActionCensus, tt as __resetCapUsageRegistryForTests, un as startRunnerHeapWatch, ut as encodeFrame, v as getOrInitReadinessRegistry, vn as formatArrayBufferCensus, vt as createAddonService, w as getMoleculerEventStats, wn as heapSpaceField, wt as capServiceName, x as EVENT_TOPIC_PREFIX, xn as HEAP_SPACE_REPORT_MIN_MB, xt as capActionName, y as getOrInitReadinessRegistryForClient, yn as isArrayBufferCensusReport, yt as validateProviderRegistrations, z as createUdsEventBus, zn as createSocketPlaneReader, zt as createAddonDataPlaneFacility } from "./child-cap-dispatch-CHTiHbsv.mjs";
32
+ import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-B1v6E07R.mjs";
33
33
  import { t as CustomActionRegistry } from "./custom-action-registry-B4Cf4O_X.mjs";
34
34
  import { z } from "zod";
35
35
  import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
@@ -95008,7 +95008,10 @@ var CRASH_WINDOW_MS = 6e4;
95008
95008
  var MAX_CRASHES_IN_WINDOW = 5;
95009
95009
  function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parentUdsPath) {
95010
95010
  const spawnFn = deps?.spawnFn ?? spawn;
95011
+ const groupOps = deps?.processGroups ?? defaultProcessGroupOps;
95012
+ const runnersOwnGroups = runnerOwnsProcessGroup(deps?.platform ?? process.platform);
95011
95013
  const processes = /* @__PURE__ */ new Map();
95014
+ const pendingGroupReaps = /* @__PURE__ */ new Map();
95012
95015
  const runnerLocks = /* @__PURE__ */ new Map();
95013
95016
  function withRunnerLock(runnerId, fn) {
95014
95017
  const result = (runnerLocks.get(runnerId) ?? Promise.resolve()).then(fn, fn);
@@ -95048,6 +95051,49 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95048
95051
  }, data));
95049
95052
  }
95050
95053
  /**
95054
+ * One line per reap that found something, or that failed. An empty group is the
95055
+ * normal case after a clean stop and stays quiet at debug. The kernel cannot say
95056
+ * which device a child served — the runner id is the finest attribution it has.
95057
+ */
95058
+ function logGroupReap(runnerId, result) {
95059
+ const found = result.membersAtExit;
95060
+ const killed = result.killed ?? [];
95061
+ const line = `[${runnerId}] runner group reaped {pgid=${result.pgid}, members=${found === null ? "unreadable" : found.length}${found !== null && found.length > 0 ? ` (${summarizeMembers(found)})` : ""}, sigkilled=${killed.length}${killed.length > 0 ? ` (${summarizeMembers(killed)})` : ""}${result.error !== null ? `, error=${result.error}` : ""}}`;
95062
+ const logger = capturedBroker?.logger;
95063
+ if (result.error !== null || killed.length > 0) if (logger) logger.warn(line);
95064
+ else console.error(line);
95065
+ else if ((found?.length ?? 0) > 0 || found === null) logger?.info(line);
95066
+ else logger?.debug(line);
95067
+ }
95068
+ /**
95069
+ * Reap a runner's process group (D668): SIGTERM, a bounded grace, SIGKILL for what
95070
+ * remains. Memoized on the entry so the `exit` handler and a stop/restart that timed
95071
+ * out waiting for that exit share one reap, and registered in `pendingGroupReaps` so
95072
+ * the next spawn under the same id waits for it. Never rejects.
95073
+ */
95074
+ function reapRunnerGroup(entry) {
95075
+ if (entry.groupReap !== null) return entry.groupReap;
95076
+ if (!entry.ownsGroup) {
95077
+ entry.groupReap = Promise.resolve();
95078
+ return entry.groupReap;
95079
+ }
95080
+ const reap = reapProcessGroup(entry.pid, groupOps).then((result) => logGroupReap(entry.name, result)).catch((err) => {
95081
+ capturedBroker?.logger.error(`[${entry.name}] runner group reap threw: ${err instanceof Error ? err.message : String(err)}`);
95082
+ }).finally(() => {
95083
+ releaseRunnerGroup(entry.pid);
95084
+ if (pendingGroupReaps.get(entry.name) === tracked) pendingGroupReaps.delete(entry.name);
95085
+ });
95086
+ const previous = pendingGroupReaps.get(entry.name);
95087
+ const tracked = previous ? Promise.all([previous, reap]).then(() => void 0) : reap;
95088
+ pendingGroupReaps.set(entry.name, tracked);
95089
+ entry.groupReap = reap;
95090
+ return reap;
95091
+ }
95092
+ /** Resolves once every reap started for `runnerId` has finished. */
95093
+ async function awaitPredecessorReap(runnerId) {
95094
+ await pendingGroupReaps.get(runnerId);
95095
+ }
95096
+ /**
95051
95097
  * Spawn ONE runner subprocess (`addon-runner.js`) that hosts the
95052
95098
  * `addons` set sharing a single Moleculer broker. The normal case is
95053
95099
  * a size-1 `addons` array — one addon, one process; the media group
@@ -95096,8 +95142,9 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95096
95142
  "pipe",
95097
95143
  "pipe"
95098
95144
  ],
95099
- detached: false
95145
+ detached: runnersOwnGroups
95100
95146
  });
95147
+ const ownsGroup = runnersOwnGroups && typeof child.pid === "number" && child.pid > 1;
95101
95148
  const entry = {
95102
95149
  pid: child.pid,
95103
95150
  nodeId,
@@ -95108,16 +95155,23 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95108
95155
  startedAt: Date.now(),
95109
95156
  restartCount: 0,
95110
95157
  state: "starting",
95111
- child
95158
+ child,
95159
+ ownsGroup,
95160
+ groupReap: null
95112
95161
  };
95162
+ if (ownsGroup) registerRunnerGroup(entry.pid, {
95163
+ runnerId,
95164
+ kill: groupOps.kill
95165
+ });
95113
95166
  child.stdout?.on("data", (data) => {
95114
- if (data.toString().includes(`READY:${nodeId}`)) entry.state = "running";
95167
+ if (data.toString().includes(`READY:${nodeId}`) && entry.state === "starting") entry.state = "running";
95115
95168
  });
95116
95169
  child.stderr?.on("data", (data) => {
95117
95170
  process.stderr.write(`[${runnerId}] ${data.toString()}`);
95118
95171
  });
95119
95172
  child.on("exit", (code, signal) => {
95120
95173
  console.error(`[process-service EXIT] runner=${runnerId} code=${String(code)} signal=${String(signal)} state=${entry.state}`);
95174
+ const groupReap = reapRunnerGroup(entry);
95121
95175
  if (entry.state === "stopped") return;
95122
95176
  entry.state = "crashed";
95123
95177
  capturedBroker?.logger.error(`[${runnerId}] Process exited (code=${code}, signal=${signal})`);
@@ -95156,6 +95210,7 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95156
95210
  if (capturedBroker) forceMoleculerDisconnect(capturedBroker, entry.nodeId);
95157
95211
  setTimeout(() => {
95158
95212
  withRunnerLock(runnerId, async () => {
95213
+ await groupReap;
95159
95214
  if (processes.has(runnerId)) return;
95160
95215
  const restarted = spawnRunner(runnerId, addons, env);
95161
95216
  restarted.restartCount = entry.restartCount + 1;
@@ -95168,10 +95223,12 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95168
95223
  return {
95169
95224
  name: "$process",
95170
95225
  actions: {
95171
- spawnRunner: { handler(ctx) {
95226
+ spawnRunner: { async handler(ctx) {
95172
95227
  const { runnerId, addons, env } = ctx.params;
95173
95228
  if (processes.has(runnerId)) throw new Error(`Runner "${runnerId}" already running`);
95174
95229
  if (!Array.isArray(addons) || addons.length === 0) throw new Error(`spawnRunner requires non-empty addons array`);
95230
+ await awaitPredecessorReap(runnerId);
95231
+ if (processes.has(runnerId)) throw new Error(`Runner "${runnerId}" already running`);
95175
95232
  invalidateHeapProfiles(addons);
95176
95233
  const entry = spawnRunner(runnerId, addons, env);
95177
95234
  return {
@@ -95208,6 +95265,7 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95208
95265
  });
95209
95266
  });
95210
95267
  }
95268
+ await reapRunnerGroup(target);
95211
95269
  processes.delete(name);
95212
95270
  crashSupervisor.reset(name);
95213
95271
  forceMoleculerDisconnect(ctx.broker, deadNodeId);
@@ -95244,6 +95302,7 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95244
95302
  });
95245
95303
  });
95246
95304
  }
95305
+ await reapRunnerGroup(target);
95247
95306
  processes.delete(name);
95248
95307
  crashSupervisor.reset(name);
95249
95308
  forceMoleculerDisconnect(ctx.broker, deadNodeId);
@@ -95303,6 +95362,7 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95303
95362
  });
95304
95363
  });
95305
95364
  }
95365
+ await reapRunnerGroup(entry);
95306
95366
  processes.delete(name);
95307
95367
  crashSupervisor.reset(name);
95308
95368
  invalidateHeapProfiles(runnerAddons);
@@ -95353,19 +95413,28 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
95353
95413
  started() {
95354
95414
  capturedBroker = this.broker;
95355
95415
  },
95356
- stopped() {
95357
- const survivors = [];
95358
- for (const entry of processes.values()) {
95416
+ async stopped() {
95417
+ const entries = [...processes.values()];
95418
+ await Promise.all(entries.map(async (entry) => {
95359
95419
  entry.state = "stopped";
95360
- entry.child.kill("SIGTERM");
95361
- survivors.push(entry);
95362
- }
95363
- setTimeout(() => {
95364
- for (const entry of survivors) if (entry.child.exitCode === null && entry.child.signalCode === null) {
95365
- capturedBroker?.logger.warn(`[${entry.name}] Did not exit on SIGTERM — sending SIGKILL`);
95366
- entry.child.kill("SIGKILL");
95420
+ if (!(entry.child.exitCode !== null || entry.child.signalCode !== null)) {
95421
+ entry.child.kill("SIGTERM");
95422
+ await new Promise((resolve) => {
95423
+ const timer = setTimeout(() => {
95424
+ if (entry.child.exitCode === null && entry.child.signalCode === null) {
95425
+ capturedBroker?.logger.warn(`[${entry.name}] Did not exit on SIGTERM — sending SIGKILL`);
95426
+ entry.child.kill("SIGKILL");
95427
+ }
95428
+ resolve();
95429
+ }, 3e3);
95430
+ entry.child.once("exit", () => {
95431
+ clearTimeout(timer);
95432
+ resolve();
95433
+ });
95434
+ });
95367
95435
  }
95368
- }, 3e3).unref();
95436
+ await reapRunnerGroup(entry);
95437
+ }));
95369
95438
  processes.clear();
95370
95439
  capturedBroker = null;
95371
95440
  }
@@ -95849,4 +95918,4 @@ var LifecycleJobEngine = class {
95849
95918
  }
95850
95919
  };
95851
95920
  //#endregion
95852
- export { AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AGENT_READINESS_SERVICE_NAME, ALL_CAPABILITY_DEFINITIONS, ARRAY_BUFFER_CENSUS_COOLDOWN_MS, ARRAY_BUFFER_CENSUS_SUSTAIN_MS, ARRAY_BUFFER_CENSUS_THRESHOLD_MB, ARRAY_BUFFER_RELEASE_MIN_DROP_RATIO, AddonApiFactory, AddonDepsManager, AddonEngineManager, AddonHealthMonitor, AddonInstaller, AddonLoader, AddonManifest, AddonRouteRegistry, AlertCenterAddon, ApiKeyManager, AuthManager, CAP_ACTION_OVERFLOW_LABEL, CAP_ACTION_UNLABELLED, CAP_METHOD_NOT_IMPLEMENTED, CA_COMMON_NAME, CA_VALIDITY_DAYS, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REJECTED_EXIT_CODE, CORE_CAP_SERVICE_NAME, CapRouteError, CapRouteResolver, CapUsageRegistry, CapabilityHandle, CapabilityRegistry, CapabilityUnavailableError, ComposerAddon, ConfigManager, ConfigStore, ConsoleDestination, ConsoleLoggingAddon, CoreBlocksAddon, CustomActionRegistry, DECISIVE_HEAP_SPACES, DEFAULT_DATA_PATH, DEFAULT_LAN_HTTP_PORT, DEFAULT_LOG_LEVEL, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATUS_METHOD, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EMPTY_CAP_ACTION_READING, EMPTY_SOCKET_DIRECTION, EMPTY_SOCKET_PLANE_BASELINE, EVENT_PLANE_TOP_N, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HEAP_RECLAIM_MIN_INTERVAL_MS, HEAP_RECLAIM_STEADY_STATE_INTERVAL_MS, HEAP_RECLAIM_TRIGGER_MB, HEAP_SPACE_REPORT_MIN_MB, HEAP_WATCH_INTERVAL_MS, HEAP_WATCH_WARN_RATIO, HUB_CAP_FWD_ACTION, HUB_CAP_FWD_SERVICE, HUB_MAIN_HEAP_WATCH_LABEL, HUB_MAIN_RSS_BUDGET_MB, HUB_RSS_BUDGET_ENV, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JobJournal, LEAF_RENEWAL_WINDOW_DAYS, LifecycleJobEngine, LifecycleStateMachine, LivenessMonitorAddon, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, LoggingGate, LokiDestination, LokiLoggingAddon, MAX_CAP_ACTIONS_PER_PEER, MAX_LEAF_VALIDITY_DAYS, MESH_QUEUE_TOP_N, METHOD_ACCESS_MAP, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NATIVE_SCAN_DEPTH, NativeMetricsAddon, NativeMetricsProvider, NetworkQualityTracker, NotificationService, OUTBOUND_QUEUE_COOLDOWN_MS, OUTBOUND_QUEUE_SUSTAIN_MS, OUTBOUND_QUEUE_THRESHOLD_BYTES, PARENT_ROUTED_PROVIDER_ADDON_ID, PYTHON_VERSION, PeerAnsweredError, PipelineRunner, PipelineValidator, PythonEnvManager, QUARANTINE_DIRNAME, RESTART_MARKER_FILE, RSS_BUDGET_REANNOUNCE_MIN_MS, RSS_BUDGET_RELEASE_RATIO, RUNNER_HEAP_WATCH_INTERVAL_MS, RUNNER_RSS_BUDGET_ENV, RUNTIME_DEFAULTS, ReadinessRegistry, ReadinessTimeoutError, ReplEngine, RingBuffer, SERVER_AUTH_OID, SOCKET_PLANE_TOP_N, SOCKET_QUEUE_TOP_N, STALL_REPORT_MS, STALL_TICK_MS, ScopedLogger, ScopedTokenManager, SocketChannel, SqliteSettingsAddon, SqliteSettingsBackend, StagingArea, StorageLocationManager, StorageManager, StorageOrchestratorAddon, StorageOrchestratorService, SystemConfigAddon, SystemEventBus, TRADITIONAL_NATIVE_PACKAGES, ToastService, UDS_NO_ROUTE_PREFIX, UdsLocalTransportClient, UdsLocalTransportServer, UserManager, WinstonDestination, WinstonLoggingAddon, ZERO_LOOP_DELAY, __resetCapUsageRegistryForTests, __resetLoggingGateForTests, adaptBrokerToCluster, addonSettingsCapability, allFamiliesListenHost, applyLanHttp, attributeStall, bindPendingLanHttp, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildHeapSample, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildNpmChildEnv, buildStorageLocationRegistry, buildUdsNativeCapProxy, builderMountedCapNames, callRegisterNodeWithRetry, callWithServiceDiscovery, canServeDataPlane, capActionLabel, capActionName, capActionSuffix, capBareAction, capServiceName, classifyAddonDir, classifyCapRoute, clearPendingRestart, closeLanHttp, clusterEventTopic, clusterSecretMatches, collectCertIdentity, collectModelFiles, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonDataPlaneFacility, createAddonPeerBytesFacility, createAddonService, createArrayBufferCensusGate, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCapActionCensus, createCoreCapService, createDeferredHeapWatchSink, createDoorSettingsView, createEventPlaneMeter, createEventPlaneReader, createFileDataPlaneHandler, createHubCapForwardService, createHubService, createInspectorArrayBufferCensus, createKernelHwAccel, createLocalTransport, createLoopDelayMeter, createMeshQueueReader, createOutboundQueueGate, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createSocketDirectionCounters, createSocketPlaneMeter, createSocketPlaneReader, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, createV8Reclaimer, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, describeRss, detectWorkspacePackagesDir, deviceCapResolverLink, diffCapActionCensus, diffEventPlane, diffSocketPlane, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, emitHeapDiagnosticReport, encodeFrame, ensureAddonNativePrebuilds, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensureNativePrebuilds, ensurePython, ensureTlsCert, evaluateExistingCert, expandCapMethods, fetchJson, findInPath, formatArrayBufferCensus, formatEventPlane, formatHeapSpaces, formatLogLine, formatMeshQueue, formatOutboundQueueVerdict, formatSocketPlane, getBrokerEventBus, getCapUsageRegistry, getFfmpegDownloadUrl, getLoggingGate, getModelFilePath, getMoleculerEventStats, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getWorkerDeviceRegistry, hasDotNode, hashClusterSecret, heapSpaceField, hostNodeIdOf, hubMainRssBudget, installManifestNativeDeps, installManifestPythonDeps, installManifestSystemDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcParentLink, isAddonDeploySource, isArrayBufferCensusReport, isArrayOutputSchema, isClusterSecretMismatchError, isCollectionArrayMethod, isInfraCapability, isModelDownloaded, isSourceNewer, isolatedBuiltinPhase, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, nextRssBudgetState, parseCapAction, parseRangeHeader, parseRssBudgetMb, parseTokenizedUrl, partitionIsolatedBuiltinIds, proxyToUpstream, quarantineAddonResidue, readExtraSans, readHeapSpaces, readLanHttpState, readMoleculerFanoutMode, readPendingRestart, readSocketQueues, readTlsAccessStatus, readTlsMode, readinessKey, reclaimIntervalMs, recordSocketFrame, registerEventBusService, registerLanHttpHandler, reissueTlsLeaf, resolveFilePath, resolveHwAccel, resolveNpmInvocation, runHubAddonBoot, runNpm, sampleSocketDirection, scheduleSelfRestart, scopeKey, scopesAllowAddon, scopesAllowDeviceCap, selectAddonResidue, selectReportedSpaces, serializeTypedArrays, setHubConnected, setNodeEventInterest, setWrapperActive, shouldReclaim, socketDirectionBytes, socketDirectionMessages, startEventLoopStallMonitor, startHeapWatch, startRunnerHeapWatch, strandedMb, stripCamstackDeps, subscribePassthrough, udsChildLogToWorkerEntry, validateProviderRegistrations, validateUploadedTls, waitUntilReady, writeExtraSans, writePendingRestart, writeTlsMode };
95921
+ export { AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AGENT_READINESS_SERVICE_NAME, ALL_CAPABILITY_DEFINITIONS, ARRAY_BUFFER_CENSUS_COOLDOWN_MS, ARRAY_BUFFER_CENSUS_SUSTAIN_MS, ARRAY_BUFFER_CENSUS_THRESHOLD_MB, ARRAY_BUFFER_RELEASE_MIN_DROP_RATIO, AddonApiFactory, AddonDepsManager, AddonEngineManager, AddonHealthMonitor, AddonInstaller, AddonLoader, AddonManifest, AddonRouteRegistry, AlertCenterAddon, ApiKeyManager, AuthManager, CAP_ACTION_OVERFLOW_LABEL, CAP_ACTION_UNLABELLED, CAP_METHOD_NOT_IMPLEMENTED, CA_COMMON_NAME, CA_VALIDITY_DAYS, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REJECTED_EXIT_CODE, CORE_CAP_SERVICE_NAME, CapRouteError, CapRouteResolver, CapUsageRegistry, CapabilityHandle, CapabilityRegistry, CapabilityUnavailableError, ComposerAddon, ConfigManager, ConfigStore, ConsoleDestination, ConsoleLoggingAddon, CoreBlocksAddon, CustomActionRegistry, DECISIVE_HEAP_SPACES, DEFAULT_DATA_PATH, DEFAULT_LAN_HTTP_PORT, DEFAULT_LOG_LEVEL, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATUS_METHOD, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EMPTY_CAP_ACTION_READING, EMPTY_SOCKET_DIRECTION, EMPTY_SOCKET_PLANE_BASELINE, EVENT_PLANE_TOP_N, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HEAP_RECLAIM_MIN_INTERVAL_MS, HEAP_RECLAIM_STEADY_STATE_INTERVAL_MS, HEAP_RECLAIM_TRIGGER_MB, HEAP_SPACE_REPORT_MIN_MB, HEAP_WATCH_INTERVAL_MS, HEAP_WATCH_WARN_RATIO, HUB_CAP_FWD_ACTION, HUB_CAP_FWD_SERVICE, HUB_MAIN_HEAP_WATCH_LABEL, HUB_MAIN_RSS_BUDGET_MB, HUB_RSS_BUDGET_ENV, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JobJournal, LEAF_RENEWAL_WINDOW_DAYS, LifecycleJobEngine, LifecycleStateMachine, LivenessMonitorAddon, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, LoggingGate, LokiDestination, LokiLoggingAddon, MAX_CAP_ACTIONS_PER_PEER, MAX_LEAF_VALIDITY_DAYS, MESH_QUEUE_TOP_N, METHOD_ACCESS_MAP, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NATIVE_SCAN_DEPTH, NativeMetricsAddon, NativeMetricsProvider, NetworkQualityTracker, NotificationService, OUTBOUND_QUEUE_COOLDOWN_MS, OUTBOUND_QUEUE_SUSTAIN_MS, OUTBOUND_QUEUE_THRESHOLD_BYTES, PARENT_ROUTED_PROVIDER_ADDON_ID, PYTHON_VERSION, PeerAnsweredError, PipelineRunner, PipelineValidator, PythonEnvManager, QUARANTINE_DIRNAME, RESTART_MARKER_FILE, RSS_BUDGET_REANNOUNCE_MIN_MS, RSS_BUDGET_RELEASE_RATIO, RUNNER_HEAP_WATCH_INTERVAL_MS, RUNNER_RSS_BUDGET_ENV, RUNTIME_DEFAULTS, ReadinessRegistry, ReadinessTimeoutError, ReplEngine, RingBuffer, SERVER_AUTH_OID, SOCKET_PLANE_TOP_N, SOCKET_QUEUE_TOP_N, STALL_REPORT_MS, STALL_TICK_MS, ScopedLogger, ScopedTokenManager, SocketChannel, SqliteSettingsAddon, SqliteSettingsBackend, StagingArea, StorageLocationManager, StorageManager, StorageOrchestratorAddon, StorageOrchestratorService, SystemConfigAddon, SystemEventBus, TRADITIONAL_NATIVE_PACKAGES, ToastService, UDS_NO_ROUTE_PREFIX, UdsLocalTransportClient, UdsLocalTransportServer, UserManager, WinstonDestination, WinstonLoggingAddon, ZERO_LOOP_DELAY, __resetCapUsageRegistryForTests, __resetLoggingGateForTests, adaptBrokerToCluster, addonSettingsCapability, allFamiliesListenHost, applyLanHttp, attributeStall, bindPendingLanHttp, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildHeapSample, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildNpmChildEnv, buildStorageLocationRegistry, buildUdsNativeCapProxy, builderMountedCapNames, callRegisterNodeWithRetry, callWithServiceDiscovery, canServeDataPlane, capActionLabel, capActionName, capActionSuffix, capBareAction, capServiceName, classifyAddonDir, classifyCapRoute, clearPendingRestart, closeLanHttp, clusterEventTopic, clusterSecretMatches, collectCertIdentity, collectModelFiles, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonDataPlaneFacility, createAddonPeerBytesFacility, createAddonService, createArrayBufferCensusGate, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCapActionCensus, createCoreCapService, createDeferredHeapWatchSink, createDoorSettingsView, createEventPlaneMeter, createEventPlaneReader, createFileDataPlaneHandler, createHubCapForwardService, createHubService, createInspectorArrayBufferCensus, createKernelHwAccel, createLocalTransport, createLoopDelayMeter, createMeshQueueReader, createOutboundQueueGate, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createSocketDirectionCounters, createSocketPlaneMeter, createSocketPlaneReader, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, createV8Reclaimer, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, describeRss, detectWorkspacePackagesDir, deviceCapResolverLink, diffCapActionCensus, diffEventPlane, diffSocketPlane, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, emitHeapDiagnosticReport, encodeFrame, ensureAddonNativePrebuilds, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensureNativePrebuilds, ensurePython, ensureTlsCert, evaluateExistingCert, expandCapMethods, fetchJson, findInPath, formatArrayBufferCensus, formatEventPlane, formatHeapSpaces, formatLogLine, formatMeshQueue, formatOutboundQueueVerdict, formatSocketPlane, getBrokerEventBus, getCapUsageRegistry, getFfmpegDownloadUrl, getLoggingGate, getModelFilePath, getMoleculerEventStats, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getWorkerDeviceRegistry, hasDotNode, hashClusterSecret, heapSpaceField, hostNodeIdOf, hubMainRssBudget, installManifestNativeDeps, installManifestPythonDeps, installManifestSystemDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcParentLink, isAddonDeploySource, isArrayBufferCensusReport, isArrayOutputSchema, isClusterSecretMismatchError, isCollectionArrayMethod, isInfraCapability, isModelDownloaded, isSourceNewer, isolatedBuiltinPhase, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, nextRssBudgetState, parseCapAction, parseRangeHeader, parseRssBudgetMb, parseTokenizedUrl, partitionIsolatedBuiltinIds, proxyToUpstream, quarantineAddonResidue, readExtraSans, readHeapSpaces, readLanHttpState, readMoleculerFanoutMode, readPendingRestart, readSocketQueues, readTlsAccessStatus, readTlsMode, readinessKey, reclaimIntervalMs, recordSocketFrame, registerEventBusService, registerLanHttpHandler, reissueTlsLeaf, resolveFilePath, resolveHwAccel, resolveNpmInvocation, runHubAddonBoot, runNpm, sampleSocketDirection, scheduleSelfRestart, scopeKey, scopesAllowAddon, scopesAllowDeviceCap, selectAddonResidue, selectReportedSpaces, serializeTypedArrays, setHubConnected, setNodeEventInterest, setWrapperActive, shouldReclaim, signalAllRunnerGroups, socketDirectionBytes, socketDirectionMessages, startEventLoopStallMonitor, startHeapWatch, startRunnerHeapWatch, strandedMb, stripCamstackDeps, subscribePassthrough, udsChildLogToWorkerEntry, validateProviderRegistrations, validateUploadedTls, waitUntilReady, writeExtraSans, writePendingRestart, writeTlsMode };
@@ -73,6 +73,7 @@ export { localProviderLink, brokerTransportLink, deviceCapResolverLink, ipcParen
73
73
  export type { LocalProviderResolver, BrokerCapCallOpts } from './moleculer/trpc-links.js';
74
74
  export { createProcessService } from './moleculer/process-service.js';
75
75
  export type { ProcessInfo } from './moleculer/process-service.js';
76
+ export { signalAllRunnerGroups } from './moleculer/runner-process-group.js';
76
77
  export { CapUsageRegistry, getCapUsageRegistry, PARENT_ROUTED_PROVIDER_ADDON_ID, __resetCapUsageRegistryForTests, } from './moleculer/cap-usage-registry.js';
77
78
  export type { CapUsageCallRecord, CapUsageEdge, CapUsageRegistryOptions, CapUsageStats, } from './moleculer/cap-usage-registry.js';
78
79
  export { createScopedProcessManager } from './moleculer/process-context.js';
@@ -1,5 +1,6 @@
1
1
  import { ChildProcess, SpawnOptions } from 'node:child_process';
2
2
  import { ServiceSchema } from 'moleculer';
3
+ import { ProcessGroupOps } from './runner-process-group.js';
3
4
  /** One addon hosted inside a runner subprocess. */
4
5
  interface RunnerAddonSpec {
5
6
  readonly addonId: string;
@@ -137,6 +138,10 @@ interface SpawnedProcess {
137
138
  */
138
139
  state: 'starting' | 'running' | 'stopping' | 'stopped' | 'crashed' | 'failed';
139
140
  child: ChildProcess;
141
+ /** The runner leads its own process group (POSIX, D668); its pgid is its `pid`. */
142
+ readonly ownsGroup: boolean;
143
+ /** The reap of this runner's group, once started. Memoized: every exit path shares it. */
144
+ groupReap: Promise<void> | null;
140
145
  }
141
146
  interface ProcessInfo {
142
147
  readonly pid: number;
@@ -174,6 +179,10 @@ interface ProcessInfo {
174
179
  type SpawnFn = (command: string, args: readonly string[], opts: SpawnOptions) => ChildProcess;
175
180
  interface ProcessServiceDeps {
176
181
  readonly spawnFn?: SpawnFn;
182
+ /** Group signalling + member listing. Tests inject a fake: a fake pid must never be signalled. */
183
+ readonly processGroups?: ProcessGroupOps;
184
+ /** Platform deciding whether a runner leads a process group. Defaults to `process.platform`. */
185
+ readonly platform?: NodeJS.Platform;
177
186
  }
178
187
  declare function createProcessService(parentNodeId: string, dataDir: string, deps?: ProcessServiceDeps, parentTcpPort?: number, parentUdsPath?: string): ServiceSchema;
179
188
  export type { ProcessInfo, SpawnedProcess };
@@ -0,0 +1,92 @@
1
+ /** SIGTERM → SIGKILL grace for a runner's group, in ms. ffmpeg finalises a segment well inside it. */
2
+ export declare const RUNNER_GROUP_REAP_GRACE_MS = 2000;
3
+ /** Whether a runner is spawned as a process-group leader on this platform. */
4
+ export declare function runnerOwnsProcessGroup(platform?: NodeJS.Platform): boolean;
5
+ /** One live (non-zombie) member of a process group. */
6
+ export interface GroupMember {
7
+ readonly pid: number;
8
+ /** Executable name (`ffmpeg`, `llama-server`, …). */
9
+ readonly command: string;
10
+ }
11
+ /**
12
+ * The OS surface a group reap needs. Injectable so a unit test never signals a real pid:
13
+ * the fake-spawn suites hand out pids like 70001, and a `kill(-70001)` on a developer's
14
+ * machine is not a test.
15
+ */
16
+ export interface ProcessGroupOps {
17
+ /** `process.kill`. A negative pid addresses a group; signal 0 probes. Throws ESRCH when empty. */
18
+ readonly kill: (pid: number, signal: NodeJS.Signals | 0) => void;
19
+ /** Live members of `pgid`, or `null` when the table could not be read. */
20
+ readonly listMembers: (pgid: number) => Promise<readonly GroupMember[] | null>;
21
+ readonly sleep: (ms: number) => Promise<void>;
22
+ readonly graceMs: number;
23
+ }
24
+ /** What one reap did. `membersAtExit === null` means the table was unreadable, not empty. */
25
+ export interface GroupReapResult {
26
+ readonly pgid: number;
27
+ readonly membersAtExit: readonly GroupMember[] | null;
28
+ /** Members still present after the grace, which were sent SIGKILL. */
29
+ readonly killed: readonly GroupMember[] | null;
30
+ /** The group was already empty when the reap began — the normal case after a clean stop. */
31
+ readonly alreadyEmpty: boolean;
32
+ /** A signal the OS refused for a reason other than ESRCH (EPERM). */
33
+ readonly error: string | null;
34
+ }
35
+ /**
36
+ * SIGTERM the whole group, wait up to `graceMs` for it to empty, SIGKILL what is left.
37
+ *
38
+ * pid reuse: POSIX never hands out a pid while a process group with that id exists, so
39
+ * the SIGTERM always lands on our group. The SIGKILL is sent only when the group is
40
+ * still populated at the end of the grace, which is the same group; the residual window
41
+ * (group empties, the pid wraps around AND is reused as a new group leader, all inside
42
+ * the grace) is not reachable in practice.
43
+ */
44
+ export declare function reapProcessGroup(pgid: number, ops: ProcessGroupOps): Promise<GroupReapResult>;
45
+ /** `ffmpeg×28 llama-server×1` — what a reap line says it removed. */
46
+ export declare function summarizeMembers(members: readonly GroupMember[]): string;
47
+ /** Parse `ps -A -o pid=,pgid=,stat=,comm=` for one group, zombies excluded. */
48
+ export declare function parsePsGroupMembers(stdout: string, pgid: number): readonly GroupMember[];
49
+ /** Live members of `pgid`: `/proc` on Linux, `ps` elsewhere; `null` when neither answers. */
50
+ export declare function listProcessGroupMembers(pgid: number, platform?: NodeJS.Platform): Promise<readonly GroupMember[] | null>;
51
+ export declare const defaultProcessGroupOps: ProcessGroupOps;
52
+ interface OwnedGroup {
53
+ readonly runnerId: string;
54
+ readonly kill: ProcessGroupOps['kill'];
55
+ }
56
+ export declare function registerRunnerGroup(pgid: number, group: OwnedGroup): void;
57
+ export declare function releaseRunnerGroup(pgid: number): void;
58
+ /**
59
+ * Signal every runner group this process owns, synchronously. Hub-main's SIGTERM/SIGINT
60
+ * handler calls it: before D668 the runners were in hub-main's own group and its
61
+ * `kill -- -<pid>` reached them and their children; now each is its own group.
62
+ * Returns how many groups were signalled.
63
+ */
64
+ export declare function signalAllRunnerGroups(signal: NodeJS.Signals): number;
65
+ /** Test seam: the groups currently owned, by pgid. */
66
+ export declare function ownedRunnerGroupIds(): readonly number[];
67
+ export interface RunnerParentDeathWatchdogOptions {
68
+ readonly initialPpid?: number;
69
+ readonly readPpid?: () => number;
70
+ readonly intervalMs?: number;
71
+ }
72
+ /**
73
+ * Arm the runner's parent-death watchdog before addon code starts loading.
74
+ *
75
+ * A parent can die while an addon module or `initialize()` is still pending, so
76
+ * installing this only after READY leaves exactly the boot window where a detached
77
+ * runner can become an orphan. Any PPID change means the direct parent died and the
78
+ * kernel reparented us; checking for a change also covers a non-PID-1 subreaper.
79
+ */
80
+ export declare function startRunnerParentDeathWatchdog(onParentDeath: () => void, options?: RunnerParentDeathWatchdogOptions): NodeJS.Timeout | null;
81
+ /**
82
+ * Last resort for a detached runner whose direct parent disappeared without an
83
+ * `exit` hook (SIGKILL, native crash). The runner's own pid is also its pgid, so
84
+ * one SIGKILL removes the runner and every descendant in that group atomically.
85
+ *
86
+ * Calling `kill(-process.pid, ...)` from an ordinary, non-group-leading process
87
+ * returns ESRCH: while this pid exists, no unrelated process group can have the
88
+ * same id. That makes the failed call a safe compatibility fallback for an older
89
+ * parent that did not spawn the runner detached.
90
+ */
91
+ export declare function killOwnRunnerGroupAfterParentLoss(platform?: NodeJS.Platform, kill?: ProcessGroupOps['kill']): boolean;
92
+ export {};