@camstack/system 1.2.57 → 1.2.58
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-runner.js +1 -1
- package/dist/addon-runner.mjs +1 -1
- package/dist/addon-utils.js +1 -1
- package/dist/addon-utils.mjs +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +2 -2
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +2 -2
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +2 -2
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +2 -2
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
- package/dist/builtins/device-manager/device-manager.addon.js +1 -1
- package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +2 -2
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +2 -2
- package/dist/builtins/sqlite-storage/index.d.ts +3 -0
- package/dist/builtins/sqlite-storage/index.js +8 -0
- package/dist/builtins/sqlite-storage/index.mjs +2 -2
- package/dist/builtins/sqlite-storage/sqlite-pragmas.d.ts +85 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +1 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +304 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +297 -2
- package/dist/builtins/sqlite-storage/wal-maintenance.d.ts +113 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{dist-BupxnZOi.js → dist-Chl2MvMd.js} +632 -17
- package/dist/{dist-DhL_zGRT.mjs → dist-UrXyJ9Zm.mjs} +632 -17
- package/dist/index.js +4 -4
- package/dist/index.mjs +5 -5
- package/dist/kernel/transport/frame-codec.d.ts +43 -2
- package/dist/{manifest-python-deps-Ck4-9K9m.mjs → manifest-python-deps-CX5tGRqf.mjs} +95 -10
- package/dist/{manifest-python-deps-BqE5j0-O.js → manifest-python-deps-d4acYXVV.js} +95 -10
- package/package.json +1 -1
- package/dist/{model-download-service-hf0ookyy.js → model-download-service-Cpc6SWzV.js} +1 -1
- package/dist/{model-download-service-Cp9f4dk6.mjs → model-download-service-D8B-4ktF.mjs} +1 -1
|
@@ -3028,6 +3028,14 @@ var VideoEncodeSchema = zod.z.object({
|
|
|
3028
3028
|
"main",
|
|
3029
3029
|
"high"
|
|
3030
3030
|
]).optional(),
|
|
3031
|
+
/**
|
|
3032
|
+
* `-level`, e.g. `'3.1'`. A consumer that ADVERTISES a level in its SDP
|
|
3033
|
+
* (`profile-level-id=42e01f` is Baseline 3.1) must constrain the encoder to
|
|
3034
|
+
* it, or it ships a stream that does not match its own advertisement — the
|
|
3035
|
+
* defect class that kept HomeKit black for a year and that Alexa carried
|
|
3036
|
+
* silently. Optional because a browser negotiates the level itself.
|
|
3037
|
+
*/
|
|
3038
|
+
level: zod.z.string().optional(),
|
|
3031
3039
|
width: zod.z.number().int().positive().optional(),
|
|
3032
3040
|
height: zod.z.number().int().positive().optional(),
|
|
3033
3041
|
fps: zod.z.number().positive().optional(),
|
|
@@ -3078,6 +3086,29 @@ var EncodeProfileSchema = zod.z.object({
|
|
|
3078
3086
|
outputArgs: zod.z.array(zod.z.string()).optional()
|
|
3079
3087
|
});
|
|
3080
3088
|
/**
|
|
3089
|
+
* The shape every live egress starts from: H.264 Baseline 3.1 at 720p25.
|
|
3090
|
+
* Baseline because it is the one profile every consumer in this repo decodes
|
|
3091
|
+
* (Echo, iOS, an old browser); 3.1 because that is what the SDPs advertise.
|
|
3092
|
+
*/
|
|
3093
|
+
var BASE_LIVE_EGRESS_PROFILE = {
|
|
3094
|
+
video: {
|
|
3095
|
+
codec: "h264",
|
|
3096
|
+
profile: "baseline",
|
|
3097
|
+
level: "3.1",
|
|
3098
|
+
width: 1280,
|
|
3099
|
+
height: 720,
|
|
3100
|
+
fps: 25,
|
|
3101
|
+
bitrateKbps: 2500,
|
|
3102
|
+
gopFrames: 25,
|
|
3103
|
+
bf: 0,
|
|
3104
|
+
preset: "veryfast",
|
|
3105
|
+
tune: "zerolatency"
|
|
3106
|
+
},
|
|
3107
|
+
audio: "passthrough"
|
|
3108
|
+
};
|
|
3109
|
+
({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
|
|
3110
|
+
({ ...BASE_LIVE_EGRESS_PROFILE });
|
|
3111
|
+
/**
|
|
3081
3112
|
* Deep wiring healthcheck — snapshot of active reachability probes across
|
|
3082
3113
|
* every declared capability + widget of every installed plugin, on every
|
|
3083
3114
|
* node. Produced by the backend `WiringHealthService` and surfaced via
|
|
@@ -3126,6 +3157,105 @@ zod.z.object({
|
|
|
3126
3157
|
unreachable: zod.z.number()
|
|
3127
3158
|
})
|
|
3128
3159
|
});
|
|
3160
|
+
/**
|
|
3161
|
+
* Per-camera FUNCTION SWITCHES — the one coherent on/off surface over the
|
|
3162
|
+
* pipeline functions an operator thinks in terms of.
|
|
3163
|
+
*
|
|
3164
|
+
* ## This file adds no state
|
|
3165
|
+
*
|
|
3166
|
+
* Every switch here is a VIEW onto an authority that already existed
|
|
3167
|
+
* ([D61](../../../../docs/decisions/adr-0062.md)). The whole point of the
|
|
3168
|
+
* group is that there is exactly one place each function is turned off, and
|
|
3169
|
+
* the group routes to it:
|
|
3170
|
+
*
|
|
3171
|
+
* | Switch | Authority | Proven "off stops the work" gate |
|
|
3172
|
+
* | --- | --- | --- |
|
|
3173
|
+
* | `stream-broker` | `deviceManager.setDisabled` | `StreamBrokerManager.reconcileAllCatalogs` releases the brokers; `ensureBroker` refuses re-creation |
|
|
3174
|
+
* | `object-detection` | `deviceManager.setWrapperActive('detection-pipeline')` | `PipelineSettingsStore.resolvePipelineForDevice` returns `{ steps: [], audio: null }` |
|
|
3175
|
+
* | `audio-analysis` | `deviceManager.setWrapperActive('audio-analysis')` | `AudioSubscriptionController.subscribeAudioStream` returns `null` before opening the stream |
|
|
3176
|
+
* | `recording` | `recording.setDeviceConfig` → `RecordingConfig.enabled` | `band-decision.shouldRecord` returns false; the controller detaches the device |
|
|
3177
|
+
* | `notifications` | `notificationRules.setDeviceMuted` | `NotificationCenter.evaluateAndEnqueue` returns before any rule is evaluated |
|
|
3178
|
+
*
|
|
3179
|
+
* The wrapper-binding pair is not a new idea: `legacy-migrations.ts` already
|
|
3180
|
+
* migrated the legacy `audioEnabled` / `pipelineEnabled` /
|
|
3181
|
+
* `motionDetectionEnabled` booleans ONTO `setWrapperActive`. The group is the
|
|
3182
|
+
* surface that decision never got.
|
|
3183
|
+
*
|
|
3184
|
+
* ## Two rules that are load-bearing
|
|
3185
|
+
*
|
|
3186
|
+
* - **Recording's switch is `enabled`, never the bands.** `bands` is the only
|
|
3187
|
+
* authored intent and `mode` is derived from it (`deriveRecordingMode`).
|
|
3188
|
+
* Expressing "off" by clearing bands destroys the operator's schedule and
|
|
3189
|
+
* turning the camera back on would then silently record nothing.
|
|
3190
|
+
* - **A switch that is off must be reported as off**, not merely produce
|
|
3191
|
+
* nothing. {@link CameraSwitch.enabled} is what a status surface renders as
|
|
3192
|
+
* "disabled by an operator" instead of "broken" — see
|
|
3193
|
+
* `CameraStatus.switchedOff`.
|
|
3194
|
+
*/
|
|
3195
|
+
/**
|
|
3196
|
+
* The five functions the operator named (2026-08-05). Deliberately NOT one id
|
|
3197
|
+
* per pipeline step: face recognition and plate/LPR are per-step toggles on
|
|
3198
|
+
* `pipelineOrchestrator.setCameraStepToggle` and belong in the pipeline
|
|
3199
|
+
* editor, not in a five-button safety group.
|
|
3200
|
+
*/
|
|
3201
|
+
var CameraSwitchIdSchema = zod.z.enum([
|
|
3202
|
+
"stream-broker",
|
|
3203
|
+
"object-detection",
|
|
3204
|
+
"audio-analysis",
|
|
3205
|
+
"recording",
|
|
3206
|
+
"notifications"
|
|
3207
|
+
]);
|
|
3208
|
+
/**
|
|
3209
|
+
* WHERE the switch's state actually lives. A discriminated union rather than a
|
|
3210
|
+
* string so both the writer (the orchestrator's `setCameraSwitch`) and any
|
|
3211
|
+
* reader can exhaustively narrow — and so "the group added a parallel map" is
|
|
3212
|
+
* a compile error rather than a review comment.
|
|
3213
|
+
*/
|
|
3214
|
+
var CameraSwitchAuthoritySchema = zod.z.discriminatedUnion("kind", [
|
|
3215
|
+
zod.z.object({ kind: zod.z.literal("device-disabled") }),
|
|
3216
|
+
zod.z.object({
|
|
3217
|
+
kind: zod.z.literal("wrapper-binding"),
|
|
3218
|
+
capName: zod.z.string()
|
|
3219
|
+
}),
|
|
3220
|
+
zod.z.object({ kind: zod.z.literal("recording-config") }),
|
|
3221
|
+
zod.z.object({ kind: zod.z.literal("notification-mute") })
|
|
3222
|
+
]);
|
|
3223
|
+
/**
|
|
3224
|
+
* Why a switch is not offered for this camera. Rendered instead of the
|
|
3225
|
+
* control, never as a dead control — an absent function and a broken one must
|
|
3226
|
+
* not look the same.
|
|
3227
|
+
*/
|
|
3228
|
+
var CameraSwitchUnavailableReasonSchema = zod.z.enum(["no-provider", "source-unreachable"]);
|
|
3229
|
+
/**
|
|
3230
|
+
* One switch, resolved for one camera.
|
|
3231
|
+
*
|
|
3232
|
+
* `label` and `costWhenOff` travel ON THE WIRE rather than being looked up
|
|
3233
|
+
* client-side: the viewer is a separate repository that does not import
|
|
3234
|
+
* `@camstack/types`, and a cost line duplicated in two clients is a cost line
|
|
3235
|
+
* that will disagree with itself. Five rows per camera is nothing.
|
|
3236
|
+
*/
|
|
3237
|
+
var CameraSwitchSchema = zod.z.object({
|
|
3238
|
+
id: CameraSwitchIdSchema,
|
|
3239
|
+
label: zod.z.string(),
|
|
3240
|
+
/**
|
|
3241
|
+
* What the operator LOSES while this is off, in one sentence. Required, not
|
|
3242
|
+
* optional: a switch that cannot say what it costs should not ship.
|
|
3243
|
+
*/
|
|
3244
|
+
costWhenOff: zod.z.string(),
|
|
3245
|
+
/** False = do not render a control. `unavailableReason` says why. */
|
|
3246
|
+
available: zod.z.boolean(),
|
|
3247
|
+
unavailableReason: CameraSwitchUnavailableReasonSchema.optional(),
|
|
3248
|
+
/** Current state. Meaningless when `available` is false — read it as `true`. */
|
|
3249
|
+
enabled: zod.z.boolean(),
|
|
3250
|
+
authority: CameraSwitchAuthoritySchema
|
|
3251
|
+
});
|
|
3252
|
+
/** The whole group for one camera. */
|
|
3253
|
+
var CameraSwitchGroupSchema = zod.z.object({
|
|
3254
|
+
deviceId: zod.z.number().int(),
|
|
3255
|
+
switches: zod.z.array(CameraSwitchSchema).readonly(),
|
|
3256
|
+
/** Unix ms when the group was composed server-side. */
|
|
3257
|
+
fetchedAt: zod.z.number()
|
|
3258
|
+
});
|
|
3129
3259
|
/** Friendly display labels for stream quality IDs. */
|
|
3130
3260
|
var STREAM_QUALITY_LABELS = {
|
|
3131
3261
|
high: "High",
|
|
@@ -3175,14 +3305,16 @@ var OpsLogOpSchema = zod.z.enum([
|
|
|
3175
3305
|
"manual-delete",
|
|
3176
3306
|
"rescan",
|
|
3177
3307
|
"retention-run",
|
|
3178
|
-
"relocate"
|
|
3308
|
+
"relocate",
|
|
3309
|
+
"orphan-audit"
|
|
3179
3310
|
]);
|
|
3180
3311
|
/** Why the operation ran. */
|
|
3181
3312
|
var OpsLogReasonSchema = zod.z.enum([
|
|
3182
3313
|
"retention",
|
|
3183
3314
|
"quota",
|
|
3184
3315
|
"manual",
|
|
3185
|
-
"operator"
|
|
3316
|
+
"operator",
|
|
3317
|
+
"maintenance"
|
|
3186
3318
|
]);
|
|
3187
3319
|
/** One audit row, shared verbatim by both domains. */
|
|
3188
3320
|
var OpsLogEntrySchema = zod.z.object({
|
|
@@ -5409,6 +5541,100 @@ var RtpSourceSchema = zod.z.object({
|
|
|
5409
5541
|
encoder: zod.z.string(),
|
|
5410
5542
|
pipelineKey: zod.z.string()
|
|
5411
5543
|
});
|
|
5544
|
+
/**
|
|
5545
|
+
* The encode request — **structured and serialisable, with NO raw-flag escape
|
|
5546
|
+
* hatch.** This is deliberate and it is the one lesson taken from
|
|
5547
|
+
* `getStreamWithCodec`: that method's `outputArgs: string[]` is simultaneously
|
|
5548
|
+
* its extensibility mechanism AND part of `pipelineKeyFor`'s sharing key, so
|
|
5549
|
+
* adding a flag silently forks the shared child, and two consumers that mean
|
|
5550
|
+
* the same thing but spell it differently never share. Here every knob is a
|
|
5551
|
+
* NAMED field: a new requirement becomes a schema field (and a codegen run),
|
|
5552
|
+
* never an opaque array.
|
|
5553
|
+
*
|
|
5554
|
+
* `inputArgs` / `outputArgs` are omitted from the profile for the same reason.
|
|
5555
|
+
* The operator-facing derived-stream transform editor still has them — that is
|
|
5556
|
+
* a different surface (`publishCameraStream({ kind: 'derived' })`) with a
|
|
5557
|
+
* different purpose (reshaping a badly-behaved SOURCE), and it is unchanged.
|
|
5558
|
+
*/
|
|
5559
|
+
var EgressEncodeSchema = EncodeProfileSchema.omit({
|
|
5560
|
+
inputArgs: true,
|
|
5561
|
+
outputArgs: true
|
|
5562
|
+
});
|
|
5563
|
+
/**
|
|
5564
|
+
* How the encoder is bounded. `'tight'` is a one-second VBV window for a
|
|
5565
|
+
* consumer whose budget is enforced per second (HomeKit); `'relaxed'` is two
|
|
5566
|
+
* seconds, letting a keyframe spike borrow from the next second (a browser,
|
|
5567
|
+
* an Echo). Named rather than numeric so the INTENT survives.
|
|
5568
|
+
*/
|
|
5569
|
+
var EgressRateControlSchema = zod.z.enum(["tight", "relaxed"]);
|
|
5570
|
+
var EgressTranscodeRequestSchema = zod.z.object({
|
|
5571
|
+
deviceId: zod.z.number().int().nonnegative(),
|
|
5572
|
+
/** Which published stream to read. */
|
|
5573
|
+
source: zod.z.discriminatedUnion("kind", [zod.z.object({
|
|
5574
|
+
kind: zod.z.literal("profile"),
|
|
5575
|
+
profile: CamProfileSchema
|
|
5576
|
+
}), zod.z.object({
|
|
5577
|
+
kind: zod.z.literal("cam-stream"),
|
|
5578
|
+
camStreamId: zod.z.string().min(1)
|
|
5579
|
+
})]),
|
|
5580
|
+
encode: EgressEncodeSchema,
|
|
5581
|
+
rateControl: EgressRateControlSchema.optional(),
|
|
5582
|
+
/**
|
|
5583
|
+
* `-bsf:v`. A consumer that negotiates its OWN SDP (HomeKit) cannot carry
|
|
5584
|
+
* out-of-band extradata and needs `dump_extra` on both the copy and encode
|
|
5585
|
+
* branches. Enumerated, not free text.
|
|
5586
|
+
*/
|
|
5587
|
+
bitstreamFilter: zod.z.enum([
|
|
5588
|
+
"dump_extra",
|
|
5589
|
+
"h264_mp4toannexb",
|
|
5590
|
+
"hevc_mp4toannexb"
|
|
5591
|
+
]).optional(),
|
|
5592
|
+
pixelFormat: zod.z.enum(["yuv420p", "nv12"]).optional(),
|
|
5593
|
+
/**
|
|
5594
|
+
* Operator/consumer override for decode hardware. ABSENT is the normal case
|
|
5595
|
+
* and the one that matters: the broker then resolves the backend from the
|
|
5596
|
+
* DECODER ADDON's per-node `probedBestHwaccel` (see
|
|
5597
|
+
* `@camstack/types` `ffmpeg/hwaccel.ts`), which is the ranking known to work
|
|
5598
|
+
* on this hardware — never the raw kernel resolver's qsv-first order.
|
|
5599
|
+
*/
|
|
5600
|
+
decodeHwAccel: zod.z.enum([
|
|
5601
|
+
"auto",
|
|
5602
|
+
"none",
|
|
5603
|
+
"videotoolbox",
|
|
5604
|
+
"vaapi",
|
|
5605
|
+
"qsv",
|
|
5606
|
+
"cuda"
|
|
5607
|
+
]).optional(),
|
|
5608
|
+
/**
|
|
5609
|
+
* Host to embed in the returned restream `url`. The broker mints hub-local
|
|
5610
|
+
* `127.0.0.1` URLs; a consumer on another node passes a cluster-resolvable
|
|
5611
|
+
* host (`NodeTopologyService.reachableHostByNode`) so the returned URL is
|
|
5612
|
+
* dialable from there. Same contract as `getStreamWithCodec.hostname` —
|
|
5613
|
+
* `substituteRtspHost` rewrites only the dial address, never the restreamer.
|
|
5614
|
+
*/
|
|
5615
|
+
hostname: zod.z.string().optional(),
|
|
5616
|
+
/** Attribution for the broker panel. Never part of the sharing key. */
|
|
5617
|
+
tag: zod.z.string().optional()
|
|
5618
|
+
});
|
|
5619
|
+
var EgressTranscodeSchema = zod.z.object({
|
|
5620
|
+
/** Dial-able RTSP url (host-substituted when `hostname` was supplied). */
|
|
5621
|
+
url: zod.z.string(),
|
|
5622
|
+
/** Release handle. Refcounted — the child dies when the last holder releases. */
|
|
5623
|
+
pipelineKey: zod.z.string(),
|
|
5624
|
+
videoCodec: zod.z.enum(["H264", "H265"]),
|
|
5625
|
+
resolution: zod.z.object({
|
|
5626
|
+
width: zod.z.number().int().positive(),
|
|
5627
|
+
height: zod.z.number().int().positive()
|
|
5628
|
+
}),
|
|
5629
|
+
transcoded: zod.z.boolean(),
|
|
5630
|
+
encoder: zod.z.string(),
|
|
5631
|
+
/**
|
|
5632
|
+
* The decode backend the child ACTUALLY ran with — `null` for software.
|
|
5633
|
+
* Returned rather than assumed: a consumer that asked for hardware and got
|
|
5634
|
+
* software needs to be able to see that without reading the broker's logs.
|
|
5635
|
+
*/
|
|
5636
|
+
decodeHwAccel: zod.z.string().nullable()
|
|
5637
|
+
});
|
|
5412
5638
|
var streamBrokerCapability = {
|
|
5413
5639
|
name: "stream-broker",
|
|
5414
5640
|
scope: "system",
|
|
@@ -5580,6 +5806,40 @@ var streamBrokerCapability = {
|
|
|
5580
5806
|
auth: "admin"
|
|
5581
5807
|
}),
|
|
5582
5808
|
/**
|
|
5809
|
+
* THE ffmpeg primitive. Acquire an encoded stream matching a structured
|
|
5810
|
+
* encode plan; the broker builds the argv through the ONE builder
|
|
5811
|
+
* (`@camstack/types` `ffmpeg/invocation.ts`), resolves decode hardware from
|
|
5812
|
+
* the DECODER ADDON's per-node ranking, and returns a dialable RTSP url.
|
|
5813
|
+
*
|
|
5814
|
+
* **Refcounted and deduplicated on EXACT match.** Two requesters whose
|
|
5815
|
+
* requests produce the same `egressTranscodeSharingKey` receive the SAME
|
|
5816
|
+
* `pipelineKey` and the same child process. Nearly-identical requests are
|
|
5817
|
+
* NOT merged.
|
|
5818
|
+
*
|
|
5819
|
+
* **The handle is immutable.** There is deliberately no `reconfigure`
|
|
5820
|
+
* method and this one never accepts a `pipelineKey` alongside encode
|
|
5821
|
+
* parameters: a consumer whose requirements change releases and
|
|
5822
|
+
* re-acquires. A mutable shared handle is exactly what made Alexa's old
|
|
5823
|
+
* `derived:alexa-<id>` stream a co-tenant hazard — one consumer's
|
|
5824
|
+
* downgrade dragged every other consumer down with it.
|
|
5825
|
+
*
|
|
5826
|
+
* Placement: unpinned, `classifyCapRoute` serves this hub-in-process
|
|
5827
|
+
* (Priority 1). A caller that wants the transcode elsewhere passes
|
|
5828
|
+
* `nodePin(nodeId)` and a `hostname` it can dial.
|
|
5829
|
+
*/
|
|
5830
|
+
acquireEgressTranscode: method(EgressTranscodeRequestSchema, EgressTranscodeSchema, {
|
|
5831
|
+
kind: "mutation",
|
|
5832
|
+
auth: "admin"
|
|
5833
|
+
}),
|
|
5834
|
+
/** Drop one reference. The child dies when the last holder releases. */
|
|
5835
|
+
releaseEgressTranscode: method(zod.z.object({ pipelineKey: zod.z.string() }), zod.z.object({
|
|
5836
|
+
released: zod.z.boolean(),
|
|
5837
|
+
refcount: zod.z.number().int().nonnegative()
|
|
5838
|
+
}), {
|
|
5839
|
+
kind: "mutation",
|
|
5840
|
+
auth: "admin"
|
|
5841
|
+
}),
|
|
5842
|
+
/**
|
|
5583
5843
|
* ── Decoded audio-chunk plane (Phase 5 / D9) ──────────────────────
|
|
5584
5844
|
*
|
|
5585
5845
|
* The serialisable replacement for the live-object `IStreamBroker.
|
|
@@ -11467,12 +11727,13 @@ var NcConditionsSchema = zod.z.object({
|
|
|
11467
11727
|
* source; otherwise the subject's source must equal it. Legacy records
|
|
11468
11728
|
* with no stamped source are treated as `pipeline`. The union spans both
|
|
11469
11729
|
* record kinds — object events carry `pipeline` | `onboard`, synthetic
|
|
11470
|
-
* tracks carry `sensor
|
|
11730
|
+
* tracks carry `sensor` (a linked device) or `audio` (a D62 audio marker).
|
|
11471
11731
|
*/
|
|
11472
11732
|
source: zod.z.enum([
|
|
11473
11733
|
"pipeline",
|
|
11474
11734
|
"onboard",
|
|
11475
11735
|
"sensor",
|
|
11736
|
+
"audio",
|
|
11476
11737
|
"any"
|
|
11477
11738
|
]).optional(),
|
|
11478
11739
|
/**
|
|
@@ -12060,6 +12321,35 @@ var notificationRulesCapability = {
|
|
|
12060
12321
|
auth: "admin"
|
|
12061
12322
|
}),
|
|
12062
12323
|
/**
|
|
12324
|
+
* PERMANENT per-camera mute — the notifications half of the per-camera
|
|
12325
|
+
* function switch group ([D61](../../../../docs/decisions/adr-0067.md)).
|
|
12326
|
+
*
|
|
12327
|
+
* Deliberately NOT a snooze. A snooze is bounded at
|
|
12328
|
+
* {@link NC_SNOOZE_MAX_MINUTES} on purpose — "a snooze that could not
|
|
12329
|
+
* expire would be an outage the operator asked for once and forgot" — and
|
|
12330
|
+
* widening it to express "this camera never notifies" would destroy that
|
|
12331
|
+
* property for every snooze. A mute is the other thing: an explicit,
|
|
12332
|
+
* indefinite, admin-only decision, visible in the switch group next to the
|
|
12333
|
+
* other four, and reported on `CameraStatus.switchedOff` so a silent
|
|
12334
|
+
* camera never reads as a working one.
|
|
12335
|
+
*
|
|
12336
|
+
* Returned as ONE list rather than a per-camera query: the group's reader
|
|
12337
|
+
* needs every camera's state, and a per-camera fan-out over the viewer's
|
|
12338
|
+
* single WebSocket is N frames serialised on one socket.
|
|
12339
|
+
*/
|
|
12340
|
+
listDeviceMutes: method(zod.z.object({}), zod.z.object({ mutedDeviceIds: zod.z.array(zod.z.number().int()).readonly() }), { auth: "admin" }),
|
|
12341
|
+
/**
|
|
12342
|
+
* Mute or unmute one camera. Idempotent; an unmute of a camera that was
|
|
12343
|
+
* never muted succeeds.
|
|
12344
|
+
*/
|
|
12345
|
+
setDeviceMuted: method(zod.z.object({
|
|
12346
|
+
deviceId: zod.z.number().int(),
|
|
12347
|
+
muted: zod.z.boolean()
|
|
12348
|
+
}), zod.z.object({ success: zod.z.literal(true) }), {
|
|
12349
|
+
kind: "mutation",
|
|
12350
|
+
auth: "admin"
|
|
12351
|
+
}),
|
|
12352
|
+
/**
|
|
12063
12353
|
* Dry-run a rule against recently persisted records (object events for
|
|
12064
12354
|
* `immediate`, closed tracks for `track-end`). Mutation kind only to
|
|
12065
12355
|
* carry the full rule object safely; no side effects.
|
|
@@ -12471,12 +12761,60 @@ var TrackAudioLabelSchema = zod.z.object({
|
|
|
12471
12761
|
});
|
|
12472
12762
|
/**
|
|
12473
12763
|
* How a track was produced. `pipeline` (default / absent) = the spatial
|
|
12474
|
-
* detection+tracking pipeline.
|
|
12475
|
-
*
|
|
12476
|
-
*
|
|
12477
|
-
*
|
|
12764
|
+
* detection+tracking pipeline. Every OTHER value is a SYNTHETIC projection —
|
|
12765
|
+
* no positions, a single snapshot, and no bbox trajectory at all:
|
|
12766
|
+
*
|
|
12767
|
+
* - `sensor` — a linked sensor/control device state change.
|
|
12768
|
+
* - `audio` — an audio event on the camera itself that was anomalous for
|
|
12769
|
+
* THAT camera, loud, and heard while nothing visual was happening (D62).
|
|
12770
|
+
*
|
|
12771
|
+
* The spatial subsystems (tracker association, occupancy count, re-id /
|
|
12772
|
+
* embedding, resurrection) MUST skip every synthetic source. Test for that
|
|
12773
|
+
* with `isSpatialTrack`, which allow-lists `pipeline` — a `!== 'sensor'`
|
|
12774
|
+
* check silently readmits every source added after it was written.
|
|
12775
|
+
*/
|
|
12776
|
+
var TrackSourceSchema = zod.z.enum([
|
|
12777
|
+
"pipeline",
|
|
12778
|
+
"sensor",
|
|
12779
|
+
"audio"
|
|
12780
|
+
]);
|
|
12781
|
+
/**
|
|
12782
|
+
* Per-track OPERATOR flags — set by hand from the admin UI or the viewer, never
|
|
12783
|
+
* by the pipeline. Spread into `TrackSchema` and `KeyEventSchema` from one place
|
|
12784
|
+
* so the two surfaces cannot drift.
|
|
12785
|
+
*
|
|
12786
|
+
* **Absent ≠ false.** A track that has never been touched omits the field; an
|
|
12787
|
+
* explicitly un-flagged track carries `false`. Legacy rows written before the
|
|
12788
|
+
* columns existed read as absent, and a consumer that needs a boolean should say
|
|
12789
|
+
* `flag === true`, not `flag !== false`.
|
|
12790
|
+
*
|
|
12791
|
+
* What the flags DO is deliberately UNDEFINED at the time of writing: they are
|
|
12792
|
+
* operator curation, and the behaviour they drive will be specified separately.
|
|
12793
|
+
* In particular a `markForTrain` track is NOT pinned against retention — see
|
|
12794
|
+
* `docs/decisions/adr-0059.md` for why that is a store-level change, not a flag.
|
|
12795
|
+
*/
|
|
12796
|
+
var TrackFlagFields = {
|
|
12797
|
+
/** Operator marked this track as training material. */
|
|
12798
|
+
markForTrain: zod.z.boolean().optional(),
|
|
12799
|
+
/** Operator marked this track for diagnostic attention. */
|
|
12800
|
+
debug: zod.z.boolean().optional()
|
|
12801
|
+
};
|
|
12802
|
+
/**
|
|
12803
|
+
* The write half: a PARTIAL patch. An omitted key is left untouched, so setting
|
|
12804
|
+
* one flag can never clear the other — the toggles are independent and are
|
|
12805
|
+
* driven from three surfaces that do not know about each other.
|
|
12478
12806
|
*/
|
|
12479
|
-
var
|
|
12807
|
+
var TrackFlagsPatchSchema = zod.z.object(TrackFlagFields);
|
|
12808
|
+
/**
|
|
12809
|
+
* The resolved flag state after a write. Both fields are REQUIRED here (absent
|
|
12810
|
+
* collapses to `false`) so a caller can drive a toggle's checked state off the
|
|
12811
|
+
* mutation result without a re-fetch.
|
|
12812
|
+
*/
|
|
12813
|
+
var TrackFlagsSchema = zod.z.object({
|
|
12814
|
+
trackId: zod.z.string(),
|
|
12815
|
+
markForTrain: zod.z.boolean(),
|
|
12816
|
+
debug: zod.z.boolean()
|
|
12817
|
+
});
|
|
12480
12818
|
var TrackSchema = zod.z.object({
|
|
12481
12819
|
trackId: zod.z.string(),
|
|
12482
12820
|
deviceId: zod.z.number(),
|
|
@@ -12519,7 +12857,8 @@ var TrackSchema = zod.z.object({
|
|
|
12519
12857
|
/** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
|
|
12520
12858
|
* Populated from the persisted envelope columns on historical reads;
|
|
12521
12859
|
* absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
|
|
12522
|
-
envelope: TrackEnvelopeSchema.optional()
|
|
12860
|
+
envelope: TrackEnvelopeSchema.optional(),
|
|
12861
|
+
...TrackFlagFields
|
|
12523
12862
|
});
|
|
12524
12863
|
var BaseEventFields = {
|
|
12525
12864
|
id: zod.z.string(),
|
|
@@ -12740,7 +13079,8 @@ var KeyEventSchema = zod.z.object({
|
|
|
12740
13079
|
/** Highest-confidence ObjectEvent id for the track (empty when none). */
|
|
12741
13080
|
bestEventId: zod.z.string(),
|
|
12742
13081
|
/** Track lifetime in ms (lastSeen - firstSeen). */
|
|
12743
|
-
windowMs: zod.z.number().optional()
|
|
13082
|
+
windowMs: zod.z.number().optional(),
|
|
13083
|
+
...TrackFlagFields
|
|
12744
13084
|
});
|
|
12745
13085
|
zod.z.object({
|
|
12746
13086
|
trackId: zod.z.string(),
|
|
@@ -12826,7 +13166,31 @@ var RebuildObjectEmbeddingsInput = zod.z.object({
|
|
|
12826
13166
|
since: zod.z.number().optional(),
|
|
12827
13167
|
until: zod.z.number().optional(),
|
|
12828
13168
|
/** Stop after this many tracks; the result reports whether more remain. */
|
|
12829
|
-
maxTracks: zod.z.number().int().positive().optional()
|
|
13169
|
+
maxTracks: zod.z.number().int().positive().optional(),
|
|
13170
|
+
/**
|
|
13171
|
+
* Run every embedding on THIS node instead of round-robining the fleet.
|
|
13172
|
+
*
|
|
13173
|
+
* Named `executeOnNodeId` and not `nodeId` on purpose: an inline `nodeId`
|
|
13174
|
+
* field in cap args is read by `parent-unowned-call.ts` as a ROUTING PIN, so
|
|
13175
|
+
* calling it that would pin the rebuild REQUEST itself to that node — the
|
|
13176
|
+
* rebuild orchestration lives on the hub, and only the per-track step runs
|
|
13177
|
+
* remotely. This field is data; the per-track pin is applied inside.
|
|
13178
|
+
*
|
|
13179
|
+
* Absent ⇒ round-robin over every online node whose runner can serve the
|
|
13180
|
+
* pinned model.
|
|
13181
|
+
*/
|
|
13182
|
+
executeOnNodeId: zod.z.string().optional(),
|
|
13183
|
+
/**
|
|
13184
|
+
* Milliseconds to wait between tracks; omit for the built-in default, `0` to
|
|
13185
|
+
* run flat out.
|
|
13186
|
+
*
|
|
13187
|
+
* A rebuild is bulk maintenance on hub-main's single thread. Measured
|
|
13188
|
+
* 2026-08-06, an unpaced pass held that thread busy 82.2 s out of 120 and
|
|
13189
|
+
* pushed `nodes.topology` from 0.25 s to 26 s for 43 minutes. The value in
|
|
13190
|
+
* force is logged at start and finish so a deliberately slow pass reads
|
|
13191
|
+
* differently from a stalled one.
|
|
13192
|
+
*/
|
|
13193
|
+
pacingMs: zod.z.number().int().nonnegative().optional()
|
|
12830
13194
|
});
|
|
12831
13195
|
/**
|
|
12832
13196
|
* Result of emptying the CLIP index.
|
|
@@ -12860,13 +13224,23 @@ var RebuildStatusSchema = zod.z.object({
|
|
|
12860
13224
|
/** Tracks with no usable detection box. */
|
|
12861
13225
|
missingBbox: zod.z.number(),
|
|
12862
13226
|
/**
|
|
12863
|
-
* Tracks
|
|
12864
|
-
*
|
|
12865
|
-
*
|
|
12866
|
-
*
|
|
12867
|
-
* otherwise read as a total engine outage.
|
|
13227
|
+
* Tracks an executing node REFUSED rather than broke on — an unreadable key
|
|
13228
|
+
* frame, a step that threw. Separate from `failed` because the remedy is
|
|
13229
|
+
* different, and because a whole camera silently contributing zero vectors
|
|
13230
|
+
* is the shape of failure a rebuild must never hide.
|
|
12868
13231
|
*/
|
|
12869
13232
|
notRunnable: zod.z.number(),
|
|
13233
|
+
/**
|
|
13234
|
+
* The pass stopped because NO node could serve the pinned model.
|
|
13235
|
+
*
|
|
13236
|
+
* Distinct from `notRunnable` on purpose: that one says "this track was
|
|
13237
|
+
* refused", this one says "the cluster cannot do this work at all" — every
|
|
13238
|
+
* candidate node either lacks the `clip-embedding` step, lacks a build of the
|
|
13239
|
+
* pinned model for its engine format, or dropped out. The remedy is a model /
|
|
13240
|
+
* engine change, not a per-camera one. Non-zero here always comes with
|
|
13241
|
+
* `complete: false`.
|
|
13242
|
+
*/
|
|
13243
|
+
noCapableNode: zod.z.number(),
|
|
12870
13244
|
failed: zod.z.number(),
|
|
12871
13245
|
/** Set once a pass ends: true only when EVERYTHING was covered. */
|
|
12872
13246
|
complete: zod.z.boolean().nullable(),
|
|
@@ -13056,6 +13430,31 @@ var pipelineAnalyticsCapability = {
|
|
|
13056
13430
|
auth: "admin"
|
|
13057
13431
|
}),
|
|
13058
13432
|
/**
|
|
13433
|
+
* Set the per-track operator flags (`markForTrain`, `debug`) on ONE track.
|
|
13434
|
+
* The patch is PARTIAL — an omitted key is left untouched — because the
|
|
13435
|
+
* three surfaces that write it (admin Events grid, viewer track detail,
|
|
13436
|
+
* viewer cluster detail) each own one toggle and must not clobber the other.
|
|
13437
|
+
*
|
|
13438
|
+
* Writes the track ROW: `markForTrain`/`debug` are per-TRACK state, so they
|
|
13439
|
+
* live where `label` and `importance` live, not in any per-device settings
|
|
13440
|
+
* store. Updates the in-RAM active track too, so a flag set on a live track
|
|
13441
|
+
* survives its expiry-time persist.
|
|
13442
|
+
*
|
|
13443
|
+
* `auth: 'protected'` (the default), NOT `admin`: the viewer is an
|
|
13444
|
+
* authenticated non-admin surface and two of the three call sites are
|
|
13445
|
+
* there. Revisit if a flag ever gains an effect that costs storage —
|
|
13446
|
+
* `deleteTracks` next door is admin for exactly that reason.
|
|
13447
|
+
*
|
|
13448
|
+
* Returns the RESOLVED state of both flags (absent → `false`) so a caller
|
|
13449
|
+
* can drive its toggle without a re-fetch. Rejects an unknown track.
|
|
13450
|
+
*/
|
|
13451
|
+
setTrackFlags: method(zod.z.object({
|
|
13452
|
+
/** Log/audit scope only — the trackId is globally unique on its own. */
|
|
13453
|
+
deviceId: zod.z.number(),
|
|
13454
|
+
trackId: zod.z.string(),
|
|
13455
|
+
flags: TrackFlagsPatchSchema
|
|
13456
|
+
}), TrackFlagsSchema, { kind: "mutation" }),
|
|
13457
|
+
/**
|
|
13059
13458
|
* Durable event-store footprint for the management UI: event rows
|
|
13060
13459
|
* (motion + object + audio) counted per camera + total, plus the
|
|
13061
13460
|
* event-owned media bytes on disk per camera + total. Stat/count-based,
|
|
@@ -13935,6 +14334,53 @@ var DetailResultSchema = zod.z.object({
|
|
|
13935
14334
|
nativeFaceShortSidePx: zod.z.number().optional()
|
|
13936
14335
|
});
|
|
13937
14336
|
/**
|
|
14337
|
+
* Why an executing node REFUSED a stateless step run (`runStatelessStep`).
|
|
14338
|
+
*
|
|
14339
|
+
* A refusal is a first-class answer, not an error, because the caller's next
|
|
14340
|
+
* move depends on WHICH one it is — and because "the pass produced nothing"
|
|
14341
|
+
* must never be reachable without a named, counted cause. The two tiers:
|
|
14342
|
+
*
|
|
14343
|
+
* - **node-level** (`unknown-step`, `model-not-servable`) — this node can
|
|
14344
|
+
* never serve this (step, model) pair. The caller drops it from its rotation
|
|
14345
|
+
* and retries the same work elsewhere; nothing about the work changes.
|
|
14346
|
+
* - **work-level** (`unreadable-frame`, `execution-failed`) — this node is
|
|
14347
|
+
* fine, this one request is not. Retrying it on another node would only
|
|
14348
|
+
* spread the same failure.
|
|
14349
|
+
*/
|
|
14350
|
+
var StatelessStepRefusalSchema = zod.z.enum([
|
|
14351
|
+
"unknown-step",
|
|
14352
|
+
"model-not-servable",
|
|
14353
|
+
"unreadable-frame",
|
|
14354
|
+
"execution-failed"
|
|
14355
|
+
]);
|
|
14356
|
+
/**
|
|
14357
|
+
* Answer to `runStatelessStep` — a discriminated union rather than a nullable
|
|
14358
|
+
* result, because `null` is exactly what made the camera-bound detail path
|
|
14359
|
+
* unable to tell "refused" from "never asked".
|
|
14360
|
+
*/
|
|
14361
|
+
var RunStatelessStepResultSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
|
|
14362
|
+
kind: zod.z.literal("ran"),
|
|
14363
|
+
/** The node that actually executed it — the pin, echoed back for the log. */
|
|
14364
|
+
nodeId: zod.z.string(),
|
|
14365
|
+
/**
|
|
14366
|
+
* The model the step ran with.
|
|
14367
|
+
*
|
|
14368
|
+
* The node verified this exact id has a build for the format it dispatched
|
|
14369
|
+
* on BEFORE running, so the executor's format resolution returns it
|
|
14370
|
+
* unchanged. A caller that pinned a model must compare this field and
|
|
14371
|
+
* treat a mismatch as a refusal — the whole point of the pin is that a
|
|
14372
|
+
* pass writes one feature space.
|
|
14373
|
+
*/
|
|
14374
|
+
modelId: zod.z.string(),
|
|
14375
|
+
details: zod.z.array(DetailResultSchema)
|
|
14376
|
+
}), zod.z.object({
|
|
14377
|
+
kind: zod.z.literal("refused"),
|
|
14378
|
+
nodeId: zod.z.string(),
|
|
14379
|
+
reason: StatelessStepRefusalSchema,
|
|
14380
|
+
/** Human-readable specifics — the format tried, the formats shipped, etc. */
|
|
14381
|
+
detail: zod.z.string()
|
|
14382
|
+
})]);
|
|
14383
|
+
/**
|
|
13938
14384
|
* Per-camera tunable ranges + defaults. Single source of truth used
|
|
13939
14385
|
* by both the Zod data schema (validation + default fallback) and
|
|
13940
14386
|
* the device settings UI (slider min/max/step). Touch one place and
|
|
@@ -14379,7 +14825,77 @@ var pipelineRunnerCapability = {
|
|
|
14379
14825
|
cropJpeg: zod.z.string().optional(),
|
|
14380
14826
|
parent: DetailParentSchema,
|
|
14381
14827
|
steps: zod.z.array(zod.z.string()).optional()
|
|
14382
|
-
}), zod.z.object({ details: zod.z.array(DetailResultSchema) }).nullable(), { kind: "mutation" })
|
|
14828
|
+
}), zod.z.object({ details: zod.z.array(DetailResultSchema) }).nullable(), { kind: "mutation" }),
|
|
14829
|
+
/**
|
|
14830
|
+
* Run ONE enrichment step against caller-supplied pixels, with NO camera
|
|
14831
|
+
* session — no attach, no frame handle, no device affinity.
|
|
14832
|
+
*
|
|
14833
|
+
* ## Why this exists next to `runDetailSubtree` and not inside it
|
|
14834
|
+
*
|
|
14835
|
+
* `runDetailSubtree` resolves its step tree from the LIVE ATTACH STATE. That
|
|
14836
|
+
* is correct for it: its reason to exist is a frame HANDLE, and a handle is
|
|
14837
|
+
* only resolvable while a decode session is open. But it makes the method
|
|
14838
|
+
* useless to anything walking history — cameras run `detectionMode:
|
|
14839
|
+
* 'on-motion'` and detach ~30 s after motion stops, so a fleet-wide
|
|
14840
|
+
* embedding rebuild found most cameras detached and rebuilt only whatever
|
|
14841
|
+
* happened to be attached at that second (measured 2026-08-06: 1125 tracks
|
|
14842
|
+
* scanned, 121 rebuilt, 601 refused as "camera is not attached").
|
|
14843
|
+
*
|
|
14844
|
+
* A caller that ships its own pixels needs none of that machinery. So this
|
|
14845
|
+
* method takes the three things a step actually needs — an image, a box, a
|
|
14846
|
+
* step id — resolves the step from the CATALOG rather than from an
|
|
14847
|
+
* attachment, and runs it on whichever node the caller pinned.
|
|
14848
|
+
*
|
|
14849
|
+
* ## What it deliberately keeps
|
|
14850
|
+
*
|
|
14851
|
+
* The crop rectangle is derived by the SAME `deriveDetailCropRect` call the
|
|
14852
|
+
* live detail plane uses, from the same cluster-wide convention
|
|
14853
|
+
* ([D52](../../../docs/decisions/adr-0052.md)). `frameJpeg` is a FULL frame,
|
|
14854
|
+
* never a pre-cut tile, for exactly the reason `runDetailSubtree` documents:
|
|
14855
|
+
* a caller that cuts is a second feature space.
|
|
14856
|
+
*
|
|
14857
|
+
* ## What it deliberately drops
|
|
14858
|
+
*
|
|
14859
|
+
* No device jump and no `deviceKey`: the jump roster comes from the attach
|
|
14860
|
+
* config, which does not exist here. The step runs on the node's default
|
|
14861
|
+
* engine. This is a maintenance path, not a latency-sensitive one.
|
|
14862
|
+
*
|
|
14863
|
+
* ## Routing
|
|
14864
|
+
*
|
|
14865
|
+
* NOT device-bound. `sourceDeviceId` is DIAGNOSTIC — the camera whose track
|
|
14866
|
+
* these pixels came from, so every log line on the executing node can carry
|
|
14867
|
+
* `tags: { deviceId }`. It is deliberately not named `deviceId`: that field
|
|
14868
|
+
* is a routing hint that would send the call to the camera's owning node,
|
|
14869
|
+
* which is the placement constraint this method exists to remove. Callers
|
|
14870
|
+
* choose the node with `nodePin(nodeId)`; unpinned, an unowned call is
|
|
14871
|
+
* served hub-in-process like any other singleton.
|
|
14872
|
+
*/
|
|
14873
|
+
runStatelessStep: method(zod.z.object({
|
|
14874
|
+
/** Catalog step id, e.g. `clip-embedding`. */
|
|
14875
|
+
stepId: zod.z.string(),
|
|
14876
|
+
/**
|
|
14877
|
+
* REQUIRED model pin. The node runs this exact model or refuses with
|
|
14878
|
+
* `model-not-servable` — it never substitutes a format default, because
|
|
14879
|
+
* a fleet pass that round-robins across nodes would then fill one index
|
|
14880
|
+
* from several encoders.
|
|
14881
|
+
*/
|
|
14882
|
+
modelId: zod.z.string(),
|
|
14883
|
+
/** FULL FRAME, base64 JPEG. The runner cuts — do NOT pre-crop. */
|
|
14884
|
+
frameJpeg: zod.z.string(),
|
|
14885
|
+
/**
|
|
14886
|
+
* The subject box, NORMALISED [0,1] against `frameJpeg`. Normalised on
|
|
14887
|
+
* purpose: the caller stores boxes against a downscaled analysis frame
|
|
14888
|
+
* while the stored key frame is native-resolution, and the only side
|
|
14889
|
+
* that reliably knows the image's pixel dimensions is the side that
|
|
14890
|
+
* decodes it. Denormalising here removes a second reader of the
|
|
14891
|
+
* dimensions and the class of mismatch that comes with it.
|
|
14892
|
+
*/
|
|
14893
|
+
bbox: NativeCropBboxSchema,
|
|
14894
|
+
/** Parent class of the subject (`person`, `vehicle`, …) — carried into the result. */
|
|
14895
|
+
className: zod.z.string(),
|
|
14896
|
+
/** Camera the pixels came from. Diagnostics + log tags ONLY — never routing. */
|
|
14897
|
+
sourceDeviceId: zod.z.number()
|
|
14898
|
+
}), RunStatelessStepResultSchema, { kind: "mutation" })
|
|
14383
14899
|
}
|
|
14384
14900
|
};
|
|
14385
14901
|
var CameraPipelineConfigSchema = zod.z.object({
|
|
@@ -14687,6 +15203,20 @@ var CameraStatusSchema = zod.z.object({
|
|
|
14687
15203
|
detection: CameraDetectionStatusSchema.nullable(),
|
|
14688
15204
|
audio: CameraAudioStatusSchema.nullable(),
|
|
14689
15205
|
recording: CameraRecordingStatusSchema.nullable(),
|
|
15206
|
+
/**
|
|
15207
|
+
* Per-camera function switches an OPERATOR has turned off
|
|
15208
|
+
* ([D61](../../../../docs/decisions/adr-0067.md)).
|
|
15209
|
+
*
|
|
15210
|
+
* This is the difference between DISABLED and BROKEN. A camera whose
|
|
15211
|
+
* `detection` block reports zero fps and whose `switchedOff` contains
|
|
15212
|
+
* `'object-detection'` was switched off by a person; the same camera with an
|
|
15213
|
+
* empty list is failing. Every status surface must render the two
|
|
15214
|
+
* differently — a quiet camera that looks identical to a dead one is the
|
|
15215
|
+
* silence-reads-as-never-happened trap this repo keeps paying for.
|
|
15216
|
+
*
|
|
15217
|
+
* Empty when nothing is off. Never contains a switch no provider offers.
|
|
15218
|
+
*/
|
|
15219
|
+
switchedOff: zod.z.array(CameraSwitchIdSchema).readonly(),
|
|
14690
15220
|
/** Unix timestamp (ms) when this snapshot was composed server-side. */
|
|
14691
15221
|
fetchedAt: zod.z.number()
|
|
14692
15222
|
});
|
|
@@ -15086,6 +15616,43 @@ var pipelineOrchestratorCapability = {
|
|
|
15086
15616
|
agentNodeId: zod.z.string().optional()
|
|
15087
15617
|
}), CameraPipelineConfigSchema),
|
|
15088
15618
|
/**
|
|
15619
|
+
* The whole per-camera function switch group, DERIVED — never a stored
|
|
15620
|
+
* list ([D61](../../../../docs/decisions/adr-0067.md)).
|
|
15621
|
+
*
|
|
15622
|
+
* The group adds no state. Each switch is a view onto the authority that
|
|
15623
|
+
* already owned it (`deviceManager.setDisabled`,
|
|
15624
|
+
* `deviceManager.setWrapperActive`, `RecordingConfig.enabled`,
|
|
15625
|
+
* `notificationRules.setDeviceMuted`), and `switch.authority` says which.
|
|
15626
|
+
* Availability comes from `deviceManager.listBindableCapsForDeviceType`,
|
|
15627
|
+
* so a deployment with no audio analyzer renders no audio switch.
|
|
15628
|
+
*
|
|
15629
|
+
* `auth: 'view'` deliberately — a NON-admin must be able to see that a
|
|
15630
|
+
* camera is quiet because somebody switched it off. Only the mutation is
|
|
15631
|
+
* admin-gated.
|
|
15632
|
+
*/
|
|
15633
|
+
getCameraSwitches: method(zod.z.object({ deviceId: zod.z.number() }), CameraSwitchGroupSchema),
|
|
15634
|
+
/**
|
|
15635
|
+
* Flip ONE switch, routed to its existing authority.
|
|
15636
|
+
*
|
|
15637
|
+
* Never writes a parallel map: `recording` patches `RecordingConfig.enabled`
|
|
15638
|
+
* and leaves `bands` byte-identical (clearing bands to express "off"
|
|
15639
|
+
* destroys the operator's authored schedule and turning the camera back on
|
|
15640
|
+
* would silently record nothing), and the two pipeline switches write the
|
|
15641
|
+
* SAME wrapper binding the legacy `pipelineEnabled` / `audioEnabled`
|
|
15642
|
+
* booleans were migrated onto.
|
|
15643
|
+
*
|
|
15644
|
+
* Rejects a switch this camera does not offer rather than persisting a
|
|
15645
|
+
* write nothing reads.
|
|
15646
|
+
*/
|
|
15647
|
+
setCameraSwitch: method(zod.z.object({
|
|
15648
|
+
deviceId: zod.z.number(),
|
|
15649
|
+
switchId: CameraSwitchIdSchema,
|
|
15650
|
+
enabled: zod.z.boolean()
|
|
15651
|
+
}), CameraSwitchGroupSchema, {
|
|
15652
|
+
kind: "mutation",
|
|
15653
|
+
auth: "admin"
|
|
15654
|
+
}),
|
|
15655
|
+
/**
|
|
15089
15656
|
* Server-composed aggregated status for a single camera.
|
|
15090
15657
|
*
|
|
15091
15658
|
* Fans out in parallel (bounded, per-stage graceful degradation) to
|
|
@@ -27811,6 +28378,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
27811
28378
|
addonId: null,
|
|
27812
28379
|
access: "view"
|
|
27813
28380
|
},
|
|
28381
|
+
"notificationRules.listDeviceMutes": {
|
|
28382
|
+
capName: "notification-rules",
|
|
28383
|
+
capScope: "system",
|
|
28384
|
+
addonId: null,
|
|
28385
|
+
access: "view"
|
|
28386
|
+
},
|
|
27814
28387
|
"notificationRules.listRules": {
|
|
27815
28388
|
capName: "notification-rules",
|
|
27816
28389
|
capScope: "system",
|
|
@@ -27829,6 +28402,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
27829
28402
|
addonId: null,
|
|
27830
28403
|
access: "create"
|
|
27831
28404
|
},
|
|
28405
|
+
"notificationRules.setDeviceMuted": {
|
|
28406
|
+
capName: "notification-rules",
|
|
28407
|
+
capScope: "system",
|
|
28408
|
+
addonId: null,
|
|
28409
|
+
access: "create"
|
|
28410
|
+
},
|
|
27832
28411
|
"notificationRules.setRuleEnabled": {
|
|
27833
28412
|
capName: "notification-rules",
|
|
27834
28413
|
capScope: "system",
|
|
@@ -28105,6 +28684,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
28105
28684
|
addonId: null,
|
|
28106
28685
|
access: "view"
|
|
28107
28686
|
},
|
|
28687
|
+
"pipelineAnalytics.setTrackFlags": {
|
|
28688
|
+
capName: "pipeline-analytics",
|
|
28689
|
+
capScope: "device",
|
|
28690
|
+
addonId: null,
|
|
28691
|
+
access: "create"
|
|
28692
|
+
},
|
|
28108
28693
|
"pipelineAnalytics.wipeAllAnalytics": {
|
|
28109
28694
|
capName: "pipeline-analytics",
|
|
28110
28695
|
capScope: "device",
|
|
@@ -28411,6 +28996,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
28411
28996
|
addonId: null,
|
|
28412
28997
|
access: "view"
|
|
28413
28998
|
},
|
|
28999
|
+
"pipelineOrchestrator.getCameraSwitches": {
|
|
29000
|
+
capName: "pipeline-orchestrator",
|
|
29001
|
+
capScope: "system",
|
|
29002
|
+
addonId: null,
|
|
29003
|
+
access: "view"
|
|
29004
|
+
},
|
|
28414
29005
|
"pipelineOrchestrator.getCapabilityBindings": {
|
|
28415
29006
|
capName: "pipeline-orchestrator",
|
|
28416
29007
|
capScope: "system",
|
|
@@ -28543,6 +29134,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
28543
29134
|
addonId: null,
|
|
28544
29135
|
access: "create"
|
|
28545
29136
|
},
|
|
29137
|
+
"pipelineOrchestrator.setCameraSwitch": {
|
|
29138
|
+
capName: "pipeline-orchestrator",
|
|
29139
|
+
capScope: "system",
|
|
29140
|
+
addonId: null,
|
|
29141
|
+
access: "create"
|
|
29142
|
+
},
|
|
28546
29143
|
"pipelineOrchestrator.setCapabilityBinding": {
|
|
28547
29144
|
capName: "pipeline-orchestrator",
|
|
28548
29145
|
capScope: "system",
|
|
@@ -28633,6 +29230,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
28633
29230
|
addonId: null,
|
|
28634
29231
|
access: "create"
|
|
28635
29232
|
},
|
|
29233
|
+
"pipelineRunner.runStatelessStep": {
|
|
29234
|
+
capName: "pipeline-runner",
|
|
29235
|
+
capScope: "system",
|
|
29236
|
+
addonId: null,
|
|
29237
|
+
access: "create"
|
|
29238
|
+
},
|
|
28636
29239
|
"plateGallery.assignPlate": {
|
|
28637
29240
|
capName: "plate-gallery",
|
|
28638
29241
|
capScope: "system",
|
|
@@ -29449,6 +30052,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
29449
30052
|
addonId: null,
|
|
29450
30053
|
access: "create"
|
|
29451
30054
|
},
|
|
30055
|
+
"streamBroker.acquireEgressTranscode": {
|
|
30056
|
+
capName: "stream-broker",
|
|
30057
|
+
capScope: "system",
|
|
30058
|
+
addonId: null,
|
|
30059
|
+
access: "create"
|
|
30060
|
+
},
|
|
29452
30061
|
"streamBroker.assignProfile": {
|
|
29453
30062
|
capName: "stream-broker",
|
|
29454
30063
|
capScope: "system",
|
|
@@ -29557,6 +30166,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
29557
30166
|
addonId: null,
|
|
29558
30167
|
access: "create"
|
|
29559
30168
|
},
|
|
30169
|
+
"streamBroker.releaseEgressTranscode": {
|
|
30170
|
+
capName: "stream-broker",
|
|
30171
|
+
capScope: "system",
|
|
30172
|
+
addonId: null,
|
|
30173
|
+
access: "create"
|
|
30174
|
+
},
|
|
29560
30175
|
"streamBroker.releaseStreamWithCodec": {
|
|
29561
30176
|
capName: "stream-broker",
|
|
29562
30177
|
capScope: "system",
|