@camstack/addon-pipeline 1.2.48 → 1.2.50
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/{model-download-service-D8B-4ktF-7-YxE9Wx.js → addon-utils-DveoBR6G.js} +219 -3
- package/dist/{model-download-service-D8B-4ktF-BB7oZL3y.mjs → addon-utils-QuxtOdK1.mjs} +219 -3
- package/dist/audio-analyzer/index.js +4 -4
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/detection-pipeline/index.js +9 -9
- package/dist/detection-pipeline/index.mjs +3 -3
- package/dist/{dist-CksKE1Sn.js → dist-Ci2-N-XC.js} +76 -2
- package/dist/{dist-DoTw0NM3.mjs → dist-hRt5QB5i.mjs} +76 -2
- package/dist/{event-loop-stall-monitor-DzdwBr4X.js → event-loop-stall-monitor-B_EOn2Q1.js} +1 -1
- package/dist/{event-loop-stall-monitor-CtktCf3u.mjs → event-loop-stall-monitor-DUSB3ymZ.mjs} +1 -1
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +127 -114
- package/dist/pipeline-runner/index.mjs +127 -114
- package/dist/recorder/index.js +5 -5
- package/dist/recorder/index.mjs +2 -2
- package/dist/session-decode/decode-worker-child.js +57 -13
- package/dist/session-decode/decode-worker-child.mjs +57 -13
- package/dist/stream-broker/_stub.js +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BNISghj9.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DwYxOvHx.mjs} +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-FL2mrnl7.mjs +26 -0
- package/dist/stream-broker/{hostInit-DqD1qq8w.mjs → hostInit-yLEcTDS8.mjs} +1 -1
- package/dist/stream-broker/index.js +3 -3
- package/dist/stream-broker/index.mjs +2 -2
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-CuEcTi66.js → worker-protocol-CxpdncMB.js} +1 -1
- package/dist/{worker-protocol-4_4L1QOe.mjs → worker-protocol-D_6eAFbv.mjs} +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-EcWZ5m8e.js → MaskShapeCanvas-DI4BY7W2-kAtChdz2.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-CfiyCD3z.js → MotionZonesSettings-NcxxQN8r-DEwkvjzt.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-ESPy4XXk.js → PrivacyMaskSettings-APgPLF7p-Brv9VL0h.js} +1 -1
- package/embed-dist/assets/{index-C3FKV1x-.js → index-Dv7fSuDA.js} +12 -12
- package/embed-dist/index.html +1 -1
- package/package.json +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Cj2xMupA.mjs +0 -26
|
@@ -18385,6 +18385,23 @@ var CameraRecordingStatusSchema = object({
|
|
|
18385
18385
|
active: boolean(),
|
|
18386
18386
|
storageBytes: number()
|
|
18387
18387
|
});
|
|
18388
|
+
/** One stage of the fan-out that could NOT be read, and how long it cost. */
|
|
18389
|
+
var CameraStatusDegradationSchema = object({
|
|
18390
|
+
stage: _enum([
|
|
18391
|
+
"source",
|
|
18392
|
+
"broker",
|
|
18393
|
+
"detection",
|
|
18394
|
+
"recording",
|
|
18395
|
+
"switches"
|
|
18396
|
+
]),
|
|
18397
|
+
reason: _enum([
|
|
18398
|
+
"timeout",
|
|
18399
|
+
"error",
|
|
18400
|
+
"partial"
|
|
18401
|
+
]),
|
|
18402
|
+
/** Wall-clock ms spent on the stage before it was abandoned. */
|
|
18403
|
+
elapsedMs: number()
|
|
18404
|
+
});
|
|
18388
18405
|
/**
|
|
18389
18406
|
* Aggregated per-camera pipeline status — server-composed, single call.
|
|
18390
18407
|
*
|
|
@@ -18415,9 +18432,28 @@ var CameraStatusSchema = object({
|
|
|
18415
18432
|
* differently — a quiet camera that looks identical to a dead one is the
|
|
18416
18433
|
* silence-reads-as-never-happened trap this repo keeps paying for.
|
|
18417
18434
|
*
|
|
18418
|
-
* Empty when nothing is off
|
|
18435
|
+
* Empty when nothing is off, and never contains a switch no provider offers
|
|
18436
|
+
* — but an empty list is only a POSITIVE claim when `degraded` does not name
|
|
18437
|
+
* `'switches'`. When it does, the switch set could not be read and nothing
|
|
18438
|
+
* here may be rendered as "the operator turned nothing off": that is the
|
|
18439
|
+
* D62 failure (a camera we could not read painted as broken) in the very
|
|
18440
|
+
* field that exists to prevent it.
|
|
18419
18441
|
*/
|
|
18420
18442
|
switchedOff: array(CameraSwitchIdSchema).readonly(),
|
|
18443
|
+
/**
|
|
18444
|
+
* Stages of the bounded fan-out that were CUT SHORT — a timeout or a
|
|
18445
|
+
* rejection — and whose block is therefore `null` because we could not
|
|
18446
|
+
* READ it, not because there is nothing there.
|
|
18447
|
+
*
|
|
18448
|
+
* Without this, three different facts arrive as the same `null`: "the stage
|
|
18449
|
+
* timed out", "the stage failed", and "this camera legitimately has no
|
|
18450
|
+
* decoder / no recording". Every surface that draws a conclusion from a null
|
|
18451
|
+
* block (or from an empty `switchedOff`) must consult this first; a stage
|
|
18452
|
+
* named here supports no conclusion at all, only "unknown".
|
|
18453
|
+
*
|
|
18454
|
+
* Empty on a clean read — the overwhelmingly common case.
|
|
18455
|
+
*/
|
|
18456
|
+
degraded: array(CameraStatusDegradationSchema).readonly(),
|
|
18421
18457
|
/** Unix timestamp (ms) when this snapshot was composed server-side. */
|
|
18422
18458
|
fetchedAt: number()
|
|
18423
18459
|
});
|
|
@@ -21137,7 +21173,29 @@ var FaceInfoSchema = object({
|
|
|
21137
21173
|
recognizedIdentityId: string().optional(),
|
|
21138
21174
|
identityName: string().optional(),
|
|
21139
21175
|
assigned: boolean(),
|
|
21176
|
+
/**
|
|
21177
|
+
* The crop, inline, base64.
|
|
21178
|
+
*
|
|
21179
|
+
* **Prefer {@link cropUrl}.** At the 500 rows the Faces view asks for this
|
|
21180
|
+
* field alone is ~2.87 MiB, re-sent in full on every operator assign and
|
|
21181
|
+
* every 30 s poll, base64-inflated over the msgpack socket and held in the
|
|
21182
|
+
* query heap. It stays for callers that have not migrated; `includeCrops:
|
|
21183
|
+
* false` turns it off once they have.
|
|
21184
|
+
*/
|
|
21140
21185
|
base64: string().optional(),
|
|
21186
|
+
/**
|
|
21187
|
+
* Same crop, as a data-plane URL for `<img src>` — the move the admin
|
|
21188
|
+
* snapshot surfaces made on 2026-08-08.
|
|
21189
|
+
*
|
|
21190
|
+
* Served by the `event-media` plane, which resolves a raw MediaStore key and
|
|
21191
|
+
* is `access: 'authenticated'`: a bare `<img>` carries the `camstack_session`
|
|
21192
|
+
* cookie, so no header plumbing is needed. The bytes then ride the browser's
|
|
21193
|
+
* HTTP cache with an ETag and `immutable`, instead of the WebSocket.
|
|
21194
|
+
*
|
|
21195
|
+
* Absent when the face has no stored crop, or when the addon has no data
|
|
21196
|
+
* plane — callers fall back to {@link base64}.
|
|
21197
|
+
*/
|
|
21198
|
+
cropUrl: string().optional(),
|
|
21141
21199
|
/** Design B: the face bbox (pixel space) on the key frame — lets a detail
|
|
21142
21200
|
* view draw the box over the native `keyFrameMediaKey` frame. Absent on
|
|
21143
21201
|
* legacy rows written before design B. */
|
|
@@ -21202,7 +21260,23 @@ method(_void(), array(IdentitySchema).readonly()), method(object({ name: string(
|
|
|
21202
21260
|
auth: "admin"
|
|
21203
21261
|
}), method(object({
|
|
21204
21262
|
limit: number().int().positive().optional(),
|
|
21205
|
-
filter: FaceFilterEnum.optional()
|
|
21263
|
+
filter: FaceFilterEnum.optional(),
|
|
21264
|
+
/**
|
|
21265
|
+
* Inline the base64 crop on every row. Default `true` — the existing
|
|
21266
|
+
* behaviour, kept so no caller breaks.
|
|
21267
|
+
*
|
|
21268
|
+
* Set `false` once the caller renders {@link FaceInfo.cropUrl}: that
|
|
21269
|
+
* drops ~2.87 MiB per 500-row page to a few KiB of metadata and lets
|
|
21270
|
+
* the browser cache the images.
|
|
21271
|
+
*
|
|
21272
|
+
* **This is an INPUT field, so it does not reach the addon until the
|
|
21273
|
+
* next train.** The hub router validates cap inputs against its own
|
|
21274
|
+
* compiled Zod, which strips a key it does not know — verified today
|
|
21275
|
+
* on the OUTPUT side, where an additive field DOES arrive immediately
|
|
21276
|
+
* (`Track.hasFace`). Until the train ships, sending `false` is
|
|
21277
|
+
* harmless and simply keeps the crops inline.
|
|
21278
|
+
*/
|
|
21279
|
+
includeCrops: boolean().optional()
|
|
21206
21280
|
}).optional(), array(FaceInfoSchema).readonly()), method(object({
|
|
21207
21281
|
deviceId: number().int(),
|
|
21208
21282
|
trackId: string()
|
|
@@ -18385,6 +18385,23 @@ var CameraRecordingStatusSchema = object({
|
|
|
18385
18385
|
active: boolean(),
|
|
18386
18386
|
storageBytes: number()
|
|
18387
18387
|
});
|
|
18388
|
+
/** One stage of the fan-out that could NOT be read, and how long it cost. */
|
|
18389
|
+
var CameraStatusDegradationSchema = object({
|
|
18390
|
+
stage: _enum([
|
|
18391
|
+
"source",
|
|
18392
|
+
"broker",
|
|
18393
|
+
"detection",
|
|
18394
|
+
"recording",
|
|
18395
|
+
"switches"
|
|
18396
|
+
]),
|
|
18397
|
+
reason: _enum([
|
|
18398
|
+
"timeout",
|
|
18399
|
+
"error",
|
|
18400
|
+
"partial"
|
|
18401
|
+
]),
|
|
18402
|
+
/** Wall-clock ms spent on the stage before it was abandoned. */
|
|
18403
|
+
elapsedMs: number()
|
|
18404
|
+
});
|
|
18388
18405
|
/**
|
|
18389
18406
|
* Aggregated per-camera pipeline status — server-composed, single call.
|
|
18390
18407
|
*
|
|
@@ -18415,9 +18432,28 @@ var CameraStatusSchema = object({
|
|
|
18415
18432
|
* differently — a quiet camera that looks identical to a dead one is the
|
|
18416
18433
|
* silence-reads-as-never-happened trap this repo keeps paying for.
|
|
18417
18434
|
*
|
|
18418
|
-
* Empty when nothing is off
|
|
18435
|
+
* Empty when nothing is off, and never contains a switch no provider offers
|
|
18436
|
+
* — but an empty list is only a POSITIVE claim when `degraded` does not name
|
|
18437
|
+
* `'switches'`. When it does, the switch set could not be read and nothing
|
|
18438
|
+
* here may be rendered as "the operator turned nothing off": that is the
|
|
18439
|
+
* D62 failure (a camera we could not read painted as broken) in the very
|
|
18440
|
+
* field that exists to prevent it.
|
|
18419
18441
|
*/
|
|
18420
18442
|
switchedOff: array(CameraSwitchIdSchema).readonly(),
|
|
18443
|
+
/**
|
|
18444
|
+
* Stages of the bounded fan-out that were CUT SHORT — a timeout or a
|
|
18445
|
+
* rejection — and whose block is therefore `null` because we could not
|
|
18446
|
+
* READ it, not because there is nothing there.
|
|
18447
|
+
*
|
|
18448
|
+
* Without this, three different facts arrive as the same `null`: "the stage
|
|
18449
|
+
* timed out", "the stage failed", and "this camera legitimately has no
|
|
18450
|
+
* decoder / no recording". Every surface that draws a conclusion from a null
|
|
18451
|
+
* block (or from an empty `switchedOff`) must consult this first; a stage
|
|
18452
|
+
* named here supports no conclusion at all, only "unknown".
|
|
18453
|
+
*
|
|
18454
|
+
* Empty on a clean read — the overwhelmingly common case.
|
|
18455
|
+
*/
|
|
18456
|
+
degraded: array(CameraStatusDegradationSchema).readonly(),
|
|
18421
18457
|
/** Unix timestamp (ms) when this snapshot was composed server-side. */
|
|
18422
18458
|
fetchedAt: number()
|
|
18423
18459
|
});
|
|
@@ -21137,7 +21173,29 @@ var FaceInfoSchema = object({
|
|
|
21137
21173
|
recognizedIdentityId: string().optional(),
|
|
21138
21174
|
identityName: string().optional(),
|
|
21139
21175
|
assigned: boolean(),
|
|
21176
|
+
/**
|
|
21177
|
+
* The crop, inline, base64.
|
|
21178
|
+
*
|
|
21179
|
+
* **Prefer {@link cropUrl}.** At the 500 rows the Faces view asks for this
|
|
21180
|
+
* field alone is ~2.87 MiB, re-sent in full on every operator assign and
|
|
21181
|
+
* every 30 s poll, base64-inflated over the msgpack socket and held in the
|
|
21182
|
+
* query heap. It stays for callers that have not migrated; `includeCrops:
|
|
21183
|
+
* false` turns it off once they have.
|
|
21184
|
+
*/
|
|
21140
21185
|
base64: string().optional(),
|
|
21186
|
+
/**
|
|
21187
|
+
* Same crop, as a data-plane URL for `<img src>` — the move the admin
|
|
21188
|
+
* snapshot surfaces made on 2026-08-08.
|
|
21189
|
+
*
|
|
21190
|
+
* Served by the `event-media` plane, which resolves a raw MediaStore key and
|
|
21191
|
+
* is `access: 'authenticated'`: a bare `<img>` carries the `camstack_session`
|
|
21192
|
+
* cookie, so no header plumbing is needed. The bytes then ride the browser's
|
|
21193
|
+
* HTTP cache with an ETag and `immutable`, instead of the WebSocket.
|
|
21194
|
+
*
|
|
21195
|
+
* Absent when the face has no stored crop, or when the addon has no data
|
|
21196
|
+
* plane — callers fall back to {@link base64}.
|
|
21197
|
+
*/
|
|
21198
|
+
cropUrl: string().optional(),
|
|
21141
21199
|
/** Design B: the face bbox (pixel space) on the key frame — lets a detail
|
|
21142
21200
|
* view draw the box over the native `keyFrameMediaKey` frame. Absent on
|
|
21143
21201
|
* legacy rows written before design B. */
|
|
@@ -21202,7 +21260,23 @@ method(_void(), array(IdentitySchema).readonly()), method(object({ name: string(
|
|
|
21202
21260
|
auth: "admin"
|
|
21203
21261
|
}), method(object({
|
|
21204
21262
|
limit: number().int().positive().optional(),
|
|
21205
|
-
filter: FaceFilterEnum.optional()
|
|
21263
|
+
filter: FaceFilterEnum.optional(),
|
|
21264
|
+
/**
|
|
21265
|
+
* Inline the base64 crop on every row. Default `true` — the existing
|
|
21266
|
+
* behaviour, kept so no caller breaks.
|
|
21267
|
+
*
|
|
21268
|
+
* Set `false` once the caller renders {@link FaceInfo.cropUrl}: that
|
|
21269
|
+
* drops ~2.87 MiB per 500-row page to a few KiB of metadata and lets
|
|
21270
|
+
* the browser cache the images.
|
|
21271
|
+
*
|
|
21272
|
+
* **This is an INPUT field, so it does not reach the addon until the
|
|
21273
|
+
* next train.** The hub router validates cap inputs against its own
|
|
21274
|
+
* compiled Zod, which strips a key it does not know — verified today
|
|
21275
|
+
* on the OUTPUT side, where an additive field DOES arrive immediately
|
|
21276
|
+
* (`Track.hasFace`). Until the train ships, sending `false` is
|
|
21277
|
+
* harmless and simply keeps the crops inline.
|
|
21278
|
+
*/
|
|
21279
|
+
includeCrops: boolean().optional()
|
|
21206
21280
|
}).optional(), array(FaceInfoSchema).readonly()), method(object({
|
|
21207
21281
|
deviceId: number().int(),
|
|
21208
21282
|
trackId: string()
|
package/dist/{event-loop-stall-monitor-CtktCf3u.mjs → event-loop-stall-monitor-DUSB3ymZ.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { L as hfModelUrl, a as COCO_TO_MACRO, i as COCO_80_LABELS, r as AUDIO_MACRO_LABELS } from "./dist-
|
|
1
|
+
import { L as hfModelUrl, a as COCO_TO_MACRO, i as COCO_80_LABELS, r as AUDIO_MACRO_LABELS } from "./dist-hRt5QB5i.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,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-Ci2-N-XC.js");
|
|
6
6
|
const require_lazy_sharp = require("../lazy-sharp-CcYuXtsa.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as motionDetectionCapability, I as evaluateZoneRules, at as BaseAddon, ct as DeviceType, ut as hydrateSchema } from "../dist-
|
|
1
|
+
import { B as motionDetectionCapability, I as evaluateZoneRules, at as BaseAddon, ct as DeviceType, ut as hydrateSchema } from "../dist-hRt5QB5i.mjs";
|
|
2
2
|
import { t as getSharp } from "../lazy-sharp-B-mb8uWx.mjs";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-
|
|
8
|
-
const require_worker_protocol = require("../worker-protocol-
|
|
6
|
+
const require_dist = require("../dist-Ci2-N-XC.js");
|
|
7
|
+
const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-B_EOn2Q1.js");
|
|
8
|
+
const require_worker_protocol = require("../worker-protocol-CxpdncMB.js");
|
|
9
9
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
10
10
|
const require_lazy_sharp = require("../lazy-sharp-CcYuXtsa.js");
|
|
11
11
|
const require_remote_restream = require("../remote-restream-CO36Sr30.js");
|
|
@@ -1824,116 +1824,6 @@ var CropConventionSource = class {
|
|
|
1824
1824
|
}
|
|
1825
1825
|
};
|
|
1826
1826
|
//#endregion
|
|
1827
|
-
//#region src/pipeline-runner/native-lease-settings-source.ts
|
|
1828
|
-
/**
|
|
1829
|
-
* Where the runner gets the cluster-wide native-frame LEASE knobs.
|
|
1830
|
-
*
|
|
1831
|
-
* ## Why a settings read and not the attach config
|
|
1832
|
-
*
|
|
1833
|
-
* Identical reasoning to `crop-convention-source.ts`, and worth restating
|
|
1834
|
-
* because the temptation is the same: `RunnerCameraConfig` is the obvious
|
|
1835
|
-
* orchestrator→runner channel and it is the wrong one. Eleven call sites build
|
|
1836
|
-
* one; a cluster-wide constant threaded through eleven builders eventually
|
|
1837
|
-
* reaches ten of them, and the cameras attached by the forgotten builder just
|
|
1838
|
-
* quietly run a different lease window. Instead the runner reads the OWNER
|
|
1839
|
-
* addon's global settings directly, hub-routed by `AddonCallGateway`, so an
|
|
1840
|
-
* agent runner transparently reads the hub-central store with no placement
|
|
1841
|
-
* branching.
|
|
1842
|
-
*
|
|
1843
|
-
* ## Deliberately NOT node-scoped
|
|
1844
|
-
*
|
|
1845
|
-
* The read passes no `nodeId`. With one, `addon-settings` would project that
|
|
1846
|
-
* node's scoped value and two nodes could hold different lease windows for the
|
|
1847
|
-
* same camera — so whether a stored crop is native pixels or the ≤640 fallback
|
|
1848
|
-
* would depend on where the balancer happened to place it, which is invisible
|
|
1849
|
-
* from the stored media.
|
|
1850
|
-
*
|
|
1851
|
-
* ## What it hands out
|
|
1852
|
-
*
|
|
1853
|
-
* A {@link NativeLeaseSettingsOverride} — only the knobs the operator SET. The
|
|
1854
|
-
* decode worker resolves the rest (env var, then the shipped default); see
|
|
1855
|
-
* `session-decode/native-lease-config.ts` for the precedence. Handing out a
|
|
1856
|
-
* fully-resolved triple here would erase the "operator set nothing" state and
|
|
1857
|
-
* with it the env-var escape hatch.
|
|
1858
|
-
*/
|
|
1859
|
-
/**
|
|
1860
|
-
* The addon whose GLOBAL settings own the knobs. `pipeline-orchestrator` is
|
|
1861
|
-
* hub-resident and neutral — the same owner as the detail-crop convention and
|
|
1862
|
-
* the per-node decoder-backend selector.
|
|
1863
|
-
*/
|
|
1864
|
-
var NATIVE_LEASE_OWNER_ADDON_ID = "pipeline-orchestrator";
|
|
1865
|
-
/**
|
|
1866
|
-
* Whether two overrides name the same knobs with the same values.
|
|
1867
|
-
*
|
|
1868
|
-
* **Every knob has to appear here.** This is the equality that decides whether
|
|
1869
|
-
* a re-read is ADOPTED at all: a knob missing from the comparison is read from
|
|
1870
|
-
* the hub, judged identical, and discarded, so the operator's change never
|
|
1871
|
-
* reaches a decode worker and nothing anywhere says so. `admission` shipped
|
|
1872
|
-
* missing from this list and did exactly that — the setting was stored, the
|
|
1873
|
-
* hub served it, the runner fetched it, and every worker kept spawning with the
|
|
1874
|
-
* old value.
|
|
1875
|
-
*/
|
|
1876
|
-
function sameOverride(a, b) {
|
|
1877
|
-
return a.ttlMs === b.ttlMs && a.budgetMb === b.budgetMb && a.activityMs === b.activityMs && a.admission === b.admission;
|
|
1878
|
-
}
|
|
1879
|
-
/**
|
|
1880
|
-
* TTL-cached read of the cluster native-lease knobs.
|
|
1881
|
-
*
|
|
1882
|
-
* {@link current} is synchronous because the fork that consumes it is;
|
|
1883
|
-
* {@link refresh} is what talks to the hub, and the runner awaits it on the
|
|
1884
|
-
* path that precedes a decode-worker spawn.
|
|
1885
|
-
*/
|
|
1886
|
-
var NativeLeaseSettingsSource = class {
|
|
1887
|
-
override = {};
|
|
1888
|
-
lastReadAtMs = Number.NEGATIVE_INFINITY;
|
|
1889
|
-
inFlight = null;
|
|
1890
|
-
logger;
|
|
1891
|
-
now;
|
|
1892
|
-
ttlMs;
|
|
1893
|
-
constructor(options) {
|
|
1894
|
-
this.logger = options.logger;
|
|
1895
|
-
this.now = options.now ?? (() => Date.now());
|
|
1896
|
-
this.ttlMs = options.ttlMs ?? 3e4;
|
|
1897
|
-
}
|
|
1898
|
-
/** The operator's overrides as last read — `{}` until a read lands. */
|
|
1899
|
-
current() {
|
|
1900
|
-
return this.override;
|
|
1901
|
-
}
|
|
1902
|
-
/**
|
|
1903
|
-
* Re-read if the cached value has expired. Concurrent callers share one
|
|
1904
|
-
* in-flight read; a failure keeps the LAST KNOWN value rather than reverting
|
|
1905
|
-
* to `{}`, because a revert would silently hand the next-spawned workers a
|
|
1906
|
-
* different RAM profile than the ones already running.
|
|
1907
|
-
*/
|
|
1908
|
-
async refresh(api) {
|
|
1909
|
-
if (api === void 0) return this.override;
|
|
1910
|
-
if (this.now() - this.lastReadAtMs < this.ttlMs) return this.override;
|
|
1911
|
-
this.inFlight ??= this.read(api).finally(() => {
|
|
1912
|
-
this.inFlight = null;
|
|
1913
|
-
});
|
|
1914
|
-
await this.inFlight;
|
|
1915
|
-
return this.override;
|
|
1916
|
-
}
|
|
1917
|
-
async read(api) {
|
|
1918
|
-
try {
|
|
1919
|
-
const next = require_dist.pickNativeLeaseOverride(await api.addonSettings.getGlobalSettings.query({ addonId: NATIVE_LEASE_OWNER_ADDON_ID }));
|
|
1920
|
-
this.lastReadAtMs = this.now();
|
|
1921
|
-
if (sameOverride(next, this.override)) return;
|
|
1922
|
-
this.logger.info("native lease settings changed — applies to NEW decode sessions", { meta: {
|
|
1923
|
-
from: { ...this.override },
|
|
1924
|
-
to: { ...next }
|
|
1925
|
-
} });
|
|
1926
|
-
this.override = next;
|
|
1927
|
-
} catch (err) {
|
|
1928
|
-
this.logger.warn("native lease settings read failed — keeping the last known value", { meta: {
|
|
1929
|
-
owner: NATIVE_LEASE_OWNER_ADDON_ID,
|
|
1930
|
-
inUse: { ...this.override },
|
|
1931
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1932
|
-
} });
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
};
|
|
1936
|
-
//#endregion
|
|
1937
1827
|
//#region src/pipeline-runner/detail-subtree.ts
|
|
1938
1828
|
/** Did the native-lease fetch report a miss (rather than return pixels)? */
|
|
1939
1829
|
function isNativeCropMiss(fetch) {
|
|
@@ -2508,6 +2398,116 @@ function resolveNativeCropSource(isFullFrame, nativeHit) {
|
|
|
2508
2398
|
return isFullFrame ? "ram-fullframe" : "miss";
|
|
2509
2399
|
}
|
|
2510
2400
|
//#endregion
|
|
2401
|
+
//#region src/pipeline-runner/native-lease-settings-source.ts
|
|
2402
|
+
/**
|
|
2403
|
+
* Where the runner gets the cluster-wide native-frame LEASE knobs.
|
|
2404
|
+
*
|
|
2405
|
+
* ## Why a settings read and not the attach config
|
|
2406
|
+
*
|
|
2407
|
+
* Identical reasoning to `crop-convention-source.ts`, and worth restating
|
|
2408
|
+
* because the temptation is the same: `RunnerCameraConfig` is the obvious
|
|
2409
|
+
* orchestrator→runner channel and it is the wrong one. Eleven call sites build
|
|
2410
|
+
* one; a cluster-wide constant threaded through eleven builders eventually
|
|
2411
|
+
* reaches ten of them, and the cameras attached by the forgotten builder just
|
|
2412
|
+
* quietly run a different lease window. Instead the runner reads the OWNER
|
|
2413
|
+
* addon's global settings directly, hub-routed by `AddonCallGateway`, so an
|
|
2414
|
+
* agent runner transparently reads the hub-central store with no placement
|
|
2415
|
+
* branching.
|
|
2416
|
+
*
|
|
2417
|
+
* ## Deliberately NOT node-scoped
|
|
2418
|
+
*
|
|
2419
|
+
* The read passes no `nodeId`. With one, `addon-settings` would project that
|
|
2420
|
+
* node's scoped value and two nodes could hold different lease windows for the
|
|
2421
|
+
* same camera — so whether a stored crop is native pixels or the ≤640 fallback
|
|
2422
|
+
* would depend on where the balancer happened to place it, which is invisible
|
|
2423
|
+
* from the stored media.
|
|
2424
|
+
*
|
|
2425
|
+
* ## What it hands out
|
|
2426
|
+
*
|
|
2427
|
+
* A {@link NativeLeaseSettingsOverride} — only the knobs the operator SET. The
|
|
2428
|
+
* decode worker resolves the rest (env var, then the shipped default); see
|
|
2429
|
+
* `session-decode/native-lease-config.ts` for the precedence. Handing out a
|
|
2430
|
+
* fully-resolved triple here would erase the "operator set nothing" state and
|
|
2431
|
+
* with it the env-var escape hatch.
|
|
2432
|
+
*/
|
|
2433
|
+
/**
|
|
2434
|
+
* The addon whose GLOBAL settings own the knobs. `pipeline-orchestrator` is
|
|
2435
|
+
* hub-resident and neutral — the same owner as the detail-crop convention and
|
|
2436
|
+
* the per-node decoder-backend selector.
|
|
2437
|
+
*/
|
|
2438
|
+
var NATIVE_LEASE_OWNER_ADDON_ID = "pipeline-orchestrator";
|
|
2439
|
+
/**
|
|
2440
|
+
* Whether two overrides name the same knobs with the same values.
|
|
2441
|
+
*
|
|
2442
|
+
* **Every knob has to appear here.** This is the equality that decides whether
|
|
2443
|
+
* a re-read is ADOPTED at all: a knob missing from the comparison is read from
|
|
2444
|
+
* the hub, judged identical, and discarded, so the operator's change never
|
|
2445
|
+
* reaches a decode worker and nothing anywhere says so. `admission` shipped
|
|
2446
|
+
* missing from this list and did exactly that — the setting was stored, the
|
|
2447
|
+
* hub served it, the runner fetched it, and every worker kept spawning with the
|
|
2448
|
+
* old value.
|
|
2449
|
+
*/
|
|
2450
|
+
function sameOverride(a, b) {
|
|
2451
|
+
return a.ttlMs === b.ttlMs && a.budgetMb === b.budgetMb && a.activityMs === b.activityMs && a.admission === b.admission;
|
|
2452
|
+
}
|
|
2453
|
+
/**
|
|
2454
|
+
* TTL-cached read of the cluster native-lease knobs.
|
|
2455
|
+
*
|
|
2456
|
+
* {@link current} is synchronous because the fork that consumes it is;
|
|
2457
|
+
* {@link refresh} is what talks to the hub, and the runner awaits it on the
|
|
2458
|
+
* path that precedes a decode-worker spawn.
|
|
2459
|
+
*/
|
|
2460
|
+
var NativeLeaseSettingsSource = class {
|
|
2461
|
+
override = {};
|
|
2462
|
+
lastReadAtMs = Number.NEGATIVE_INFINITY;
|
|
2463
|
+
inFlight = null;
|
|
2464
|
+
logger;
|
|
2465
|
+
now;
|
|
2466
|
+
ttlMs;
|
|
2467
|
+
constructor(options) {
|
|
2468
|
+
this.logger = options.logger;
|
|
2469
|
+
this.now = options.now ?? (() => Date.now());
|
|
2470
|
+
this.ttlMs = options.ttlMs ?? 3e4;
|
|
2471
|
+
}
|
|
2472
|
+
/** The operator's overrides as last read — `{}` until a read lands. */
|
|
2473
|
+
current() {
|
|
2474
|
+
return this.override;
|
|
2475
|
+
}
|
|
2476
|
+
/**
|
|
2477
|
+
* Re-read if the cached value has expired. Concurrent callers share one
|
|
2478
|
+
* in-flight read; a failure keeps the LAST KNOWN value rather than reverting
|
|
2479
|
+
* to `{}`, because a revert would silently hand the next-spawned workers a
|
|
2480
|
+
* different RAM profile than the ones already running.
|
|
2481
|
+
*/
|
|
2482
|
+
async refresh(api) {
|
|
2483
|
+
if (api === void 0) return this.override;
|
|
2484
|
+
if (this.now() - this.lastReadAtMs < this.ttlMs) return this.override;
|
|
2485
|
+
this.inFlight ??= this.read(api).finally(() => {
|
|
2486
|
+
this.inFlight = null;
|
|
2487
|
+
});
|
|
2488
|
+
await this.inFlight;
|
|
2489
|
+
return this.override;
|
|
2490
|
+
}
|
|
2491
|
+
async read(api) {
|
|
2492
|
+
try {
|
|
2493
|
+
const next = require_dist.pickNativeLeaseOverride(await api.addonSettings.getGlobalSettings.query({ addonId: NATIVE_LEASE_OWNER_ADDON_ID }));
|
|
2494
|
+
this.lastReadAtMs = this.now();
|
|
2495
|
+
if (sameOverride(next, this.override)) return;
|
|
2496
|
+
this.logger.info("native lease settings changed — applies to NEW decode sessions", { meta: {
|
|
2497
|
+
from: { ...this.override },
|
|
2498
|
+
to: { ...next }
|
|
2499
|
+
} });
|
|
2500
|
+
this.override = next;
|
|
2501
|
+
} catch (err) {
|
|
2502
|
+
this.logger.warn("native lease settings read failed — keeping the last known value", { meta: {
|
|
2503
|
+
owner: NATIVE_LEASE_OWNER_ADDON_ID,
|
|
2504
|
+
inUse: { ...this.override },
|
|
2505
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2506
|
+
} });
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
};
|
|
2510
|
+
//#endregion
|
|
2511
2511
|
//#region src/pipeline-runner/frame-governor.ts
|
|
2512
2512
|
var FrameGovernor = class {
|
|
2513
2513
|
config;
|
|
@@ -4986,7 +4986,20 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
4986
4986
|
if (resolved.tier === "native" && resolved.pixels !== null) return this.encodeNativeCropResult(resolved.pixels, resolved.tier, input.encodeJpeg);
|
|
4987
4987
|
if (resolved.tier !== "ram-fullframe") return null;
|
|
4988
4988
|
const crop = this.retainedFrames?.crop(input.handle, input.bbox, input.maxWidth);
|
|
4989
|
-
if (!crop)
|
|
4989
|
+
if (!crop) {
|
|
4990
|
+
this.ctx?.logger.warn("native full-frame miss — the retained store no longer holds this frame", {
|
|
4991
|
+
tags: { deviceId: this.nativeCropRegistry.deviceIdFor(input.handle) ?? -1 },
|
|
4992
|
+
meta: {
|
|
4993
|
+
handle: `${input.handle.shmId}#${input.handle.slot}#${input.handle.seq}`,
|
|
4994
|
+
handleNodeId: input.handle.nodeId,
|
|
4995
|
+
nativeMissReason: resolved.missReason,
|
|
4996
|
+
nativeHandleAgeMs: resolved.handleAgeMs,
|
|
4997
|
+
maxWidth: input.maxWidth ?? null,
|
|
4998
|
+
hint: "both the native lease and the retained-frame window have closed for this frame — the detail dispatch that asked for it produces NO crop"
|
|
4999
|
+
}
|
|
5000
|
+
});
|
|
5001
|
+
return null;
|
|
5002
|
+
}
|
|
4990
5003
|
return this.encodeNativeCropResult({
|
|
4991
5004
|
bytes: crop.bytes,
|
|
4992
5005
|
width: crop.width,
|