@camstack/system 1.0.6 → 1.0.7

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.
@@ -1,11 +1,14 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_manifest_python_deps = require("./manifest-python-deps-B4BmMoGT.js");
2
+ const require_manifest_python_deps = require("./manifest-python-deps-eBDj5HEY.js");
3
+ const require_custom_action_registry = require("./custom-action-registry-vLYEFTtv.js");
3
4
  let node_fs = require("node:fs");
4
5
  node_fs = require_chunk.__toESM(node_fs);
5
6
  let node_path = require("node:path");
6
7
  node_path = require_chunk.__toESM(node_path);
7
- let _camstack_types = require("@camstack/types");
8
+ let _camstack_types_addon = require("@camstack/types/addon");
8
9
  let node_url = require("node:url");
10
+ let node_v8 = require("node:v8");
11
+ node_v8 = require_chunk.__toESM(node_v8);
9
12
  let node_module = require("node:module");
10
13
  //#region src/kernel/moleculer/worker-device-restore.ts
11
14
  /**
@@ -41,7 +44,7 @@ async function runWorkerDeviceRestoreWithRetry(addon, context, addonId, sourceNo
41
44
  return;
42
45
  }
43
46
  const shared = context.kernel?.readinessRegistry ?? null;
44
- const registry = shared ?? new _camstack_types.ReadinessRegistry({
47
+ const registry = shared ?? new _camstack_types_addon.ReadinessRegistry({
45
48
  eventBus: bus,
46
49
  sourceNodeId,
47
50
  logger: context.logger
@@ -54,13 +57,13 @@ async function runWorkerDeviceRestoreWithRetry(addon, context, addonId, sourceNo
54
57
  }, { timeoutMs: 6e4 });
55
58
  log?.debug?.(`[worker-restore] "${addonId}": device-manager READY`);
56
59
  } catch (err) {
57
- if (err instanceof _camstack_types.ReadinessTimeoutError) {
60
+ if (err instanceof _camstack_types_addon.ReadinessTimeoutError) {
58
61
  context.logger?.warn?.(`[worker-restore] device-manager not ready within ${err.waitedMs}ms — skipping for "${addonId}"`);
59
62
  return;
60
63
  }
61
64
  throw err;
62
65
  } finally {
63
- if (!shared && registry instanceof _camstack_types.ReadinessRegistry) registry.close();
66
+ if (!shared && registry instanceof _camstack_types_addon.ReadinessRegistry) registry.close();
64
67
  }
65
68
  try {
66
69
  const deviceManager = Reflect.get(api, "deviceManager");
@@ -86,7 +89,7 @@ async function runWorkerDeviceRestoreWithRetry(addon, context, addonId, sourceNo
86
89
  await restoreFn.call(addon, savedDevices);
87
90
  log?.info?.(`[worker-restore] "${addonId}": restored ${savedDevices.length} device(s)`);
88
91
  } catch (err) {
89
- log?.warn?.(`[worker-restore] "${addonId}": restoreDevices threw: ${(0, _camstack_types.errMsg)(err)}`);
92
+ log?.warn?.(`[worker-restore] "${addonId}": restoreDevices threw: ${(0, _camstack_types_addon.errMsg)(err)}`);
90
93
  }
91
94
  }
92
95
  //#endregion
@@ -272,11 +275,11 @@ function createChildAddonCallDispatch(deps) {
272
275
  * every loaded addon before closing the UDS channel.
273
276
  */
274
277
  process.on("uncaughtException", (err) => {
275
- console.error(`[addon-runner CRASH] uncaughtException: ${(0, _camstack_types.errMsg)(err)}\n${err instanceof Error ? err.stack ?? "" : ""}`);
278
+ console.error(`[addon-runner CRASH] uncaughtException: ${(0, _camstack_types_addon.errMsg)(err)}\n${err instanceof Error ? err.stack ?? "" : ""}`);
276
279
  process.exit(1);
277
280
  });
278
281
  process.on("unhandledRejection", (reason) => {
279
- console.error(`[addon-runner CRASH] unhandledRejection: ${(0, _camstack_types.errMsg)(reason)}\n${reason instanceof Error ? reason.stack ?? "" : ""}`);
282
+ console.error(`[addon-runner CRASH] unhandledRejection: ${(0, _camstack_types_addon.errMsg)(reason)}\n${reason instanceof Error ? reason.stack ?? "" : ""}`);
280
283
  });
281
284
  process.on("exit", (code) => {
282
285
  console.error(`[addon-runner EXIT] code=${String(code)} nodeId=${process.env["CAMSTACK_RUNNER_ID"] ?? "?"}`);
@@ -297,7 +300,7 @@ function parseAddonsSpec(raw) {
297
300
  try {
298
301
  parsed = JSON.parse(raw);
299
302
  } catch (err) {
300
- throw new Error(`[addon-runner] CAMSTACK_RUNNER_ADDONS is not valid JSON: ${(0, _camstack_types.errMsg)(err)}`, { cause: err });
303
+ throw new Error(`[addon-runner] CAMSTACK_RUNNER_ADDONS is not valid JSON: ${(0, _camstack_types_addon.errMsg)(err)}`, { cause: err });
301
304
  }
302
305
  if (!Array.isArray(parsed)) throw new Error(`[addon-runner] CAMSTACK_RUNNER_ADDONS must be a JSON array`);
303
306
  const specs = [];
@@ -370,7 +373,7 @@ async function main() {
370
373
  try {
371
374
  specs = parseAddonsSpec(addonsJson);
372
375
  } catch (err) {
373
- console.error((0, _camstack_types.errMsg)(err));
376
+ console.error((0, _camstack_types_addon.errMsg)(err));
374
377
  process.exit(1);
375
378
  }
376
379
  if (specs.length === 0) {
@@ -393,7 +396,7 @@ async function main() {
393
396
  AddonClass
394
397
  });
395
398
  } catch (err) {
396
- console.error(`[addon-runner] Failed to load "${spec.addonId}": ${(0, _camstack_types.errMsg)(err)}`);
399
+ console.error(`[addon-runner] Failed to load "${spec.addonId}": ${(0, _camstack_types_addon.errMsg)(err)}`);
397
400
  process.exit(1);
398
401
  }
399
402
  let storageProvider;
@@ -463,7 +466,7 @@ async function main() {
463
466
  * removed `custom.<action>` Moleculer action). Constructed up front so the
464
467
  * addon-call handler can close over it at client-construction time.
465
468
  */
466
- const customActionRegistry = new require_manifest_python_deps.CustomActionRegistry();
469
+ const customActionRegistry = new require_custom_action_registry.CustomActionRegistry();
467
470
  let shuttingDown = false;
468
471
  const udsChildClient = new require_manifest_python_deps.LocalChildClient({
469
472
  nodeId: parentNodeId,
@@ -488,12 +491,12 @@ async function main() {
488
491
  try {
489
492
  await udsChildClient.start();
490
493
  } catch (err) {
491
- console.error(`[addon-runner] "${nodeId}" UDS connect failed — exiting: ${(0, _camstack_types.errMsg)(err)}`);
494
+ console.error(`[addon-runner] "${nodeId}" UDS connect failed — exiting: ${(0, _camstack_types_addon.errMsg)(err)}`);
492
495
  process.exit(1);
493
496
  }
494
497
  runnerLog.info("UDS child transport connected", { meta: { childId: runnerId } });
495
498
  if (storageProvider) runnerLog.info(`"${nodeId}" storage provider rooted at ${dataDir}`);
496
- else runnerLog.warn(`"${nodeId}" failed to load FilesystemStorageProvider`, { meta: { error: (0, _camstack_types.errMsg)(storageProviderError) } });
499
+ else runnerLog.warn(`"${nodeId}" failed to load FilesystemStorageProvider`, { meta: { error: (0, _camstack_types_addon.errMsg)(storageProviderError) } });
497
500
  const refreshChildCaps = (reason) => {
498
501
  const freshDescriptors = buildChildCapDescriptors(runnerManifest, require_manifest_python_deps.getWorkerNativeCapSnapshot());
499
502
  udsChildClient.updateCaps(freshDescriptors).catch((err) => {
@@ -523,12 +526,12 @@ async function main() {
523
526
  const pkgRaw = JSON.parse(node_fs.readFileSync(node_path.join(spec.addonDir, "package.json"), "utf-8"));
524
527
  await require_manifest_python_deps.installManifestNativeDeps(spec.addonDir, pkgRaw, context.logger);
525
528
  } catch (nativeErr) {
526
- console.error(`[addon-runner] native deps install failed for "${spec.addonId}": ${(0, _camstack_types.errMsg)(nativeErr)}`);
529
+ console.error(`[addon-runner] native deps install failed for "${spec.addonId}": ${(0, _camstack_types_addon.errMsg)(nativeErr)}`);
527
530
  process.exit(1);
528
531
  }
529
532
  await require_manifest_python_deps.installManifestPythonDeps(declaration, spec.addonDir, context.deps, context.logger);
530
533
  const registeredProviders = /* @__PURE__ */ new Map();
531
- const initResult = (0, _camstack_types.normalizeAddonInitResult)(await addon.initialize(context));
534
+ const initResult = (0, _camstack_types_addon.normalizeAddonInitResult)(await addon.initialize(context));
532
535
  for (const reg of initResult?.providers ?? []) {
533
536
  const capName = reg.capability.name;
534
537
  registeredProviders.set(capName, reg.provider);
@@ -564,7 +567,7 @@ async function main() {
564
567
  initResult: initResult ?? null
565
568
  });
566
569
  } catch (err) {
567
- console.error(`[addon-runner] Init failed for "${spec.addonId}": ${(0, _camstack_types.errMsg)(err)}`);
570
+ console.error(`[addon-runner] Init failed for "${spec.addonId}": ${(0, _camstack_types_addon.errMsg)(err)}`);
568
571
  process.exit(1);
569
572
  }
570
573
  if (deviceRestorePromises.length > 0 && !shuttingDown) await Promise.allSettled(deviceRestorePromises);
@@ -574,7 +577,7 @@ async function main() {
574
577
  if (hubReachableFired) return;
575
578
  hubReachableFired = true;
576
579
  for (const entry of loaded) if (typeof entry.addon.onHubReachable === "function") Promise.resolve(entry.addon.onHubReachable()).catch((err) => {
577
- runnerLog.error(`"${entry.addonId}" onHubReachable() threw`, { meta: { error: (0, _camstack_types.errMsg)(err) } });
580
+ runnerLog.error(`"${entry.addonId}" onHubReachable() threw`, { meta: { error: (0, _camstack_types_addon.errMsg)(err) } });
578
581
  });
579
582
  };
580
583
  udsChildClient.onConnected(() => {
@@ -584,6 +587,20 @@ async function main() {
584
587
  fireHubReachableOnce();
585
588
  runnerLog.info(`"${nodeId}" started — ${loaded.length} addon(s) ready (UDS connected)`);
586
589
  process.stdout.write(`READY:${nodeId}\n`);
590
+ process.on("SIGUSR2", () => {
591
+ const MB = (x) => Math.round(x / 1048576 * 10) / 10;
592
+ try {
593
+ const mu = process.memoryUsage();
594
+ const spaces = node_v8.getHeapSpaceStatistics().filter((s) => s.space_used_size > 0).map((s) => `${s.space_name}=${MB(s.space_used_size)}`).join(" ");
595
+ const addons = loaded.map((e) => e.addonId).join(",");
596
+ runnerLog.info(`[mem] node="${nodeId}" addons=[${addons}] rss=${MB(mu.rss)} heapUsed=${MB(mu.heapUsed)} heapTotal=${MB(mu.heapTotal)} external=${MB(mu.external)} arrayBuffers=${MB(mu.arrayBuffers)} | ${spaces}`);
597
+ const snapPath = `/tmp/heap-${nodeId.replace(/[^\w.-]/g, "_")}.heapsnapshot`;
598
+ node_v8.writeHeapSnapshot(snapPath);
599
+ runnerLog.info(`[mem] node="${nodeId}" heap snapshot written -> ${snapPath}`);
600
+ } catch (err) {
601
+ runnerLog.warn(`[mem] snapshot failed: ${(0, _camstack_types_addon.errMsg)(err)}`);
602
+ }
603
+ });
587
604
  const shutdown = async () => {
588
605
  shuttingDown = true;
589
606
  require_manifest_python_deps.setWorkerNativeCapsChangeListener(null);
@@ -595,12 +612,12 @@ async function main() {
595
612
  for (const entry of loaded) try {
596
613
  await entry.addon.shutdown();
597
614
  } catch (err) {
598
- runnerLog.error(`"${entry.addonId}" shutdown threw`, { meta: { error: (0, _camstack_types.errMsg)(err) } });
615
+ runnerLog.error(`"${entry.addonId}" shutdown threw`, { meta: { error: (0, _camstack_types_addon.errMsg)(err) } });
599
616
  }
600
617
  try {
601
618
  await udsChildClient.close();
602
619
  } catch (err) {
603
- runnerLog.warn("UDS child transport close threw during shutdown", { meta: { error: (0, _camstack_types.errMsg)(err) } });
620
+ runnerLog.warn("UDS child transport close threw during shutdown", { meta: { error: (0, _camstack_types_addon.errMsg)(err) } });
604
621
  }
605
622
  process.exit(0);
606
623
  };
@@ -619,7 +636,7 @@ async function main() {
619
636
  }, 5e3);
620
637
  }
621
638
  main().catch((err) => {
622
- console.error(`[addon-runner] Fatal error: ${(0, _camstack_types.errMsg)(err)}`);
639
+ console.error(`[addon-runner] Fatal error: ${(0, _camstack_types_addon.errMsg)(err)}`);
623
640
  process.exit(1);
624
641
  });
625
642
  //#endregion
@@ -1,9 +1,11 @@
1
- import { $ as setWorkerNativeCapsChangeListener, A as createUdsLoggerWithControl, L as LocalChildClient, X as getWorkerNativeCapProvider, Z as getWorkerNativeCapSnapshot, dt as CustomActionRegistry, ht as resolveAddonClass, i as createUdsAddonContext, mt as installManifestNativeDeps, t as installManifestPythonDeps, tt as validateProviderRegistrations } from "./manifest-python-deps-CXbKrOdk.mjs";
1
+ import { $ as setWorkerNativeCapsChangeListener, A as createUdsLoggerWithControl, L as LocalChildClient, X as getWorkerNativeCapProvider, Z as getWorkerNativeCapSnapshot, i as createUdsAddonContext, mt as resolveAddonClass, pt as installManifestNativeDeps, t as installManifestPythonDeps, tt as validateProviderRegistrations } from "./manifest-python-deps-CoJXeb9u.mjs";
2
+ import { t as CustomActionRegistry } from "./custom-action-registry-BEXwC-oo.mjs";
2
3
  import { register } from "node:module";
3
4
  import * as fs from "node:fs";
4
5
  import * as path$1 from "node:path";
5
- import { ReadinessRegistry, ReadinessTimeoutError, errMsg, normalizeAddonInitResult } from "@camstack/types";
6
+ import { ReadinessRegistry, ReadinessTimeoutError, errMsg, normalizeAddonInitResult } from "@camstack/types/addon";
6
7
  import { pathToFileURL } from "node:url";
8
+ import * as v8 from "node:v8";
7
9
  //#region src/kernel/moleculer/worker-device-restore.ts
8
10
  /**
9
11
  * Worker-side device restore helper.
@@ -581,6 +583,20 @@ async function main() {
581
583
  fireHubReachableOnce();
582
584
  runnerLog.info(`"${nodeId}" started — ${loaded.length} addon(s) ready (UDS connected)`);
583
585
  process.stdout.write(`READY:${nodeId}\n`);
586
+ process.on("SIGUSR2", () => {
587
+ const MB = (x) => Math.round(x / 1048576 * 10) / 10;
588
+ try {
589
+ const mu = process.memoryUsage();
590
+ const spaces = v8.getHeapSpaceStatistics().filter((s) => s.space_used_size > 0).map((s) => `${s.space_name}=${MB(s.space_used_size)}`).join(" ");
591
+ const addons = loaded.map((e) => e.addonId).join(",");
592
+ runnerLog.info(`[mem] node="${nodeId}" addons=[${addons}] rss=${MB(mu.rss)} heapUsed=${MB(mu.heapUsed)} heapTotal=${MB(mu.heapTotal)} external=${MB(mu.external)} arrayBuffers=${MB(mu.arrayBuffers)} | ${spaces}`);
593
+ const snapPath = `/tmp/heap-${nodeId.replace(/[^\w.-]/g, "_")}.heapsnapshot`;
594
+ v8.writeHeapSnapshot(snapPath);
595
+ runnerLog.info(`[mem] node="${nodeId}" heap snapshot written -> ${snapPath}`);
596
+ } catch (err) {
597
+ runnerLog.warn(`[mem] snapshot failed: ${errMsg(err)}`);
598
+ }
599
+ });
584
600
  const shutdown = async () => {
585
601
  shuttingDown = true;
586
602
  setWorkerNativeCapsChangeListener(null);
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Lightweight addon-facing utility surface for `@camstack/system`.
3
+ *
4
+ * Importing the root barrel (`@camstack/system`) loads the ENTIRE framework —
5
+ * the kernel (moleculer) + every builtin addon (sqlite-storage, winston-logging,
6
+ * device-manager, …) — ~64MB of V8 heap PER process, even when an addon only
7
+ * needs a model-download or data-plane helper. Node has no runtime tree-shaking
8
+ * and the package is host-provided (loaded as the prebuilt barrel), so every
9
+ * addon that imported one util from the barrel paid the full ~64MB — multiplied
10
+ * across every forked pipeline runner.
11
+ *
12
+ * This subpath re-exports ONLY the addon-facing helpers, each from its DEEP,
13
+ * kernel-free source module, so importing it costs a few MB instead of ~64MB.
14
+ *
15
+ * See memory: camstack_runner_ram_and_build_rootcause.
16
+ */
17
+ export { downloadFile, ensureModel, isModelDownloaded, deleteModelFromDisk, } from './download/model-downloader.js';
18
+ export { ModelDownloadService } from './download/model-download-service.js';
19
+ export { createFileDataPlaneHandler } from './http/file-data-plane.js';
20
+ export { CustomActionRegistry } from './kernel/custom-action-registry.js';
@@ -0,0 +1,11 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("./chunk-Cek0wNdY.js");
3
+ const require_model_download_service = require("./model-download-service-JtVQtbb6.js");
4
+ const require_custom_action_registry = require("./custom-action-registry-vLYEFTtv.js");
5
+ exports.CustomActionRegistry = require_custom_action_registry.CustomActionRegistry;
6
+ exports.ModelDownloadService = require_model_download_service.ModelDownloadService;
7
+ exports.createFileDataPlaneHandler = require_model_download_service.createFileDataPlaneHandler;
8
+ exports.deleteModelFromDisk = require_model_download_service.deleteModelFromDisk;
9
+ exports.downloadFile = require_model_download_service.downloadFile;
10
+ exports.ensureModel = require_model_download_service.ensureModel;
11
+ exports.isModelDownloaded = require_model_download_service.isModelDownloaded;
@@ -0,0 +1,3 @@
1
+ import { a as ensureModel, c as isModelDownloaded, l as createFileDataPlaneHandler, n as deleteModelFromDisk, r as downloadFile, t as ModelDownloadService } from "./model-download-service-C7AjBsX9.mjs";
2
+ import { t as CustomActionRegistry } from "./custom-action-registry-BEXwC-oo.mjs";
3
+ export { CustomActionRegistry, ModelDownloadService, createFileDataPlaneHandler, deleteModelFromDisk, downloadFile, ensureModel, isModelDownloaded };
@@ -66,6 +66,14 @@ export default class NativeMetricsAddon extends BaseAddon<NativeMetricsConfig> {
66
66
  * the dedicated `$process.restart` action, not this kill API.
67
67
  */
68
68
  private killProcess;
69
+ /**
70
+ * Ask the addon's forked runner to write a V8 heap snapshot (SIGUSR2 → the
71
+ * runner's diagnostic handler in addon-runner.ts), for deep per-addon memory
72
+ * attribution. Resolves the addon's worker pid from the kernel `$process.list`
73
+ * and signals it; the runner writes `/tmp/heap-<sanitized nodeId>.heapsnapshot`
74
+ * and logs its memoryUsage + heap-space breakdown. '$hub' targets this process.
75
+ */
76
+ private dumpHeapSnapshot;
69
77
  /** Raw `ps` scan returning every pid + command + resource stats. */
70
78
  private runPs;
71
79
  protected globalSettingsSchema(): import('@camstack/types').ConfigUISchema;
@@ -123,13 +123,15 @@ var NativeMetricsProvider = class {
123
123
  return getCpuTemperatureInternal();
124
124
  }
125
125
  async getProcessStats(input) {
126
- const { getPidStats } = await Promise.resolve().then(() => require("../../resource-monitor-IIEanuJt.js")).then((n) => n.resource_monitor_exports);
126
+ const { getPidStats } = await Promise.resolve().then(() => require("../../resource-monitor-DNNomR-i.js")).then((n) => n.resource_monitor_exports);
127
127
  const raw = await getPidStats(input.pids);
128
128
  const result = [];
129
129
  for (const [pid, s] of raw) result.push({
130
130
  pid,
131
131
  cpu: s.cpu,
132
- memory: s.memory
132
+ memory: s.memory,
133
+ privateBytes: s.privateBytes,
134
+ sharedBytes: s.sharedBytes
133
135
  });
134
136
  return result;
135
137
  }
@@ -596,7 +598,8 @@ var NativeMetricsAddon = class extends _camstack_types.BaseAddon {
596
598
  listAddonInstances: () => this.listAddonInstances(),
597
599
  getAddonStats: (params) => this.getAddonStats(params.addonId),
598
600
  listNodeProcesses: () => this.listNodeProcesses(),
599
- killProcess: (params) => this.killProcess(params)
601
+ killProcess: (params) => this.killProcess(params),
602
+ dumpHeapSnapshot: (params) => this.dumpHeapSnapshot(params)
600
603
  };
601
604
  this.snapshotTimer = setInterval(() => this.emitMetricsSnapshots(), METRICS_SNAPSHOT_INTERVAL_MS);
602
605
  return [{
@@ -845,6 +848,50 @@ var NativeMetricsAddon = class extends _camstack_types.BaseAddon {
845
848
  };
846
849
  }
847
850
  }
851
+ /**
852
+ * Ask the addon's forked runner to write a V8 heap snapshot (SIGUSR2 → the
853
+ * runner's diagnostic handler in addon-runner.ts), for deep per-addon memory
854
+ * attribution. Resolves the addon's worker pid from the kernel `$process.list`
855
+ * and signals it; the runner writes `/tmp/heap-<sanitized nodeId>.heapsnapshot`
856
+ * and logs its memoryUsage + heap-space breakdown. '$hub' targets this process.
857
+ */
858
+ async dumpHeapSnapshot(input) {
859
+ const sanitize = (nodeId) => nodeId.replace(/[^\w.-]/g, "_");
860
+ let pid;
861
+ let nodeId;
862
+ if (input.addonId === "$hub") {
863
+ pid = process.pid;
864
+ nodeId = this.ctx.kernel.cluster?.broker?.nodeID ?? "$hub";
865
+ } else {
866
+ const target = (await this.listWorkerInstances()).find((w) => w.addonId === input.addonId);
867
+ if (!target) return {
868
+ success: false,
869
+ reason: `no runner process for addon '${input.addonId}'`
870
+ };
871
+ pid = target.pid;
872
+ nodeId = target.nodeId;
873
+ }
874
+ const path = `/tmp/heap-${sanitize(nodeId)}.heapsnapshot`;
875
+ try {
876
+ process.kill(pid, "SIGUSR2");
877
+ this.ctx.logger.info("Requested heap snapshot from runner", { meta: {
878
+ addonId: input.addonId,
879
+ pid,
880
+ path
881
+ } });
882
+ return {
883
+ success: true,
884
+ pid,
885
+ path
886
+ };
887
+ } catch (err) {
888
+ return {
889
+ success: false,
890
+ reason: err instanceof Error ? err.message : String(err),
891
+ pid
892
+ };
893
+ }
894
+ }
848
895
  /** Raw `ps` scan returning every pid + command + resource stats. */
849
896
  async runPs() {
850
897
  try {
@@ -116,13 +116,15 @@ var NativeMetricsProvider = class {
116
116
  return getCpuTemperatureInternal();
117
117
  }
118
118
  async getProcessStats(input) {
119
- const { getPidStats } = await import("../../resource-monitor-ClDGFyf6.mjs").then((n) => n.r);
119
+ const { getPidStats } = await import("../../resource-monitor-BkP504Vq.mjs").then((n) => n.r);
120
120
  const raw = await getPidStats(input.pids);
121
121
  const result = [];
122
122
  for (const [pid, s] of raw) result.push({
123
123
  pid,
124
124
  cpu: s.cpu,
125
- memory: s.memory
125
+ memory: s.memory,
126
+ privateBytes: s.privateBytes,
127
+ sharedBytes: s.sharedBytes
126
128
  });
127
129
  return result;
128
130
  }
@@ -589,7 +591,8 @@ var NativeMetricsAddon = class extends BaseAddon {
589
591
  listAddonInstances: () => this.listAddonInstances(),
590
592
  getAddonStats: (params) => this.getAddonStats(params.addonId),
591
593
  listNodeProcesses: () => this.listNodeProcesses(),
592
- killProcess: (params) => this.killProcess(params)
594
+ killProcess: (params) => this.killProcess(params),
595
+ dumpHeapSnapshot: (params) => this.dumpHeapSnapshot(params)
593
596
  };
594
597
  this.snapshotTimer = setInterval(() => this.emitMetricsSnapshots(), METRICS_SNAPSHOT_INTERVAL_MS);
595
598
  return [{
@@ -838,6 +841,50 @@ var NativeMetricsAddon = class extends BaseAddon {
838
841
  };
839
842
  }
840
843
  }
844
+ /**
845
+ * Ask the addon's forked runner to write a V8 heap snapshot (SIGUSR2 → the
846
+ * runner's diagnostic handler in addon-runner.ts), for deep per-addon memory
847
+ * attribution. Resolves the addon's worker pid from the kernel `$process.list`
848
+ * and signals it; the runner writes `/tmp/heap-<sanitized nodeId>.heapsnapshot`
849
+ * and logs its memoryUsage + heap-space breakdown. '$hub' targets this process.
850
+ */
851
+ async dumpHeapSnapshot(input) {
852
+ const sanitize = (nodeId) => nodeId.replace(/[^\w.-]/g, "_");
853
+ let pid;
854
+ let nodeId;
855
+ if (input.addonId === "$hub") {
856
+ pid = process.pid;
857
+ nodeId = this.ctx.kernel.cluster?.broker?.nodeID ?? "$hub";
858
+ } else {
859
+ const target = (await this.listWorkerInstances()).find((w) => w.addonId === input.addonId);
860
+ if (!target) return {
861
+ success: false,
862
+ reason: `no runner process for addon '${input.addonId}'`
863
+ };
864
+ pid = target.pid;
865
+ nodeId = target.nodeId;
866
+ }
867
+ const path = `/tmp/heap-${sanitize(nodeId)}.heapsnapshot`;
868
+ try {
869
+ process.kill(pid, "SIGUSR2");
870
+ this.ctx.logger.info("Requested heap snapshot from runner", { meta: {
871
+ addonId: input.addonId,
872
+ pid,
873
+ path
874
+ } });
875
+ return {
876
+ success: true,
877
+ pid,
878
+ path
879
+ };
880
+ } catch (err) {
881
+ return {
882
+ success: false,
883
+ reason: err instanceof Error ? err.message : String(err),
884
+ pid
885
+ };
886
+ }
887
+ }
841
888
  /** Raw `ps` scan returning every pid + command + resource stats. */
842
889
  async runPs() {
843
890
  try {
@@ -0,0 +1,38 @@
1
+ //#region src/kernel/custom-action-registry.ts
2
+ /**
3
+ * CustomActionRegistry — per-process registry of addon custom actions.
4
+ *
5
+ * Populated at boot from each addon's `AddonInitResult.customActions` +
6
+ * `handleCustomAction` handler. Rejects actions declared with scope other
7
+ * than 'system' (today only 'system' is runtime-supported; the descriptor
8
+ * allows future scopes for forward compat).
9
+ */
10
+ var CustomActionRegistry = class {
11
+ byAddon = /* @__PURE__ */ new Map();
12
+ registerAddon(addonId, catalog, handler) {
13
+ const actions = /* @__PURE__ */ new Map();
14
+ for (const [name, spec] of Object.entries(catalog)) {
15
+ const scope = spec.scope ?? { kind: "system" };
16
+ if (scope.kind !== "system") throw new Error(`custom action '${addonId}.${name}' declared scope '${scope.kind}' — not yet implemented`);
17
+ actions.set(name, {
18
+ spec,
19
+ handler: (input) => handler(name, input)
20
+ });
21
+ }
22
+ this.byAddon.set(addonId, actions);
23
+ }
24
+ unregisterAddon(addonId) {
25
+ this.byAddon.delete(addonId);
26
+ }
27
+ resolve(addonId, action) {
28
+ return this.byAddon.get(addonId)?.get(action) ?? null;
29
+ }
30
+ listActions(addonId) {
31
+ return [...this.byAddon.get(addonId)?.keys() ?? []];
32
+ }
33
+ listAddons() {
34
+ return [...this.byAddon.keys()];
35
+ }
36
+ };
37
+ //#endregion
38
+ export { CustomActionRegistry as t };
@@ -0,0 +1,43 @@
1
+ //#region src/kernel/custom-action-registry.ts
2
+ /**
3
+ * CustomActionRegistry — per-process registry of addon custom actions.
4
+ *
5
+ * Populated at boot from each addon's `AddonInitResult.customActions` +
6
+ * `handleCustomAction` handler. Rejects actions declared with scope other
7
+ * than 'system' (today only 'system' is runtime-supported; the descriptor
8
+ * allows future scopes for forward compat).
9
+ */
10
+ var CustomActionRegistry = class {
11
+ byAddon = /* @__PURE__ */ new Map();
12
+ registerAddon(addonId, catalog, handler) {
13
+ const actions = /* @__PURE__ */ new Map();
14
+ for (const [name, spec] of Object.entries(catalog)) {
15
+ const scope = spec.scope ?? { kind: "system" };
16
+ if (scope.kind !== "system") throw new Error(`custom action '${addonId}.${name}' declared scope '${scope.kind}' — not yet implemented`);
17
+ actions.set(name, {
18
+ spec,
19
+ handler: (input) => handler(name, input)
20
+ });
21
+ }
22
+ this.byAddon.set(addonId, actions);
23
+ }
24
+ unregisterAddon(addonId) {
25
+ this.byAddon.delete(addonId);
26
+ }
27
+ resolve(addonId, action) {
28
+ return this.byAddon.get(addonId)?.get(action) ?? null;
29
+ }
30
+ listActions(addonId) {
31
+ return [...this.byAddon.get(addonId)?.keys() ?? []];
32
+ }
33
+ listAddons() {
34
+ return [...this.byAddon.keys()];
35
+ }
36
+ };
37
+ //#endregion
38
+ Object.defineProperty(exports, "CustomActionRegistry", {
39
+ enumerable: true,
40
+ get: function() {
41
+ return CustomActionRegistry;
42
+ }
43
+ });