@camstack/system 1.2.103 → 1.2.105

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 (55) hide show
  1. package/dist/addon-runner.js +15 -1
  2. package/dist/addon-runner.mjs +15 -1
  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/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  11. package/dist/builtins/console-logging/index.js +1 -1
  12. package/dist/builtins/console-logging/index.mjs +1 -1
  13. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  14. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  15. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  16. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  18. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  19. package/dist/builtins/hub-forwarder/index.js +1 -1
  20. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  21. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  22. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  23. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  24. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  25. package/dist/builtins/local-network/local-network.addon.js +1 -1
  26. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  27. package/dist/builtins/loki-logging/index.js +1 -1
  28. package/dist/builtins/loki-logging/index.mjs +1 -1
  29. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  30. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  31. package/dist/builtins/platform-probe/index.js +1 -1
  32. package/dist/builtins/platform-probe/index.mjs +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  34. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  35. package/dist/builtins/snapshot/index.js +1 -1
  36. package/dist/builtins/snapshot/index.mjs +1 -1
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  38. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  39. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
  40. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
  41. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  42. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  43. package/dist/builtins/system-config/system-config.addon.js +1 -1
  44. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  45. package/dist/builtins/winston-logging/index.js +1 -1
  46. package/dist/builtins/winston-logging/index.mjs +1 -1
  47. package/dist/{dist-qnwy_iWM.js → dist-CB2eCKJb.js} +28 -1
  48. package/dist/{dist-DHKXob1m.mjs → dist-CF6uy00y.mjs} +28 -1
  49. package/dist/index.d.ts +2 -2
  50. package/dist/index.js +25 -312
  51. package/dist/index.mjs +15 -303
  52. package/dist/kernel/heap-watch.d.ts +67 -0
  53. package/dist/{manifest-python-deps-Bt_J04b0.mjs → manifest-python-deps-5ei2xVUh.mjs} +355 -1
  54. package/dist/{manifest-python-deps-BtVjvfvj.js → manifest-python-deps-GejnH--L.js} +422 -0
  55. 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-qnwy_iWM.js");
3
+ const require_dist = require("./dist-CB2eCKJb.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_formatter = require("./formatter-DqAKDlvN.js");
@@ -25,7 +25,7 @@ const require_builtins_system_config_system_config_addon = require("./builtins/s
25
25
  require("./builtins/system-config/index.js");
26
26
  const require_builtins_winston_logging_index = require("./builtins/winston-logging/index.js");
27
27
  const require_file_data_plane = require("./file-data-plane-DUHPHa-Y.js");
28
- const require_manifest_python_deps = require("./manifest-python-deps-BtVjvfvj.js");
28
+ const require_manifest_python_deps = require("./manifest-python-deps-GejnH--L.js");
29
29
  const require_resource_monitor = require("./resource-monitor-CdnzxBLP.js");
30
30
  const require_custom_action_registry = require("./custom-action-registry-jY0NOZK8.js");
31
31
  let zod = require("zod");
@@ -43,8 +43,6 @@ let node_fs = require("node:fs");
43
43
  let node_fs$1 = require_chunk.__toESM(node_fs, 1);
44
44
  node_fs = require_chunk.__toESM(node_fs);
45
45
  let node_http = require("node:http");
46
- let node_v8 = require("node:v8");
47
- node_v8 = require_chunk.__toESM(node_v8);
48
46
  let node_vm = require("node:vm");
49
47
  node_vm = require_chunk.__toESM(node_vm);
50
48
  let _camstack_types_addon = require("@camstack/types/addon");
@@ -431,304 +429,6 @@ function proxyToUpstream(opts) {
431
429
  else clientReq.pipe(upstream);
432
430
  }
433
431
  //#endregion
434
- //#region src/kernel/heap-watch.ts
435
- /**
436
- * heap-watch — hub-main's own memory, on the record, and returned to the OS.
437
- *
438
- * ## Why this exists
439
- *
440
- * hub-main used to be launched with `--max-old-space-size=1536` (docker-entrypoint;
441
- * the cap was removed on 2026-08-02, so the live ceiling is now V8's own default —
442
- * 4288MB in this container — and this module reads it at runtime rather than
443
- * assuming a number). It logged NOTHING about its own heap. When it died on
444
- * 2026-08-01/02 the only trace was V8's own stderr line:
445
- *
446
- * FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
447
- *
448
- * which carries no process name, no heap number and no history — the whole
449
- * server restarts and the evidence is gone. Four of those in ~15h, and the one
450
- * at 06:57:57 landed nine seconds after a viewer opened a WebRTC session, which
451
- * is why the app sat in a permanent "loading": the hub was dying as it
452
- * connected.
453
- *
454
- * `[mem]` reports already exist for forked addon runners (SIGUSR2 in
455
- * `addon-runner.ts`) — hub-main, the one process that actually OOMs, was the
456
- * one process with no telemetry at all.
457
- *
458
- * ## What it gives
459
- *
460
- * A heartbeat carrying the numbers that separate the two possible stories, and
461
- * a WARN as the heap approaches the ceiling — the line that will exist BEFORE
462
- * the next fatal instead of after it:
463
- *
464
- * - `heapUsedMb` climbing steadily between restarts → a leak;
465
- * - `heapUsedMb` flat with a sharp jump under load → a burst allocation, and
466
- * the timestamp says what was being served.
467
- *
468
- * `external`/`arrayBuffers` ride along because they are NOT bounded by
469
- * `--max-old-space-size` and are most of the gap between a 1.5GB cap and the
470
- * ~2.05GB RSS observed live: a diagnosis that only looks at the JS heap
471
- * misreads that process by 500MB.
472
- *
473
- * ## And then the heartbeat explained the "off-heap leak", which was not one
474
- *
475
- * 2026-08-08, measured on the live hub over 1019 consecutive samples and seven
476
- * separate boots: **hub-main's RSS is a HIGH-WATER MARK, not a leak.** In every
477
- * one of those 1019 samples RSS was >= the running maximum of
478
- * `heapTotal + external`, never once below it, and
479
- * `RSS_peak - peak(heapTotal + external)` landed between 874MB and 1639MB on
480
- * every boot — a roughly constant native floor on top of a number that only
481
- * ever goes up. RSS tracked the highest point the process ever reached and
482
- * stayed there: 3.3GB -> 4.5GB -> 5.2GB, and 7.8GB on the worst boot.
483
- *
484
- * Two things push that peak, and neither is a retained object:
485
- *
486
- * - the JS heap sawtooths to 2.0-2.8GB because the ceiling is 4288MB and
487
- * nothing creates pressure before then;
488
- * - `arrayBuffers` accumulates DEAD Buffers — up to 3.65GB — because V8 only
489
- * sweeps them on a major GC. Eleven collapses were captured, and every
490
- * single one coincides with `heapUsed` dropping to its ~650MB floor. No code
491
- * released a reference at 13:23:03; a mark-compact ran.
492
- *
493
- * At the peak V8 has committed the pages, and **an ordinary major GC does not
494
- * give them back.** Proven on the production hub: `smaps` showed 10,819 mapped
495
- * 256KB pages (V8's `kRegularPageSize`) holding 2704MB while V8 itself reported
496
- * `total_physical_size` of only 846MB — ~7,800 pages committed, resident, and
497
- * outside V8's own accounting. One `gc({ flavor: 'last-resort' })` took 417ms
498
- * and returned RSS 3355MB -> 1237MB, pages 10,819 -> 2,963.
499
- *
500
- * So the fix for the "~3GB/2h off-heap growth" is not a retainer hunt. It is to
501
- * ask V8 for the pages back when the process is visibly holding more than it is
502
- * using — which is what `shouldReclaim` decides and what the heartbeat now does.
503
- *
504
- * The cost is a stop-the-world on the hub's router process — 250-450ms when
505
- * first measured, 654ms on the largest pass seen since — bounded to once per
506
- * `HEAP_RECLAIM_MIN_INTERVAL_MS`. The alternative, measured, is a ratchet to
507
- * 7.8GB and the OOM that ends it.
508
- */
509
- /** Heartbeat period. Slow enough to be free, fast enough that the sample before
510
- * a fatal is recent. */
511
- var HEAP_WATCH_INTERVAL_MS = 6e4;
512
- /** Warn above this share of the heap ceiling. Chosen so the warning precedes
513
- * the fatal by more than one interval under realistic growth. */
514
- var HEAP_WATCH_WARN_RATIO = .8;
515
- /**
516
- * Escalate above this share of the ceiling.
517
- *
518
- * 0.70 is 3002MB: clear of the measured routine max (2847MB) so steady state
519
- * never escalates, and below {@link HEAP_WATCH_WARN_RATIO} so the fast cadence
520
- * is already running by the time the warn is reachable.
521
- */
522
- var HEAP_WATCH_ESCALATE_RATIO = .7;
523
- /**
524
- * Release escalation below this share.
525
- *
526
- * Hysteresis, not a second threshold: hub-main's heap sawtooths continuously,
527
- * so a single ratio would flap the mode on every GC. 0.60 is 2573MB — a real
528
- * retreat rather than the trough of one collection.
529
- */
530
- var HEAP_WATCH_DEESCALATE_RATIO = .6;
531
- /**
532
- * The cadence state machine, pure so the thresholds are testable without
533
- * allocating three gigabytes to reach them.
534
- */
535
- function nextMode(current, usedRatio, escalateRatio = HEAP_WATCH_ESCALATE_RATIO, deescalateRatio = HEAP_WATCH_DEESCALATE_RATIO) {
536
- if (current === "escalated") return usedRatio < deescalateRatio ? "steady" : "escalated";
537
- return usedRatio >= escalateRatio ? "escalated" : "steady";
538
- }
539
- /**
540
- * Reclaim once the process holds this many MB beyond what it is USING.
541
- *
542
- * Live calibration, both readings from the same hub minutes apart: 2491MB
543
- * stranded before the reclaim, 438MB after. 438MB is this process's irreducible
544
- * native floor (sqlite pages, libvips, sharp, thread stacks, node's own text) —
545
- * collecting against it would buy nothing and cost a stall every minute.
546
- *
547
- * ## Why this came down from 1536 (2026-08-17)
548
- *
549
- * The trigger was originally set to fire only on an unmistakable strand. It
550
- * does, and the pass on the record works exactly as designed:
551
- *
552
- * [mem] reclaim hub-main stranded=2365MB rss=4016MB→2368MB freed=1648MB took=654ms
553
- *
554
- * But the number it was protecting against changed. The host now runs with a
555
- * 24GiB cgroup ceiling and **no swap**, and is OOM-killing a neighbouring VM.
556
- * RSS this process is holding and not using is no longer merely untidy — it is
557
- * the headroom something else needed, and the OOM is triggered by the PEAK, not
558
- * by the average. A trigger of 1536 means the peak is allowed to stand 1.5GiB
559
- * above the floor before anything happens.
560
- *
561
- * 1024 is 2.3x the measured 438MB floor, so ordinary movement around the floor
562
- * can never trip it, and every pass that does fire still returns roughly
563
- * `1024 - 438` ≈ **580MB minimum** — a real return for a 250-650ms stall,
564
- * not a stall spent collecting noise.
565
- */
566
- var HEAP_RECLAIM_TRIGGER_MB = 1024;
567
- /**
568
- * Floor on how often the stop-the-world may run.
569
- *
570
- * Ten minutes was chosen to keep the worst case under 0.1% of the router's
571
- * time. The worst case is not what governs the cost, though: the reclaim only
572
- * runs when {@link shouldReclaim} says the process is genuinely stranded, and
573
- * after a pass it is at the ~438MB floor. Live off-heap growth was measured at
574
- * roughly 3GB/2h ≈ 25MB/min, so re-stranding the ~580MB needed to qualify again
575
- * takes on the order of **20 minutes** — well outside any floor considered
576
- * here. In steady state this constant therefore costs nothing at all.
577
- *
578
- * What it does govern is a BURST, which is the only case that matters on a host
579
- * with no swap: the ten-minute floor let a peak that re-formed in seconds stand
580
- * for ten minutes. Two minutes bounds that, and even the pathological case of a
581
- * process that re-strands instantly costs 654ms per 120s — 0.55% of the
582
- * router's time, against a ~1.5GiB peak it removes each pass.
583
- */
584
- var HEAP_RECLAIM_MIN_INTERVAL_MS = 12e4;
585
- var MB = (bytes) => Math.round(bytes / 1048576);
586
- /**
587
- * Build a sample from raw readings. Pure so the thresholds are testable without
588
- * allocating a gigabyte to reach them.
589
- */
590
- function buildHeapSample(mem, heapLimitBytes, warnRatio = HEAP_WATCH_WARN_RATIO) {
591
- const usedRatio = heapLimitBytes > 0 ? mem.heapUsed / heapLimitBytes : 0;
592
- return {
593
- rssMb: MB(mem.rss),
594
- heapUsedMb: MB(mem.heapUsed),
595
- heapTotalMb: MB(mem.heapTotal),
596
- heapLimitMb: MB(heapLimitBytes),
597
- externalMb: MB(mem.external),
598
- arrayBuffersMb: MB(mem.arrayBuffers),
599
- usedRatio: Math.round(usedRatio * 100) / 100,
600
- nearLimit: usedRatio >= warnRatio
601
- };
602
- }
603
- var consoleSink = {
604
- info: (line) => console.log(line),
605
- warn: (line) => console.warn(line)
606
- };
607
- /**
608
- * RSS the process is holding but not using.
609
- *
610
- * The subtrahend is heapUSED, not heapTotal, deliberately: the pages V8
611
- * committed at a sawtooth peak and no longer needs are counted inside heapTotal,
612
- * and they are precisely what this number exists to see.
613
- */
614
- function strandedMb(s) {
615
- return s.rssMb - s.heapUsedMb - s.externalMb;
616
- }
617
- /** Whether it is worth paying a stop-the-world to get those pages back. */
618
- function shouldReclaim(s, triggerMb = HEAP_RECLAIM_TRIGGER_MB) {
619
- return strandedMb(s) > triggerMb;
620
- }
621
- function isGcFunction(value) {
622
- return typeof value === "function";
623
- }
624
- /**
625
- * Build the reclaimer without requiring `--expose-gc` on argv.
626
- *
627
- * The flag is turned on, the function captured, and the flag turned straight
628
- * back off — a global `gc` left reachable by application code is a footgun, and
629
- * requiring an argv change would mean this cannot ship without a new image.
630
- * Verified against the live hub before being written: 266ms, RSS 1453 -> 1237.
631
- *
632
- * Returns undefined if V8 declines, so the caller keeps a heartbeat either way.
633
- */
634
- function createV8Reclaimer() {
635
- try {
636
- node_v8.setFlagsFromString("--expose-gc");
637
- const gc = node_vm.runInNewContext("gc");
638
- node_v8.setFlagsFromString("--no-expose-gc");
639
- if (!isGcFunction(gc)) return void 0;
640
- return () => gc({
641
- execution: "sync",
642
- flavor: "last-resort"
643
- });
644
- } catch {
645
- return;
646
- }
647
- }
648
- function format$1(label, s) {
649
- return `[mem] ${label} rss=${s.rssMb}MB heapUsed=${s.heapUsedMb}MB heapTotal=${s.heapTotalMb}MB heapLimit=${s.heapLimitMb}MB used=${Math.round(s.usedRatio * 100)}% external=${s.externalMb}MB arrayBuffers=${s.arrayBuffersMb}MB`;
650
- }
651
- /**
652
- * Start the heartbeat. Returns a stop function.
653
- *
654
- * `unref`ed so it can never hold the process open, and every read is wrapped:
655
- * a diagnostic that can crash the process it watches is worse than none.
656
- */
657
- function startHeapWatch(label = "hub-main", sink = consoleSink, intervalMs = HEAP_WATCH_INTERVAL_MS, reclaimOptions) {
658
- const readMemory = reclaimOptions?.readMemory ?? (() => process.memoryUsage());
659
- const now = reclaimOptions?.now ?? (() => Date.now());
660
- const triggerMb = reclaimOptions?.triggerMb ?? 1024;
661
- const minIntervalMs = reclaimOptions?.minIntervalMs ?? 12e4;
662
- const fastIntervalMs = reclaimOptions?.fastIntervalMs ?? 2e3;
663
- const escalateRatio = reclaimOptions?.escalateRatio ?? .7;
664
- const deescalateRatio = reclaimOptions?.deescalateRatio ?? .6;
665
- let lastReclaimAt = Number.NEGATIVE_INFINITY;
666
- const read = () => {
667
- const limit = reclaimOptions?.heapLimitBytes ?? node_v8.getHeapStatistics().heap_size_limit;
668
- return buildHeapSample(readMemory(), limit);
669
- };
670
- const maybeReclaim = (sample) => {
671
- if (reclaimOptions === void 0) return;
672
- if (!shouldReclaim(sample, triggerMb)) return;
673
- if (now() - lastReclaimAt < minIntervalMs) return;
674
- lastReclaimAt = now();
675
- const startedAt = now();
676
- try {
677
- reclaimOptions.reclaim();
678
- } catch (error) {
679
- sink.warn(`[mem] reclaim failed — ${error instanceof Error ? error.message : String(error)}`);
680
- return;
681
- }
682
- const after = read();
683
- sink.info(`[mem] reclaim ${label} stranded=${strandedMb(sample)}MB rss=${sample.rssMb}MB→${after.rssMb}MB freed=${sample.rssMb - after.rssMb}MB arrayBuffers=${sample.arrayBuffersMb}MB→${after.arrayBuffersMb}MB took=${now() - startedAt}ms`);
684
- };
685
- let mode = "steady";
686
- let lastLoggedAt = Number.NEGATIVE_INFINITY;
687
- /**
688
- * PROBING and LOGGING are separate cadences, and that separation is the whole
689
- * point.
690
- *
691
- * The 2026-08-08 fatal escaped a 60s heartbeat because the burst began and
692
- * ended between two samples. Deciding "is the heap elevated?" only when a
693
- * line is due would reproduce that exactly: the process would still be read
694
- * once a minute, and a 62-second climb would still be invisible. So the timer
695
- * runs at the FAST period always — `process.memoryUsage()` is tens of
696
- * microseconds, i.e. free at this rate — and the slow period governs only how
697
- * often a steady process is allowed to say so.
698
- *
699
- * Never probe slower than the log cadence, or a caller passing a short
700
- * `intervalMs` would silently get the long one.
701
- */
702
- const probeIntervalMs = Math.min(fastIntervalMs, intervalMs);
703
- const tick = () => {
704
- try {
705
- const sample = read();
706
- const previous = mode;
707
- mode = nextMode(previous, sample.usedRatio, escalateRatio, deescalateRatio);
708
- const at = now();
709
- const due = at - lastLoggedAt >= intervalMs;
710
- if (mode === "escalated" || due) {
711
- lastLoggedAt = at;
712
- const line = format$1(label, sample);
713
- if (sample.nearLimit) sink.warn(`${line} — APPROACHING HEAP LIMIT`);
714
- else if (mode === "escalated") sink.warn(`${line} — heap elevated, sampling every ${probeIntervalMs}ms`);
715
- else sink.info(line);
716
- }
717
- if (previous === "escalated" && mode === "steady") sink.info(`[mem] ${label} heap back to routine — logging every ${intervalMs}ms`);
718
- maybeReclaim(sample);
719
- } catch {}
720
- };
721
- const timer = setInterval(tick, probeIntervalMs);
722
- timer.unref?.();
723
- tick();
724
- let stopped = false;
725
- return () => {
726
- if (stopped) return;
727
- stopped = true;
728
- clearInterval(timer);
729
- };
730
- }
731
- //#endregion
732
432
  //#region src/lifecycle/lifecycle-state-machine.ts
733
433
  var VALID_TRANSITIONS = {
734
434
  stopped: ["starting", "disabled"],
@@ -94653,19 +94353,21 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
94653
94353
  function spawnRunner(runnerId, addons, env) {
94654
94354
  const nodeId = buildNodeId(runnerId);
94655
94355
  const runnerPath = node_path.resolve(__dirname, "addon-runner.js");
94356
+ const heavy = isHeavyRunner(addons);
94656
94357
  const childEnv = {
94657
94358
  ...process.env,
94658
94359
  CAMSTACK_RUNNER_ID: runnerId,
94659
94360
  CAMSTACK_RUNNER_ADDONS: JSON.stringify(addons),
94660
94361
  CAMSTACK_PARENT_NODE_ID: parentNodeId,
94661
94362
  CAMSTACK_DATA_DIR: dataDir,
94363
+ CAMSTACK_RUNNER_HEAP_PROFILE: heavy ? "heavy" : "light",
94662
94364
  CAMSTACK_LOG_LEVEL: process.env["CAMSTACK_LOG_LEVEL"] ?? "info",
94663
94365
  ...parentTcpPort !== void 0 ? { CAMSTACK_PARENT_TCP_PORT: String(parentTcpPort) } : {},
94664
94366
  ...parentUdsPath !== void 0 ? { CAMSTACK_PARENT_UDS_PATH: parentUdsPath } : {},
94665
94367
  ...env
94666
94368
  };
94667
94369
  const heapFlags = runnerHeapFlags(addons);
94668
- capturedBroker?.logger.info(`[${runnerId}] heap profile: ${isHeavyRunner(addons) ? "heavy" : "light"} flags=[${heapFlags.join(" ")}]`);
94370
+ capturedBroker?.logger.info(`[${runnerId}] heap profile: ${heavy ? "heavy" : "light"} flags=[${heapFlags.join(" ")}]`);
94669
94371
  const child = spawnFn(process.execPath, [...heapFlags, runnerPath], {
94670
94372
  env: childEnv,
94671
94373
  stdio: [
@@ -94709,6 +94411,15 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
94709
94411
  capturedBroker?.logger.error(`[${runnerId}] Crash circuit-breaker tripped — ${decision.crashesInWindow} crashes in ${CRASH_WINDOW_MS}ms. Not respawning.`);
94710
94412
  processes.set(runnerId, entry);
94711
94413
  if (capturedBroker) forceMoleculerDisconnect(capturedBroker, entry.nodeId);
94414
+ emitRunnerEvent(require_dist.EventCategory.AddonRunnerFailed, runnerId, {
94415
+ runnerId,
94416
+ nodeId: parentNodeId,
94417
+ addonIds: entry.runnerAddons.map((addon) => addon.addonId),
94418
+ crashesInWindow: decision.crashesInWindow,
94419
+ windowMs: CRASH_WINDOW_MS,
94420
+ ...code !== null ? { exitCode: code } : {},
94421
+ ...signal !== null ? { signal } : {}
94422
+ });
94712
94423
  for (const addon of entry.runnerAddons) emitRunnerEvent(require_dist.EventCategory.AddonCrashed, addon.addonId, {
94713
94424
  addonId: addon.addonId,
94714
94425
  error: `Crash circuit-breaker tripped after ${decision.crashesInWindow} crashes in ${CRASH_WINDOW_MS}ms`
@@ -95435,10 +95146,10 @@ exports.FsStorageBackend = FsStorageBackend;
95435
95146
  exports.HEALTH_MONITOR_GRACE_PERIOD_MS = HEALTH_MONITOR_GRACE_PERIOD_MS;
95436
95147
  exports.HEALTH_MONITOR_RETRY_INTERVALS_MS = HEALTH_MONITOR_RETRY_INTERVALS_MS;
95437
95148
  exports.HEALTH_MONITOR_TICK_MS = HEALTH_MONITOR_TICK_MS;
95438
- exports.HEAP_RECLAIM_MIN_INTERVAL_MS = HEAP_RECLAIM_MIN_INTERVAL_MS;
95439
- exports.HEAP_RECLAIM_TRIGGER_MB = HEAP_RECLAIM_TRIGGER_MB;
95440
- exports.HEAP_WATCH_INTERVAL_MS = HEAP_WATCH_INTERVAL_MS;
95441
- exports.HEAP_WATCH_WARN_RATIO = HEAP_WATCH_WARN_RATIO;
95149
+ exports.HEAP_RECLAIM_MIN_INTERVAL_MS = require_manifest_python_deps.HEAP_RECLAIM_MIN_INTERVAL_MS;
95150
+ exports.HEAP_RECLAIM_TRIGGER_MB = require_manifest_python_deps.HEAP_RECLAIM_TRIGGER_MB;
95151
+ exports.HEAP_WATCH_INTERVAL_MS = require_manifest_python_deps.HEAP_WATCH_INTERVAL_MS;
95152
+ exports.HEAP_WATCH_WARN_RATIO = require_manifest_python_deps.HEAP_WATCH_WARN_RATIO;
95442
95153
  exports.HUB_CAP_FWD_ACTION = require_manifest_python_deps.HUB_CAP_FWD_ACTION;
95443
95154
  exports.HUB_CAP_FWD_SERVICE = require_manifest_python_deps.HUB_CAP_FWD_SERVICE;
95444
95155
  exports.HubForwarderAddon = require_builtins_hub_forwarder_index.HubForwarderAddon$1;
@@ -95477,6 +95188,7 @@ exports.PipelineValidator = PipelineValidator;
95477
95188
  exports.PythonEnvManager = PythonEnvManager;
95478
95189
  exports.QUARANTINE_DIRNAME = QUARANTINE_DIRNAME;
95479
95190
  exports.RESTART_MARKER_FILE = RESTART_MARKER_FILE;
95191
+ exports.RUNNER_HEAP_WATCH_INTERVAL_MS = require_manifest_python_deps.RUNNER_HEAP_WATCH_INTERVAL_MS;
95480
95192
  exports.RUNTIME_DEFAULTS = require_dist.RUNTIME_DEFAULTS;
95481
95193
  exports.ReadinessRegistry = require_dist.ReadinessRegistry;
95482
95194
  exports.ReadinessTimeoutError = require_dist.ReadinessTimeoutError;
@@ -95515,7 +95227,7 @@ Object.defineProperty(exports, "buildBinaryPath", {
95515
95227
  }
95516
95228
  });
95517
95229
  exports.buildCapRouters = buildCapRouters;
95518
- exports.buildHeapSample = buildHeapSample;
95230
+ exports.buildHeapSample = require_manifest_python_deps.buildHeapSample;
95519
95231
  exports.buildLinkChain = require_manifest_python_deps.buildLinkChain;
95520
95232
  exports.buildNativeCapProxy = require_manifest_python_deps.buildNativeCapProxy;
95521
95233
  exports.buildNodeManifest = buildNodeManifest;
@@ -95560,7 +95272,7 @@ exports.createUdsEventBridge = require_manifest_python_deps.createUdsEventBridge
95560
95272
  exports.createUdsEventBus = require_manifest_python_deps.createUdsEventBus;
95561
95273
  exports.createUdsLogger = require_manifest_python_deps.createUdsLogger;
95562
95274
  exports.createUdsLoggerWithControl = require_manifest_python_deps.createUdsLoggerWithControl;
95563
- exports.createV8Reclaimer = createV8Reclaimer;
95275
+ exports.createV8Reclaimer = require_manifest_python_deps.createV8Reclaimer;
95564
95276
  exports.deleteModelFromDisk = require_file_data_plane.deleteModelFromDisk;
95565
95277
  exports.deriveAgentListenPort = deriveAgentListenPort;
95566
95278
  exports.describeProviderKindDrift = describeProviderKindDrift;
@@ -95686,9 +95398,10 @@ exports.selectAddonResidue = selectAddonResidue;
95686
95398
  exports.serializeTypedArrays = require_manifest_python_deps.serializeTypedArrays;
95687
95399
  exports.setHubConnected = require_manifest_python_deps.setHubConnected;
95688
95400
  exports.setNodeEventInterest = require_manifest_python_deps.setNodeEventInterest;
95689
- exports.shouldReclaim = shouldReclaim;
95690
- exports.startHeapWatch = startHeapWatch;
95691
- exports.strandedMb = strandedMb;
95401
+ exports.shouldReclaim = require_manifest_python_deps.shouldReclaim;
95402
+ exports.startHeapWatch = require_manifest_python_deps.startHeapWatch;
95403
+ exports.startRunnerHeapWatch = require_manifest_python_deps.startRunnerHeapWatch;
95404
+ exports.strandedMb = require_manifest_python_deps.strandedMb;
95692
95405
  exports.stripCamstackDeps = stripCamstackDeps;
95693
95406
  exports.subscribePassthrough = require_manifest_python_deps.subscribePassthrough;
95694
95407
  exports.udsChildLogToWorkerEntry = require_manifest_python_deps.udsChildLogToWorkerEntry;