@camstack/addon-pipeline 1.2.93 → 1.2.95
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.
- package/dist/{addon-utils-CTRpEiSd.js → addon-utils-UMWiMAxR.js} +1 -1
- package/dist/audio-analyzer/index.js +58 -4
- package/dist/audio-analyzer/index.mjs +56 -2
- package/dist/detection-pipeline/index.js +194 -6
- package/dist/detection-pipeline/index.mjs +191 -3
- package/dist/{dist-6POVIYmC.mjs → dist-C49o1k7u.mjs} +359 -21
- package/dist/{dist-D9To_r_d.js → dist-hyryByiw.js} +376 -20
- package/dist/{event-loop-stall-monitor-C-VvWOxS.mjs → event-loop-stall-monitor-CfxZBgNi.mjs} +1 -1
- package/dist/{event-loop-stall-monitor-CGacbzXq.js → event-loop-stall-monitor-DPNTzGOT.js} +1 -1
- package/dist/{lazy-sharp-C3zOnmiU.js → lazy-sharp-Dxg6UeW-.js} +1 -1
- package/dist/motion-wasm/index.js +2 -2
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +195 -14
- package/dist/pipeline-runner/index.mjs +194 -13
- package/dist/process-memory-B9-UD7Rp.js +78 -0
- package/dist/process-memory-DXOB6eHX.mjs +66 -0
- package/dist/recorder/index.js +943 -109
- package/dist/recorder/index.mjs +942 -108
- package/dist/session-decode/decode-worker-child.js +4 -4
- package/dist/session-decode/decode-worker-child.mjs +3 -3
- package/dist/stream-broker/_stub.js +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-C2woB6bY.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CF-c0cDZ.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BjvmJK_d.mjs +26 -0
- package/dist/stream-broker/{hostInit-VCURcZLt.mjs → hostInit-Cpcf2hDB.mjs} +3 -3
- package/dist/stream-broker/index.js +25 -7
- package/dist/stream-broker/index.mjs +25 -7
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-Bq28qobd.js → worker-protocol-DR_a77D5.js} +1 -1
- package/dist/{worker-protocol-PzAKg8mC.mjs → worker-protocol-DqQ2GI4M.mjs} +1 -1
- package/package.json +13 -7
- package/python/inference_pool.py +178 -3
- package/python/test_inference_pool_backpressure.py +7 -1
- package/python/test_inference_pool_memstats.py +146 -0
- package/dist/node-topology-platform-BkR_k6WT.mjs +0 -15
- package/dist/node-topology-platform-CFZ7F4xW.js +0 -20
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Dut56EeQ.mjs +0 -26
|
@@ -22,7 +22,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
//#endregion
|
|
24
24
|
let node_crypto = require("node:crypto");
|
|
25
|
-
//#region ../types/dist/event-category-
|
|
25
|
+
//#region ../types/dist/event-category-XfKNtfCc.mjs
|
|
26
26
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
27
27
|
EventCategory["SystemBoot"] = "system.boot";
|
|
28
28
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -61,6 +61,22 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
61
61
|
EventCategory["AddonInstalled"] = "addon.installed";
|
|
62
62
|
EventCategory["AddonUninstalled"] = "addon.uninstalled";
|
|
63
63
|
EventCategory["AddonCrashed"] = "addon.crashed";
|
|
64
|
+
/**
|
|
65
|
+
* A RUNNER the D6 crash circuit-breaker gave up on — terminal.
|
|
66
|
+
*
|
|
67
|
+
* `AddonCrashed` is the routine, self-healing fact ("it crashed; it is being
|
|
68
|
+
* respawned"); this is the one that never resolves itself. Emitted exactly
|
|
69
|
+
* once per trip, by `process-service.ts`, carrying the node, the runner, the
|
|
70
|
+
* addons it hosted and the crash count that tripped the breaker.
|
|
71
|
+
*
|
|
72
|
+
* It exists because a runner marked terminally `failed` used to be SILENT:
|
|
73
|
+
* on 2026-08-18 the `recorder` runner died of three unhandled ffmpeg spawn
|
|
74
|
+
* errors, the breaker stopped respawning it (correctly), `/health` kept
|
|
75
|
+
* returning 200, and fleet-wide recording was gone for 3h15 before the
|
|
76
|
+
* operator's phone told him. The Notification Center's system-event intake
|
|
77
|
+
* consumes this category and maps it to the `addon-crash-loop` kind.
|
|
78
|
+
*/
|
|
79
|
+
EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
|
|
64
80
|
EventCategory["AddonError"] = "addon.error";
|
|
65
81
|
EventCategory["AddonPageReady"] = "addon.page-ready";
|
|
66
82
|
EventCategory["AddonWidgetReady"] = "addon.widget-ready";
|
|
@@ -16094,6 +16110,7 @@ var NcSystemEventKindSchema = _enum([
|
|
|
16094
16110
|
"node-offline",
|
|
16095
16111
|
"node-inference-unavailable",
|
|
16096
16112
|
"detection-blind",
|
|
16113
|
+
"addon-crash-loop",
|
|
16097
16114
|
"addon-update-available",
|
|
16098
16115
|
"server-update-available",
|
|
16099
16116
|
"alarm-triggered",
|
|
@@ -21996,20 +22013,6 @@ var VectorStatsResultSchema = object({
|
|
|
21996
22013
|
exact: boolean()
|
|
21997
22014
|
});
|
|
21998
22015
|
method(VectorDeclareIndexInputSchema, _void(), { kind: "mutation" }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, { kind: "mutation" }), method(VectorQueryInputSchema, VectorQueryResultSchema), method(VectorGetInputSchema, VectorGetResultSchema), method(VectorDeleteInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorStatsInputSchema, VectorStatsResultSchema);
|
|
21999
|
-
/**
|
|
22000
|
-
* `videoclips` — the unified, navigable-clip surface for a camera.
|
|
22001
|
-
*
|
|
22002
|
-
* A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
|
|
22003
|
-
* provider per device, substitutable. The DEFAULT provider (registered by
|
|
22004
|
-
* `addon-post-analysis`, `defaultActive: true`) composes the analytics event
|
|
22005
|
-
* log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
|
|
22006
|
-
* is a time-WINDOW over existing footage, never a separate file. A camera that
|
|
22007
|
-
* exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
|
|
22008
|
-
* wrapper provider for its device and serve its own clip catalog + URLs.
|
|
22009
|
-
*
|
|
22010
|
-
* A `Clip` is purely time-based (subtree-blind): playback resolves segments by
|
|
22011
|
-
* temporal overlap, so the API never decides `continuous` vs `events`.
|
|
22012
|
-
*/
|
|
22013
22016
|
var ClipSchema = object({
|
|
22014
22017
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
22015
22018
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -22035,12 +22038,41 @@ var ClipSchema = object({
|
|
|
22035
22038
|
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
22036
22039
|
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
22037
22040
|
* mint their own stills.
|
|
22041
|
+
*
|
|
22042
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
22043
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
22044
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
22045
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
22046
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
22047
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
22038
22048
|
*/
|
|
22039
22049
|
thumbnail: string().optional(),
|
|
22040
|
-
/**
|
|
22041
|
-
*
|
|
22042
|
-
*
|
|
22050
|
+
/**
|
|
22051
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
22052
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
22053
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
22054
|
+
*
|
|
22055
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
22056
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
22057
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
22058
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
22059
|
+
* construction, so a naive midpoint lands in dead air.
|
|
22060
|
+
*/
|
|
22061
|
+
stillAtMs: number().optional(),
|
|
22062
|
+
/**
|
|
22063
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
22064
|
+
* first within kind (object → motion → audio), capped at
|
|
22065
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
22066
|
+
*
|
|
22067
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
22068
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
22069
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
22070
|
+
*/
|
|
22043
22071
|
eventIds: array(string()).optional(),
|
|
22072
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
22073
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
22074
|
+
* list is never mistaken for a quiet visit. */
|
|
22075
|
+
eventCount: number().int().nonnegative().optional(),
|
|
22044
22076
|
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
22045
22077
|
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
22046
22078
|
* bar keeps showing them via `recording.getAvailability`. */
|
|
@@ -22591,7 +22623,14 @@ var SearchResultSchema = object({
|
|
|
22591
22623
|
});
|
|
22592
22624
|
var AutoUpdateSettingsSchema = object({
|
|
22593
22625
|
channel: ChannelSchema,
|
|
22594
|
-
intervalSeconds: number()
|
|
22626
|
+
intervalSeconds: number(),
|
|
22627
|
+
/**
|
|
22628
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
22629
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
22630
|
+
* about a publish and installing it are different decisions. Clamped
|
|
22631
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
22632
|
+
*/
|
|
22633
|
+
updateCheckIntervalSeconds: number()
|
|
22595
22634
|
});
|
|
22596
22635
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
22597
22636
|
var RestartAddonResultSchema = unknown();
|
|
@@ -22732,7 +22771,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
22732
22771
|
auth: "admin"
|
|
22733
22772
|
}), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
|
|
22734
22773
|
channel: ChannelSchema,
|
|
22735
|
-
intervalSeconds: number().min(300).max(86400).optional()
|
|
22774
|
+
intervalSeconds: number().min(300).max(86400).optional(),
|
|
22775
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
22776
|
+
updateCheckIntervalSeconds: number().min(900).max(604800).optional()
|
|
22736
22777
|
}), unknown(), {
|
|
22737
22778
|
kind: "mutation",
|
|
22738
22779
|
auth: "admin"
|
|
@@ -36424,6 +36465,303 @@ function maskUrlCredentials(rawUrl) {
|
|
|
36424
36465
|
return rawUrl;
|
|
36425
36466
|
}
|
|
36426
36467
|
}
|
|
36468
|
+
var MB = 1024 * 1024;
|
|
36469
|
+
var DEFAULT_POOL_MEMORY_POLICY = {
|
|
36470
|
+
sampleIntervalMs: 6e4,
|
|
36471
|
+
baselineSettleSamples: 5,
|
|
36472
|
+
baselineSampleCount: 3,
|
|
36473
|
+
restartMultiple: 4,
|
|
36474
|
+
floorBytes: 1024 * MB,
|
|
36475
|
+
ceilingBytes: 3072 * MB,
|
|
36476
|
+
cooldownMs: 30 * 6e4,
|
|
36477
|
+
maxRestartsPerWindow: 6,
|
|
36478
|
+
restartWindowMs: 1440 * 6e4
|
|
36479
|
+
};
|
|
36480
|
+
/**
|
|
36481
|
+
* Resolve the policy from env overrides (`CAMSTACK_POOL_MEM_*`). Garbage or
|
|
36482
|
+
* absent values keep the default — an operator typo must never disable the
|
|
36483
|
+
* bound or set it to zero.
|
|
36484
|
+
*/
|
|
36485
|
+
function resolvePoolMemoryPolicy(env) {
|
|
36486
|
+
const num = (key, fallback, min) => {
|
|
36487
|
+
const raw = env[key];
|
|
36488
|
+
if (raw === void 0) return fallback;
|
|
36489
|
+
const parsed = Number(raw);
|
|
36490
|
+
return Number.isFinite(parsed) && parsed >= min ? parsed : fallback;
|
|
36491
|
+
};
|
|
36492
|
+
const d = DEFAULT_POOL_MEMORY_POLICY;
|
|
36493
|
+
return {
|
|
36494
|
+
sampleIntervalMs: num("CAMSTACK_POOL_MEM_INTERVAL_MS", d.sampleIntervalMs, 5e3),
|
|
36495
|
+
baselineSettleSamples: d.baselineSettleSamples,
|
|
36496
|
+
baselineSampleCount: d.baselineSampleCount,
|
|
36497
|
+
restartMultiple: num("CAMSTACK_POOL_MEM_MULTIPLE", d.restartMultiple, 1.5),
|
|
36498
|
+
floorBytes: num("CAMSTACK_POOL_MEM_FLOOR_MB", d.floorBytes / MB, 128) * MB,
|
|
36499
|
+
ceilingBytes: num("CAMSTACK_POOL_MEM_CEILING_MB", d.ceilingBytes / MB, 256) * MB,
|
|
36500
|
+
cooldownMs: num("CAMSTACK_POOL_MEM_COOLDOWN_MS", d.cooldownMs, 6e4),
|
|
36501
|
+
maxRestartsPerWindow: num("CAMSTACK_POOL_MEM_MAX_RESTARTS", d.maxRestartsPerWindow, 1),
|
|
36502
|
+
restartWindowMs: num("CAMSTACK_POOL_MEM_RESTART_WINDOW_MS", d.restartWindowMs, 6e4)
|
|
36503
|
+
};
|
|
36504
|
+
}
|
|
36505
|
+
/** Parse the fields this watchdog needs out of `/proc/<pid>/status` text.
|
|
36506
|
+
* Returns null when VmRSS is missing (dead pid, kernel thread, bad read). */
|
|
36507
|
+
function parseProcStatus(text) {
|
|
36508
|
+
const kb = (label) => {
|
|
36509
|
+
const match = text.match(new RegExp(`^${label}:\\s+(\\d+)\\s*kB`, "m"));
|
|
36510
|
+
return match ? Number(match[1]) * 1024 : null;
|
|
36511
|
+
};
|
|
36512
|
+
const rssBytes = kb("VmRSS");
|
|
36513
|
+
if (rssBytes === null) return null;
|
|
36514
|
+
const threadsMatch = text.match(/^Threads:\s+(\d+)/m);
|
|
36515
|
+
return {
|
|
36516
|
+
rssBytes,
|
|
36517
|
+
vmBytes: kb("VmSize") ?? 0,
|
|
36518
|
+
hwmBytes: kb("VmHWM") ?? 0,
|
|
36519
|
+
swapBytes: kb("VmSwap") ?? 0,
|
|
36520
|
+
threads: threadsMatch ? Number(threadsMatch[1]) : 0
|
|
36521
|
+
};
|
|
36522
|
+
}
|
|
36523
|
+
function initialPoolMemoryState() {
|
|
36524
|
+
return {
|
|
36525
|
+
settleSeen: 0,
|
|
36526
|
+
baselineWindow: [],
|
|
36527
|
+
baselineBytes: null,
|
|
36528
|
+
restartsAt: [],
|
|
36529
|
+
lastRestartAt: null
|
|
36530
|
+
};
|
|
36531
|
+
}
|
|
36532
|
+
function median(values) {
|
|
36533
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
36534
|
+
return sorted[Math.floor(sorted.length / 2)];
|
|
36535
|
+
}
|
|
36536
|
+
function advanceBaseline(state, rssBytes, policy) {
|
|
36537
|
+
if (state.baselineBytes !== null) return state;
|
|
36538
|
+
if (state.settleSeen < policy.baselineSettleSamples) return {
|
|
36539
|
+
...state,
|
|
36540
|
+
settleSeen: state.settleSeen + 1
|
|
36541
|
+
};
|
|
36542
|
+
const window = [...state.baselineWindow, rssBytes];
|
|
36543
|
+
if (window.length < policy.baselineSampleCount) return {
|
|
36544
|
+
...state,
|
|
36545
|
+
baselineWindow: window
|
|
36546
|
+
};
|
|
36547
|
+
return {
|
|
36548
|
+
...state,
|
|
36549
|
+
baselineWindow: window,
|
|
36550
|
+
baselineBytes: median(window)
|
|
36551
|
+
};
|
|
36552
|
+
}
|
|
36553
|
+
/** The one place a pool's restart threshold is computed. */
|
|
36554
|
+
function poolMemoryThreshold(baselineBytes, policy) {
|
|
36555
|
+
if (baselineBytes === null) return policy.ceilingBytes;
|
|
36556
|
+
return Math.min(policy.ceilingBytes, Math.max(policy.floorBytes, policy.restartMultiple * baselineBytes));
|
|
36557
|
+
}
|
|
36558
|
+
function pruneRestarts(restartsAt, nowMs, policy) {
|
|
36559
|
+
return restartsAt.filter((t) => nowMs - t < policy.restartWindowMs);
|
|
36560
|
+
}
|
|
36561
|
+
/**
|
|
36562
|
+
* Evaluate one RSS sample. Pure: returns the successor state plus the verdict.
|
|
36563
|
+
* The caller performs (and logs) the restart, then commits it via
|
|
36564
|
+
* {@link commitWatchdogRestart}.
|
|
36565
|
+
*/
|
|
36566
|
+
function evaluatePoolMemory(state, rssBytes, nowMs, policy) {
|
|
36567
|
+
const next = advanceBaseline(state, rssBytes, policy);
|
|
36568
|
+
const thresholdBytes = poolMemoryThreshold(next.baselineBytes, policy);
|
|
36569
|
+
if (rssBytes <= thresholdBytes) return {
|
|
36570
|
+
state: next,
|
|
36571
|
+
action: next.baselineBytes === null ? "baseline-pending" : "ok",
|
|
36572
|
+
baselineBytes: next.baselineBytes,
|
|
36573
|
+
thresholdBytes
|
|
36574
|
+
};
|
|
36575
|
+
const inWindow = pruneRestarts(next.restartsAt, nowMs, policy);
|
|
36576
|
+
const pruned = {
|
|
36577
|
+
...next,
|
|
36578
|
+
restartsAt: inWindow
|
|
36579
|
+
};
|
|
36580
|
+
if (inWindow.length >= policy.maxRestartsPerWindow) return {
|
|
36581
|
+
state: pruned,
|
|
36582
|
+
action: "exhausted",
|
|
36583
|
+
baselineBytes: next.baselineBytes,
|
|
36584
|
+
thresholdBytes
|
|
36585
|
+
};
|
|
36586
|
+
if (pruned.lastRestartAt !== null && nowMs - pruned.lastRestartAt < policy.cooldownMs) return {
|
|
36587
|
+
state: pruned,
|
|
36588
|
+
action: "cooldown",
|
|
36589
|
+
baselineBytes: next.baselineBytes,
|
|
36590
|
+
thresholdBytes
|
|
36591
|
+
};
|
|
36592
|
+
return {
|
|
36593
|
+
state: pruned,
|
|
36594
|
+
action: "restart",
|
|
36595
|
+
baselineBytes: next.baselineBytes,
|
|
36596
|
+
thresholdBytes
|
|
36597
|
+
};
|
|
36598
|
+
}
|
|
36599
|
+
/** Record a performed watchdog restart: budget consumed, baseline reset —
|
|
36600
|
+
* the recreated pool is a NEW process and gets a fresh settle window. */
|
|
36601
|
+
function commitWatchdogRestart(state, nowMs) {
|
|
36602
|
+
return {
|
|
36603
|
+
...initialPoolMemoryState(),
|
|
36604
|
+
restartsAt: [...state.restartsAt, nowMs],
|
|
36605
|
+
lastRestartAt: nowMs
|
|
36606
|
+
};
|
|
36607
|
+
}
|
|
36608
|
+
/** Reset baseline tracking (pool process replaced OUTSIDE the watchdog —
|
|
36609
|
+
* kernel OOM kill + respawn, idle-reaper eviction, tuning respawn). The
|
|
36610
|
+
* restart budget is kept: it bounds the WATCHDOG, not the pool. */
|
|
36611
|
+
function resetPoolBaseline(state) {
|
|
36612
|
+
return {
|
|
36613
|
+
...initialPoolMemoryState(),
|
|
36614
|
+
restartsAt: state.restartsAt,
|
|
36615
|
+
lastRestartAt: state.lastRestartAt
|
|
36616
|
+
};
|
|
36617
|
+
}
|
|
36618
|
+
/**
|
|
36619
|
+
* Pick ONE pool to restart this sweep — the worst overage ratio. Restarting
|
|
36620
|
+
* several pools at once (e.g. after a model rollout grew every baseline)
|
|
36621
|
+
* would take detection down on every camera simultaneously; one per sweep
|
|
36622
|
+
* means the survivors keep serving while the worst offender reloads, and the
|
|
36623
|
+
* next sweep (one interval later) takes the next one.
|
|
36624
|
+
*/
|
|
36625
|
+
function pickRestartCandidate(candidates) {
|
|
36626
|
+
if (candidates.length === 0) return null;
|
|
36627
|
+
let worst = candidates[0];
|
|
36628
|
+
for (const c of candidates) if (c.rssBytes / c.thresholdBytes > worst.rssBytes / worst.thresholdBytes) worst = c;
|
|
36629
|
+
return worst.key;
|
|
36630
|
+
}
|
|
36631
|
+
var PoolMemoryWatchdog = class {
|
|
36632
|
+
pools = /* @__PURE__ */ new Map();
|
|
36633
|
+
timer = null;
|
|
36634
|
+
sweeping = false;
|
|
36635
|
+
stopped = false;
|
|
36636
|
+
opts;
|
|
36637
|
+
now;
|
|
36638
|
+
setTimer;
|
|
36639
|
+
clearTimer;
|
|
36640
|
+
constructor(opts) {
|
|
36641
|
+
this.opts = opts;
|
|
36642
|
+
this.now = opts.now ?? (() => Date.now());
|
|
36643
|
+
this.setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
36644
|
+
this.clearTimer = opts.clearTimer ?? ((t) => clearTimeout(t));
|
|
36645
|
+
}
|
|
36646
|
+
start() {
|
|
36647
|
+
if (this.timer || this.stopped) return;
|
|
36648
|
+
this.arm();
|
|
36649
|
+
}
|
|
36650
|
+
stop() {
|
|
36651
|
+
this.stopped = true;
|
|
36652
|
+
if (this.timer) {
|
|
36653
|
+
this.clearTimer(this.timer);
|
|
36654
|
+
this.timer = null;
|
|
36655
|
+
}
|
|
36656
|
+
}
|
|
36657
|
+
arm() {
|
|
36658
|
+
const timer = this.setTimer(() => {
|
|
36659
|
+
this.sweep().finally(() => {
|
|
36660
|
+
if (!this.stopped) this.arm();
|
|
36661
|
+
});
|
|
36662
|
+
}, this.opts.policy.sampleIntervalMs);
|
|
36663
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
36664
|
+
this.timer = timer;
|
|
36665
|
+
}
|
|
36666
|
+
/** One sweep: sample → log every pool → restart at most one. Public so the
|
|
36667
|
+
* loop is testable without fake global timers. */
|
|
36668
|
+
async sweep() {
|
|
36669
|
+
if (this.sweeping) return;
|
|
36670
|
+
this.sweeping = true;
|
|
36671
|
+
try {
|
|
36672
|
+
await this.doSweep();
|
|
36673
|
+
} catch (err) {
|
|
36674
|
+
this.opts.log.warn("pool memory sweep failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
36675
|
+
} finally {
|
|
36676
|
+
this.sweeping = false;
|
|
36677
|
+
}
|
|
36678
|
+
}
|
|
36679
|
+
async doSweep() {
|
|
36680
|
+
const nowMs = this.now();
|
|
36681
|
+
const samples = await this.opts.sample();
|
|
36682
|
+
const candidates = [];
|
|
36683
|
+
const verdictByKey = /* @__PURE__ */ new Map();
|
|
36684
|
+
for (const t of samples) {
|
|
36685
|
+
const tracked = this.pools.get(t.key);
|
|
36686
|
+
let state = tracked?.state ?? initialPoolMemoryState();
|
|
36687
|
+
if (tracked && tracked.pids.length > 0 && !samePids(tracked.pids, t.pids)) {
|
|
36688
|
+
this.opts.log.warn("pool process changed outside the watchdog — re-baselining", { meta: {
|
|
36689
|
+
poolKey: t.key,
|
|
36690
|
+
previousPids: tracked.pids,
|
|
36691
|
+
pids: t.pids
|
|
36692
|
+
} });
|
|
36693
|
+
state = resetPoolBaseline(state);
|
|
36694
|
+
}
|
|
36695
|
+
const verdict = evaluatePoolMemory(state, t.rssBytes, nowMs, this.opts.policy);
|
|
36696
|
+
this.pools.set(t.key, {
|
|
36697
|
+
state: verdict.state,
|
|
36698
|
+
pids: t.pids
|
|
36699
|
+
});
|
|
36700
|
+
verdictByKey.set(t.key, verdict);
|
|
36701
|
+
this.opts.log.info("pool memory", { meta: {
|
|
36702
|
+
poolKey: t.key,
|
|
36703
|
+
pids: t.pids,
|
|
36704
|
+
rssMb: Math.round(t.rssBytes / MB),
|
|
36705
|
+
baselineMb: verdict.baselineBytes === null ? null : Math.round(verdict.baselineBytes / MB),
|
|
36706
|
+
thresholdMb: Math.round(verdict.thresholdBytes / MB),
|
|
36707
|
+
action: verdict.action,
|
|
36708
|
+
...t.meta
|
|
36709
|
+
} });
|
|
36710
|
+
if (verdict.action === "restart") candidates.push({
|
|
36711
|
+
key: t.key,
|
|
36712
|
+
rssBytes: t.rssBytes,
|
|
36713
|
+
thresholdBytes: verdict.thresholdBytes
|
|
36714
|
+
});
|
|
36715
|
+
else if (verdict.action === "cooldown") this.opts.log.warn("pool over memory threshold but inside restart cooldown", { meta: {
|
|
36716
|
+
poolKey: t.key,
|
|
36717
|
+
rssMb: Math.round(t.rssBytes / MB),
|
|
36718
|
+
thresholdMb: Math.round(verdict.thresholdBytes / MB),
|
|
36719
|
+
cooldownMs: this.opts.policy.cooldownMs
|
|
36720
|
+
} });
|
|
36721
|
+
else if (verdict.action === "exhausted") this.opts.log.error("pool memory watchdog gave up — restart budget exhausted, pool leaks faster than restarts can pay for; needs a heap profile (py-spy / tracemalloc)", { meta: {
|
|
36722
|
+
poolKey: t.key,
|
|
36723
|
+
rssMb: Math.round(t.rssBytes / MB),
|
|
36724
|
+
thresholdMb: Math.round(verdict.thresholdBytes / MB),
|
|
36725
|
+
maxRestartsPerWindow: this.opts.policy.maxRestartsPerWindow,
|
|
36726
|
+
restartWindowMs: this.opts.policy.restartWindowMs
|
|
36727
|
+
} });
|
|
36728
|
+
}
|
|
36729
|
+
for (const key of [...this.pools.keys()]) if (!samples.some((t) => t.key === key)) this.pools.delete(key);
|
|
36730
|
+
const pickedKey = pickRestartCandidate(candidates);
|
|
36731
|
+
if (pickedKey === null) return;
|
|
36732
|
+
const picked = candidates.find((c) => c.key === pickedKey);
|
|
36733
|
+
const verdict = verdictByKey.get(pickedKey);
|
|
36734
|
+
const tracked = this.pools.get(pickedKey);
|
|
36735
|
+
const restartsInWindow = tracked.state.restartsAt.length + 1;
|
|
36736
|
+
this.opts.log.warn("pool memory watchdog restarting pool — RSS over threshold", { meta: {
|
|
36737
|
+
poolKey: pickedKey,
|
|
36738
|
+
pids: tracked.pids,
|
|
36739
|
+
rssMb: Math.round(picked.rssBytes / MB),
|
|
36740
|
+
thresholdMb: Math.round(picked.thresholdBytes / MB),
|
|
36741
|
+
baselineMb: verdict.baselineBytes === null ? null : Math.round(verdict.baselineBytes / MB),
|
|
36742
|
+
restartsInWindow,
|
|
36743
|
+
deferredCandidates: candidates.filter((c) => c.key !== pickedKey).map((c) => c.key)
|
|
36744
|
+
} });
|
|
36745
|
+
try {
|
|
36746
|
+
await this.opts.restart(pickedKey);
|
|
36747
|
+
this.pools.set(pickedKey, {
|
|
36748
|
+
state: commitWatchdogRestart(tracked.state, this.now()),
|
|
36749
|
+
pids: []
|
|
36750
|
+
});
|
|
36751
|
+
} catch (err) {
|
|
36752
|
+
this.opts.log.error("pool memory watchdog restart failed", { meta: {
|
|
36753
|
+
poolKey: pickedKey,
|
|
36754
|
+
error: err instanceof Error ? err.message : String(err)
|
|
36755
|
+
} });
|
|
36756
|
+
}
|
|
36757
|
+
}
|
|
36758
|
+
};
|
|
36759
|
+
function samePids(a, b) {
|
|
36760
|
+
if (a.length !== b.length) return false;
|
|
36761
|
+
const sortedA = [...a].sort((x, y) => x - y);
|
|
36762
|
+
const sortedB = [...b].sort((x, y) => x - y);
|
|
36763
|
+
return sortedA.every((v, i) => v === sortedB[i]);
|
|
36764
|
+
}
|
|
36427
36765
|
/**
|
|
36428
36766
|
* Fixed-capacity ring buffer. When full, push() overwrites the oldest entry.
|
|
36429
36767
|
* drain() returns up to maxCount items in FIFO order and removes them.
|
|
@@ -36911,6 +37249,12 @@ Object.defineProperty(exports, "OpsLogEntrySchema", {
|
|
|
36911
37249
|
return OpsLogEntrySchema;
|
|
36912
37250
|
}
|
|
36913
37251
|
});
|
|
37252
|
+
Object.defineProperty(exports, "PoolMemoryWatchdog", {
|
|
37253
|
+
enumerable: true,
|
|
37254
|
+
get: function() {
|
|
37255
|
+
return PoolMemoryWatchdog;
|
|
37256
|
+
}
|
|
37257
|
+
});
|
|
36914
37258
|
Object.defineProperty(exports, "RATE_CONTROL_RELAXED", {
|
|
36915
37259
|
enumerable: true,
|
|
36916
37260
|
get: function() {
|
|
@@ -37199,6 +37543,12 @@ Object.defineProperty(exports, "parseJsonUnknown", {
|
|
|
37199
37543
|
return parseJsonUnknown;
|
|
37200
37544
|
}
|
|
37201
37545
|
});
|
|
37546
|
+
Object.defineProperty(exports, "parseProcStatus", {
|
|
37547
|
+
enumerable: true,
|
|
37548
|
+
get: function() {
|
|
37549
|
+
return parseProcStatus;
|
|
37550
|
+
}
|
|
37551
|
+
});
|
|
37202
37552
|
Object.defineProperty(exports, "parseProfileBrokerId", {
|
|
37203
37553
|
enumerable: true,
|
|
37204
37554
|
get: function() {
|
|
@@ -37253,6 +37603,12 @@ Object.defineProperty(exports, "resolveEgressDecodeHwAccel", {
|
|
|
37253
37603
|
return resolveEgressDecodeHwAccel;
|
|
37254
37604
|
}
|
|
37255
37605
|
});
|
|
37606
|
+
Object.defineProperty(exports, "resolvePoolMemoryPolicy", {
|
|
37607
|
+
enumerable: true,
|
|
37608
|
+
get: function() {
|
|
37609
|
+
return resolvePoolMemoryPolicy;
|
|
37610
|
+
}
|
|
37611
|
+
});
|
|
37256
37612
|
Object.defineProperty(exports, "resolveRecordingProfiles", {
|
|
37257
37613
|
enumerable: true,
|
|
37258
37614
|
get: function() {
|
package/dist/{event-loop-stall-monitor-C-VvWOxS.mjs → event-loop-stall-monitor-CfxZBgNi.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { U as hfModelUrl, a as COCO_80_LABELS, o as COCO_TO_MACRO, r as AUDIO_MACRO_LABELS } from "./dist-C49o1k7u.mjs";
|
|
2
2
|
import { PerformanceObserver } from "node:perf_hooks";
|
|
3
3
|
//#region src/detection-pipeline/registry/model-catalogs.ts
|
|
4
4
|
var HF_REPO = "camstack/camstack-models";
|
|
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_lazy_sharp = require("../lazy-sharp-
|
|
5
|
+
const require_dist = require("../dist-hyryByiw.js");
|
|
6
|
+
const require_lazy_sharp = require("../lazy-sharp-Dxg6UeW-.js");
|
|
7
7
|
let node_path = require("node:path");
|
|
8
8
|
let node_fs = require("node:fs");
|
|
9
9
|
//#region src/motion-wasm/wasm-motion-detector.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { H as evaluateZoneRules, K as motionDetectionCapability, _t as DeviceType, mt as BaseAddon, yt as hydrateSchema } from "../dist-C49o1k7u.mjs";
|
|
2
2
|
import { t as getSharp } from "../lazy-sharp-6oymT_yf.mjs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { readFileSync } from "node:fs";
|