@camstack/addon-admin-ui 1.2.31 → 1.2.32
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/assets/{AddLocationWizard-C8l2xgGA.js → AddLocationWizard-Bqn51iVc.js} +1 -1
- package/dist/assets/{AddonCollectionPage-C6c6iSQR.js → AddonCollectionPage-D9EE6p80.js} +1 -1
- package/dist/assets/Addons-Bp55KXZR.js +1 -0
- package/dist/assets/{AdminTabs-BlETE4he.js → AdminTabs-DB9TozBh.js} +1 -1
- package/dist/assets/AiProfiles-C4ji0awJ.js +1 -0
- package/dist/assets/{Automation-C28vW-50.js → Automation-BmDGNoTh.js} +2 -2
- package/dist/assets/{BrokerForm-DV5P6yLC.js → BrokerForm-CtjETVv8.js} +1 -1
- package/dist/assets/{Capabilities-I7OecGck.js → Capabilities-BlEUfcGN.js} +1 -1
- package/dist/assets/{CapabilityBadges-CeffiM3_.js → CapabilityBadges-CTZ7zqWM.js} +1 -1
- package/dist/assets/{Cluster-CtsElqbm.js → Cluster-CkGq5KVh.js} +1 -1
- package/dist/assets/{ClusterRecordingTab-Cu4e2Sa0.js → ClusterRecordingTab-nszIK7qG.js} +1 -1
- package/dist/assets/{Dashboard-BV2-sKn8.js → Dashboard-CO5PTZ9n.js} +1 -1
- package/dist/assets/{Data-D_in2E11.js → Data-CYwE4-K2.js} +1 -1
- package/dist/assets/DetectionIntelligence-MaxZ_sn8.js +1 -0
- package/dist/assets/{DeviceAggregateSection-BM70M394.js → DeviceAggregateSection-C6sscd95.js} +1 -1
- package/dist/assets/DeviceDetail-KHB2dWaR.js +2 -0
- package/dist/assets/{Devices-B3eRW0ZV.js → Devices-xlmwhKxI.js} +1 -1
- package/dist/assets/EmbedPlayerPage-CLGFNg5C.js +1 -0
- package/dist/assets/Events-EfdVWQGS.js +1 -0
- package/dist/assets/{FormBuilder-CjOCkLVf.js → FormBuilder-ZuE9B0w9.js} +1 -1
- package/dist/assets/Identity-CKR7bmrK.js +1 -0
- package/dist/assets/{IntegrationDetail-AqDFph4_.js → IntegrationDetail-DUkC3dFN.js} +1 -1
- package/dist/assets/{Integrations-UekIuTxN.js → Integrations-BuhF2y9p.js} +1 -1
- package/dist/assets/{Integrations-5slYYtBd.js → Integrations-t8-3dqN4.js} +1 -1
- package/dist/assets/{Logs-CcRjhQ8q.js → Logs-D1sMKNHu.js} +1 -1
- package/dist/assets/{MotionZonesSettings-CeuQ-dQ4.js → MotionZonesSettings-JUP-5dky.js} +1 -1
- package/dist/assets/{MyAccess-C6ZM2f5P.js → MyAccess-BSkJTbz8.js} +1 -1
- package/dist/assets/{Network-D0CKemO3.js → Network-CXKyrgFS.js} +1 -1
- package/dist/assets/{NodeAddonsSettingsPanel-D2e6v5bt.js → NodeAddonsSettingsPanel-1C9rZfoa.js} +1 -1
- package/dist/assets/{NodeInferenceDevices-BCis98Y5.js → NodeInferenceDevices-D1yIrxri.js} +1 -1
- package/dist/assets/{Notifications-DITbe32-.js → Notifications-gurR5fDh.js} +1 -1
- package/dist/assets/Pipeline-DTKomVZV.js +1 -0
- package/dist/assets/{Plates-BJe-z0Ly.js → Plates-CQ4aoncO.js} +1 -1
- package/dist/assets/{PrivacyMaskSettings-DL1wRk3D.js → PrivacyMaskSettings-CfvJZ3tP.js} +1 -1
- package/dist/assets/{ProviderIcon-DTUqxb3A.js → ProviderIcon-ClXY1rL4.js} +1 -1
- package/dist/assets/{Recordings-DsFS0v_V.js → Recordings-DglOV8NW.js} +1 -1
- package/dist/assets/{ScanNetworkModal-Sh8DKc_Z.js → ScanNetworkModal-DmZyDQIW.js} +1 -1
- package/dist/assets/{Settings-CX4Nf9gj.js → Settings-BVw2pnr7.js} +1 -1
- package/dist/assets/{Showroom-BMroQnR-.js → Showroom-9BtpR6nD.js} +1 -1
- package/dist/assets/{Terminal-WVzNWGgt.js → Terminal-C-v8B3we.js} +2 -2
- package/dist/assets/__vite-browser-external-DQHwpgVa.js +64 -0
- package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-ueW3CNXD.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-BtA4eogJ.js} +1 -1
- package/dist/assets/{build-cluster-graph-BQfyn5-h.js → build-cluster-graph-6UMcPt27.js} +2 -2
- package/dist/assets/circle-check-big-5J4vhHcH.js +1 -0
- package/dist/assets/clock-D19QkJv2.js +1 -0
- package/dist/assets/{copy-CgqX6jAd.js → copy-BObtPXKC.js} +1 -1
- package/dist/assets/{debounced-invalidate-Ckd5veqP.js → debounced-invalidate-Sygznr_T.js} +1 -1
- package/dist/assets/dist-Bso6V1TI.js +7 -0
- package/dist/assets/{dist-CWZqSYg7.js → dist-W1UImz79.js} +1 -1
- package/dist/assets/{download-p0j7iBId.js → download-DsIysR_y.js} +1 -1
- package/dist/assets/{face-bbox-D1cykRSA.js → face-bbox-DjqvdhYZ.js} +1 -1
- package/dist/assets/{hostInit-Dt2ASvrJ.js → hostInit-BUHMjqwF.js} +1 -1
- package/dist/assets/index-BH_HqZVJ.css +2 -0
- package/dist/assets/index-BqCgqp9V.js +4 -0
- package/dist/assets/{key-round-Cx7q2or5.js → key-round-DF1dcL0w.js} +1 -1
- package/dist/assets/{mf-entry-bootstrap-0-ba8db39e.js → mf-entry-bootstrap-0-62a13374.js} +2 -2
- package/dist/assets/{pencil-CSOkVyOg.js → pencil-CQwfEybQ.js} +1 -1
- package/dist/assets/{player-overlays-DMopxtfV.js → player-overlays-CJ2EDXmI.js} +1 -1
- package/dist/assets/plus-BIR1e9Y7.js +1 -0
- package/dist/assets/{radio-sWK9osbk.js → radio-C754NrLM.js} +1 -1
- package/dist/assets/{refresh-cw-DvvUyPxz.js → refresh-cw-DAm7QoFj.js} +1 -1
- package/dist/assets/remoteEntry-EVtyXnE6.js +1 -0
- package/dist/assets/{rotate-ccw-C9AgJ2n3.js → rotate-ccw-ildFCltM.js} +1 -1
- package/dist/assets/rotate-cw-DyrhB96Y.js +1 -0
- package/dist/assets/search-BFU3RCdH.js +1 -0
- package/dist/assets/{send-DHGacLkf.js → send-BMpnpOJi.js} +1 -1
- package/dist/assets/{server-BTkuOhTj.js → server-2ReeYjUz.js} +1 -1
- package/dist/assets/square-CjeJk_qb.js +1 -0
- package/dist/assets/{square-check-big-Bj-h9e8-.js → square-check-big-DUv4JqM3.js} +1 -1
- package/dist/assets/{src-DMF-vfTM.js → src-DCsdXZpS.js} +10 -10
- package/dist/assets/src-YgttllUb.js +7 -0
- package/dist/assets/{upload-DpVq2e2i.js → upload-Dvv16Zg3.js} +1 -1
- package/dist/assets/{useEventInvalidation-d8_SLAo2.js → useEventInvalidation-BI2Lc7mp.js} +1 -1
- package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-CxUAKqIn.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-D3wOFHTg.js} +2 -2
- package/dist/assets/{wifi-DTySHt8w.js → wifi-cBAahSiZ.js} +1 -1
- package/dist/assets/{zap-r1jKR3Im.js → zap-C86yuB6j.js} +1 -1
- package/dist/index.html +7 -7
- package/dist/server/addon.js +635 -16
- package/package.json +1 -1
- package/dist/assets/Addons-BltMkzm5.js +0 -1
- package/dist/assets/AiProfiles-BcPA79i8.js +0 -1
- package/dist/assets/DetectionIntelligence-DoDZdfkQ.js +0 -1
- package/dist/assets/DeviceDetail-BkHiJUVr.js +0 -2
- package/dist/assets/EmbedPlayerPage-BsSzvPTu.js +0 -1
- package/dist/assets/Events-CbDa9mNk.js +0 -1
- package/dist/assets/Identity-BOJJJ9fs.js +0 -1
- package/dist/assets/Pipeline-BuUTm7JS.js +0 -1
- package/dist/assets/__vite-browser-external-NEUUz2mh.js +0 -1
- package/dist/assets/circle-check-big-DeInsepB.js +0 -1
- package/dist/assets/clock-BW_cTo5d.js +0 -1
- package/dist/assets/compat-CbwIzIXq.js +0 -64
- package/dist/assets/dist-QF9uqGQD.js +0 -7
- package/dist/assets/index-BHm1FmD-.css +0 -2
- package/dist/assets/index-CCxo3WRY.js +0 -4
- package/dist/assets/plus-65CWfpkk.js +0 -1
- package/dist/assets/remoteEntry-BG0gquJL.js +0 -1
- package/dist/assets/rotate-cw-DzWdgqzc.js +0 -1
- package/dist/assets/search-C0NOsICx.js +0 -1
- package/dist/assets/square-5WfxRUZm.js +0 -1
- package/dist/assets/src-Bbly4y8Q.js +0 -7
package/dist/server/addon.js
CHANGED
|
@@ -6954,6 +6954,14 @@ var EncodeProfileSchema = object({
|
|
|
6954
6954
|
"main",
|
|
6955
6955
|
"high"
|
|
6956
6956
|
]).optional(),
|
|
6957
|
+
/**
|
|
6958
|
+
* `-level`, e.g. `'3.1'`. A consumer that ADVERTISES a level in its SDP
|
|
6959
|
+
* (`profile-level-id=42e01f` is Baseline 3.1) must constrain the encoder to
|
|
6960
|
+
* it, or it ships a stream that does not match its own advertisement — the
|
|
6961
|
+
* defect class that kept HomeKit black for a year and that Alexa carried
|
|
6962
|
+
* silently. Optional because a browser negotiates the level itself.
|
|
6963
|
+
*/
|
|
6964
|
+
level: string().optional(),
|
|
6957
6965
|
width: number().int().positive().optional(),
|
|
6958
6966
|
height: number().int().positive().optional(),
|
|
6959
6967
|
fps: number().positive().optional(),
|
|
@@ -7001,6 +7009,29 @@ var EncodeProfileSchema = object({
|
|
|
7001
7009
|
outputArgs: array(string()).optional()
|
|
7002
7010
|
});
|
|
7003
7011
|
/**
|
|
7012
|
+
* The shape every live egress starts from: H.264 Baseline 3.1 at 720p25.
|
|
7013
|
+
* Baseline because it is the one profile every consumer in this repo decodes
|
|
7014
|
+
* (Echo, iOS, an old browser); 3.1 because that is what the SDPs advertise.
|
|
7015
|
+
*/
|
|
7016
|
+
var BASE_LIVE_EGRESS_PROFILE = {
|
|
7017
|
+
video: {
|
|
7018
|
+
codec: "h264",
|
|
7019
|
+
profile: "baseline",
|
|
7020
|
+
level: "3.1",
|
|
7021
|
+
width: 1280,
|
|
7022
|
+
height: 720,
|
|
7023
|
+
fps: 25,
|
|
7024
|
+
bitrateKbps: 2500,
|
|
7025
|
+
gopFrames: 25,
|
|
7026
|
+
bf: 0,
|
|
7027
|
+
preset: "veryfast",
|
|
7028
|
+
tune: "zerolatency"
|
|
7029
|
+
},
|
|
7030
|
+
audio: "passthrough"
|
|
7031
|
+
};
|
|
7032
|
+
({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
|
|
7033
|
+
({ ...BASE_LIVE_EGRESS_PROFILE });
|
|
7034
|
+
/**
|
|
7004
7035
|
* Deep wiring healthcheck — snapshot of active reachability probes across
|
|
7005
7036
|
* every declared capability + widget of every installed plugin, on every
|
|
7006
7037
|
* node. Produced by the backend `WiringHealthService` and surfaced via
|
|
@@ -7050,6 +7081,105 @@ object({
|
|
|
7050
7081
|
})
|
|
7051
7082
|
});
|
|
7052
7083
|
/**
|
|
7084
|
+
* Per-camera FUNCTION SWITCHES — the one coherent on/off surface over the
|
|
7085
|
+
* pipeline functions an operator thinks in terms of.
|
|
7086
|
+
*
|
|
7087
|
+
* ## This file adds no state
|
|
7088
|
+
*
|
|
7089
|
+
* Every switch here is a VIEW onto an authority that already existed
|
|
7090
|
+
* ([D61](../../../../docs/decisions/adr-0062.md)). The whole point of the
|
|
7091
|
+
* group is that there is exactly one place each function is turned off, and
|
|
7092
|
+
* the group routes to it:
|
|
7093
|
+
*
|
|
7094
|
+
* | Switch | Authority | Proven "off stops the work" gate |
|
|
7095
|
+
* | --- | --- | --- |
|
|
7096
|
+
* | `stream-broker` | `deviceManager.setDisabled` | `StreamBrokerManager.reconcileAllCatalogs` releases the brokers; `ensureBroker` refuses re-creation |
|
|
7097
|
+
* | `object-detection` | `deviceManager.setWrapperActive('detection-pipeline')` | `PipelineSettingsStore.resolvePipelineForDevice` returns `{ steps: [], audio: null }` |
|
|
7098
|
+
* | `audio-analysis` | `deviceManager.setWrapperActive('audio-analysis')` | `AudioSubscriptionController.subscribeAudioStream` returns `null` before opening the stream |
|
|
7099
|
+
* | `recording` | `recording.setDeviceConfig` → `RecordingConfig.enabled` | `band-decision.shouldRecord` returns false; the controller detaches the device |
|
|
7100
|
+
* | `notifications` | `notificationRules.setDeviceMuted` | `NotificationCenter.evaluateAndEnqueue` returns before any rule is evaluated |
|
|
7101
|
+
*
|
|
7102
|
+
* The wrapper-binding pair is not a new idea: `legacy-migrations.ts` already
|
|
7103
|
+
* migrated the legacy `audioEnabled` / `pipelineEnabled` /
|
|
7104
|
+
* `motionDetectionEnabled` booleans ONTO `setWrapperActive`. The group is the
|
|
7105
|
+
* surface that decision never got.
|
|
7106
|
+
*
|
|
7107
|
+
* ## Two rules that are load-bearing
|
|
7108
|
+
*
|
|
7109
|
+
* - **Recording's switch is `enabled`, never the bands.** `bands` is the only
|
|
7110
|
+
* authored intent and `mode` is derived from it (`deriveRecordingMode`).
|
|
7111
|
+
* Expressing "off" by clearing bands destroys the operator's schedule and
|
|
7112
|
+
* turning the camera back on would then silently record nothing.
|
|
7113
|
+
* - **A switch that is off must be reported as off**, not merely produce
|
|
7114
|
+
* nothing. {@link CameraSwitch.enabled} is what a status surface renders as
|
|
7115
|
+
* "disabled by an operator" instead of "broken" — see
|
|
7116
|
+
* `CameraStatus.switchedOff`.
|
|
7117
|
+
*/
|
|
7118
|
+
/**
|
|
7119
|
+
* The five functions the operator named (2026-08-05). Deliberately NOT one id
|
|
7120
|
+
* per pipeline step: face recognition and plate/LPR are per-step toggles on
|
|
7121
|
+
* `pipelineOrchestrator.setCameraStepToggle` and belong in the pipeline
|
|
7122
|
+
* editor, not in a five-button safety group.
|
|
7123
|
+
*/
|
|
7124
|
+
var CameraSwitchIdSchema = _enum([
|
|
7125
|
+
"stream-broker",
|
|
7126
|
+
"object-detection",
|
|
7127
|
+
"audio-analysis",
|
|
7128
|
+
"recording",
|
|
7129
|
+
"notifications"
|
|
7130
|
+
]);
|
|
7131
|
+
/**
|
|
7132
|
+
* WHERE the switch's state actually lives. A discriminated union rather than a
|
|
7133
|
+
* string so both the writer (the orchestrator's `setCameraSwitch`) and any
|
|
7134
|
+
* reader can exhaustively narrow — and so "the group added a parallel map" is
|
|
7135
|
+
* a compile error rather than a review comment.
|
|
7136
|
+
*/
|
|
7137
|
+
var CameraSwitchAuthoritySchema = discriminatedUnion("kind", [
|
|
7138
|
+
object({ kind: literal("device-disabled") }),
|
|
7139
|
+
object({
|
|
7140
|
+
kind: literal("wrapper-binding"),
|
|
7141
|
+
capName: string()
|
|
7142
|
+
}),
|
|
7143
|
+
object({ kind: literal("recording-config") }),
|
|
7144
|
+
object({ kind: literal("notification-mute") })
|
|
7145
|
+
]);
|
|
7146
|
+
/**
|
|
7147
|
+
* Why a switch is not offered for this camera. Rendered instead of the
|
|
7148
|
+
* control, never as a dead control — an absent function and a broken one must
|
|
7149
|
+
* not look the same.
|
|
7150
|
+
*/
|
|
7151
|
+
var CameraSwitchUnavailableReasonSchema = _enum(["no-provider", "source-unreachable"]);
|
|
7152
|
+
/**
|
|
7153
|
+
* One switch, resolved for one camera.
|
|
7154
|
+
*
|
|
7155
|
+
* `label` and `costWhenOff` travel ON THE WIRE rather than being looked up
|
|
7156
|
+
* client-side: the viewer is a separate repository that does not import
|
|
7157
|
+
* `@camstack/types`, and a cost line duplicated in two clients is a cost line
|
|
7158
|
+
* that will disagree with itself. Five rows per camera is nothing.
|
|
7159
|
+
*/
|
|
7160
|
+
var CameraSwitchSchema = object({
|
|
7161
|
+
id: CameraSwitchIdSchema,
|
|
7162
|
+
label: string(),
|
|
7163
|
+
/**
|
|
7164
|
+
* What the operator LOSES while this is off, in one sentence. Required, not
|
|
7165
|
+
* optional: a switch that cannot say what it costs should not ship.
|
|
7166
|
+
*/
|
|
7167
|
+
costWhenOff: string(),
|
|
7168
|
+
/** False = do not render a control. `unavailableReason` says why. */
|
|
7169
|
+
available: boolean(),
|
|
7170
|
+
unavailableReason: CameraSwitchUnavailableReasonSchema.optional(),
|
|
7171
|
+
/** Current state. Meaningless when `available` is false — read it as `true`. */
|
|
7172
|
+
enabled: boolean(),
|
|
7173
|
+
authority: CameraSwitchAuthoritySchema
|
|
7174
|
+
});
|
|
7175
|
+
/** The whole group for one camera. */
|
|
7176
|
+
var CameraSwitchGroupSchema = object({
|
|
7177
|
+
deviceId: number().int(),
|
|
7178
|
+
switches: array(CameraSwitchSchema).readonly(),
|
|
7179
|
+
/** Unix ms when the group was composed server-side. */
|
|
7180
|
+
fetchedAt: number()
|
|
7181
|
+
});
|
|
7182
|
+
/**
|
|
7053
7183
|
* Ops-log — the durable, append-only operations audit shared by the
|
|
7054
7184
|
* recordings and events management surfaces.
|
|
7055
7185
|
*
|
|
@@ -7068,14 +7198,16 @@ var OpsLogOpSchema = _enum([
|
|
|
7068
7198
|
"manual-delete",
|
|
7069
7199
|
"rescan",
|
|
7070
7200
|
"retention-run",
|
|
7071
|
-
"relocate"
|
|
7201
|
+
"relocate",
|
|
7202
|
+
"orphan-audit"
|
|
7072
7203
|
]);
|
|
7073
7204
|
/** Why the operation ran. */
|
|
7074
7205
|
var OpsLogReasonSchema = _enum([
|
|
7075
7206
|
"retention",
|
|
7076
7207
|
"quota",
|
|
7077
7208
|
"manual",
|
|
7078
|
-
"operator"
|
|
7209
|
+
"operator",
|
|
7210
|
+
"maintenance"
|
|
7079
7211
|
]);
|
|
7080
7212
|
/** One audit row, shared verbatim by both domains. */
|
|
7081
7213
|
var OpsLogEntrySchema = object({
|
|
@@ -8936,6 +9068,100 @@ var RtpSourceSchema = object({
|
|
|
8936
9068
|
encoder: string(),
|
|
8937
9069
|
pipelineKey: string()
|
|
8938
9070
|
});
|
|
9071
|
+
/**
|
|
9072
|
+
* The encode request — **structured and serialisable, with NO raw-flag escape
|
|
9073
|
+
* hatch.** This is deliberate and it is the one lesson taken from
|
|
9074
|
+
* `getStreamWithCodec`: that method's `outputArgs: string[]` is simultaneously
|
|
9075
|
+
* its extensibility mechanism AND part of `pipelineKeyFor`'s sharing key, so
|
|
9076
|
+
* adding a flag silently forks the shared child, and two consumers that mean
|
|
9077
|
+
* the same thing but spell it differently never share. Here every knob is a
|
|
9078
|
+
* NAMED field: a new requirement becomes a schema field (and a codegen run),
|
|
9079
|
+
* never an opaque array.
|
|
9080
|
+
*
|
|
9081
|
+
* `inputArgs` / `outputArgs` are omitted from the profile for the same reason.
|
|
9082
|
+
* The operator-facing derived-stream transform editor still has them — that is
|
|
9083
|
+
* a different surface (`publishCameraStream({ kind: 'derived' })`) with a
|
|
9084
|
+
* different purpose (reshaping a badly-behaved SOURCE), and it is unchanged.
|
|
9085
|
+
*/
|
|
9086
|
+
var EgressEncodeSchema = EncodeProfileSchema.omit({
|
|
9087
|
+
inputArgs: true,
|
|
9088
|
+
outputArgs: true
|
|
9089
|
+
});
|
|
9090
|
+
/**
|
|
9091
|
+
* How the encoder is bounded. `'tight'` is a one-second VBV window for a
|
|
9092
|
+
* consumer whose budget is enforced per second (HomeKit); `'relaxed'` is two
|
|
9093
|
+
* seconds, letting a keyframe spike borrow from the next second (a browser,
|
|
9094
|
+
* an Echo). Named rather than numeric so the INTENT survives.
|
|
9095
|
+
*/
|
|
9096
|
+
var EgressRateControlSchema = _enum(["tight", "relaxed"]);
|
|
9097
|
+
var EgressTranscodeRequestSchema = object({
|
|
9098
|
+
deviceId: number().int().nonnegative(),
|
|
9099
|
+
/** Which published stream to read. */
|
|
9100
|
+
source: discriminatedUnion("kind", [object({
|
|
9101
|
+
kind: literal("profile"),
|
|
9102
|
+
profile: CamProfileSchema
|
|
9103
|
+
}), object({
|
|
9104
|
+
kind: literal("cam-stream"),
|
|
9105
|
+
camStreamId: string().min(1)
|
|
9106
|
+
})]),
|
|
9107
|
+
encode: EgressEncodeSchema,
|
|
9108
|
+
rateControl: EgressRateControlSchema.optional(),
|
|
9109
|
+
/**
|
|
9110
|
+
* `-bsf:v`. A consumer that negotiates its OWN SDP (HomeKit) cannot carry
|
|
9111
|
+
* out-of-band extradata and needs `dump_extra` on both the copy and encode
|
|
9112
|
+
* branches. Enumerated, not free text.
|
|
9113
|
+
*/
|
|
9114
|
+
bitstreamFilter: _enum([
|
|
9115
|
+
"dump_extra",
|
|
9116
|
+
"h264_mp4toannexb",
|
|
9117
|
+
"hevc_mp4toannexb"
|
|
9118
|
+
]).optional(),
|
|
9119
|
+
pixelFormat: _enum(["yuv420p", "nv12"]).optional(),
|
|
9120
|
+
/**
|
|
9121
|
+
* Operator/consumer override for decode hardware. ABSENT is the normal case
|
|
9122
|
+
* and the one that matters: the broker then resolves the backend from the
|
|
9123
|
+
* DECODER ADDON's per-node `probedBestHwaccel` (see
|
|
9124
|
+
* `@camstack/types` `ffmpeg/hwaccel.ts`), which is the ranking known to work
|
|
9125
|
+
* on this hardware — never the raw kernel resolver's qsv-first order.
|
|
9126
|
+
*/
|
|
9127
|
+
decodeHwAccel: _enum([
|
|
9128
|
+
"auto",
|
|
9129
|
+
"none",
|
|
9130
|
+
"videotoolbox",
|
|
9131
|
+
"vaapi",
|
|
9132
|
+
"qsv",
|
|
9133
|
+
"cuda"
|
|
9134
|
+
]).optional(),
|
|
9135
|
+
/**
|
|
9136
|
+
* Host to embed in the returned restream `url`. The broker mints hub-local
|
|
9137
|
+
* `127.0.0.1` URLs; a consumer on another node passes a cluster-resolvable
|
|
9138
|
+
* host (`NodeTopologyService.reachableHostByNode`) so the returned URL is
|
|
9139
|
+
* dialable from there. Same contract as `getStreamWithCodec.hostname` —
|
|
9140
|
+
* `substituteRtspHost` rewrites only the dial address, never the restreamer.
|
|
9141
|
+
*/
|
|
9142
|
+
hostname: string().optional(),
|
|
9143
|
+
/** Attribution for the broker panel. Never part of the sharing key. */
|
|
9144
|
+
tag: string().optional()
|
|
9145
|
+
});
|
|
9146
|
+
var EgressTranscodeSchema = object({
|
|
9147
|
+
/** Dial-able RTSP url (host-substituted when `hostname` was supplied). */
|
|
9148
|
+
url: string(),
|
|
9149
|
+
/** Release handle. Refcounted — the child dies when the last holder releases. */
|
|
9150
|
+
pipelineKey: string(),
|
|
9151
|
+
videoCodec: _enum(["H264", "H265"]),
|
|
9152
|
+
resolution: object({
|
|
9153
|
+
width: number().int().positive(),
|
|
9154
|
+
height: number().int().positive()
|
|
9155
|
+
}),
|
|
9156
|
+
transcoded: boolean(),
|
|
9157
|
+
encoder: string(),
|
|
9158
|
+
/**
|
|
9159
|
+
* The decode backend the child ACTUALLY ran with — `null` for software.
|
|
9160
|
+
* Returned rather than assumed: a consumer that asked for hardware and got
|
|
9161
|
+
* software needs to be able to see that without reading the broker's logs.
|
|
9162
|
+
*/
|
|
9163
|
+
decodeHwAccel: string().nullable()
|
|
9164
|
+
});
|
|
8939
9165
|
method(object({
|
|
8940
9166
|
deviceId: number().int().nonnegative(),
|
|
8941
9167
|
camStreamId: string().min(1),
|
|
@@ -9045,6 +9271,15 @@ method(object({
|
|
|
9045
9271
|
}), {
|
|
9046
9272
|
kind: "mutation",
|
|
9047
9273
|
auth: "admin"
|
|
9274
|
+
}), method(EgressTranscodeRequestSchema, EgressTranscodeSchema, {
|
|
9275
|
+
kind: "mutation",
|
|
9276
|
+
auth: "admin"
|
|
9277
|
+
}), method(object({ pipelineKey: string() }), object({
|
|
9278
|
+
released: boolean(),
|
|
9279
|
+
refcount: number().int().nonnegative()
|
|
9280
|
+
}), {
|
|
9281
|
+
kind: "mutation",
|
|
9282
|
+
auth: "admin"
|
|
9048
9283
|
}), method(SubscribeAudioChunksInputSchema, SubscribeAudioChunksResultSchema, { kind: "mutation" }), method(object({
|
|
9049
9284
|
subscriptionId: string(),
|
|
9050
9285
|
maxCount: number().int().positive().default(8)
|
|
@@ -13371,12 +13606,13 @@ var NcConditionsSchema = object({
|
|
|
13371
13606
|
* source; otherwise the subject's source must equal it. Legacy records
|
|
13372
13607
|
* with no stamped source are treated as `pipeline`. The union spans both
|
|
13373
13608
|
* record kinds — object events carry `pipeline` | `onboard`, synthetic
|
|
13374
|
-
* tracks carry `sensor
|
|
13609
|
+
* tracks carry `sensor` (a linked device) or `audio` (a D62 audio marker).
|
|
13375
13610
|
*/
|
|
13376
13611
|
source: _enum([
|
|
13377
13612
|
"pipeline",
|
|
13378
13613
|
"onboard",
|
|
13379
13614
|
"sensor",
|
|
13615
|
+
"audio",
|
|
13380
13616
|
"any"
|
|
13381
13617
|
]).optional(),
|
|
13382
13618
|
/**
|
|
@@ -13952,6 +14188,12 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
|
|
|
13952
14188
|
}), object({ success: literal(true) }), {
|
|
13953
14189
|
kind: "mutation",
|
|
13954
14190
|
auth: "admin"
|
|
14191
|
+
}), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
|
|
14192
|
+
deviceId: number().int(),
|
|
14193
|
+
muted: boolean()
|
|
14194
|
+
}), object({ success: literal(true) }), {
|
|
14195
|
+
kind: "mutation",
|
|
14196
|
+
auth: "admin"
|
|
13955
14197
|
}), method(object({
|
|
13956
14198
|
rule: NcRuleInputSchema,
|
|
13957
14199
|
lookbackMinutes: number().int().min(1).max(1440).default(60)
|
|
@@ -14290,12 +14532,60 @@ var TrackAudioLabelSchema = object({
|
|
|
14290
14532
|
});
|
|
14291
14533
|
/**
|
|
14292
14534
|
* How a track was produced. `pipeline` (default / absent) = the spatial
|
|
14293
|
-
* detection+tracking pipeline.
|
|
14294
|
-
*
|
|
14295
|
-
*
|
|
14296
|
-
*
|
|
14535
|
+
* detection+tracking pipeline. Every OTHER value is a SYNTHETIC projection —
|
|
14536
|
+
* no positions, a single snapshot, and no bbox trajectory at all:
|
|
14537
|
+
*
|
|
14538
|
+
* - `sensor` — a linked sensor/control device state change.
|
|
14539
|
+
* - `audio` — an audio event on the camera itself that was anomalous for
|
|
14540
|
+
* THAT camera, loud, and heard while nothing visual was happening (D62).
|
|
14541
|
+
*
|
|
14542
|
+
* The spatial subsystems (tracker association, occupancy count, re-id /
|
|
14543
|
+
* embedding, resurrection) MUST skip every synthetic source. Test for that
|
|
14544
|
+
* with `isSpatialTrack`, which allow-lists `pipeline` — a `!== 'sensor'`
|
|
14545
|
+
* check silently readmits every source added after it was written.
|
|
14546
|
+
*/
|
|
14547
|
+
var TrackSourceSchema = _enum([
|
|
14548
|
+
"pipeline",
|
|
14549
|
+
"sensor",
|
|
14550
|
+
"audio"
|
|
14551
|
+
]);
|
|
14552
|
+
/**
|
|
14553
|
+
* Per-track OPERATOR flags — set by hand from the admin UI or the viewer, never
|
|
14554
|
+
* by the pipeline. Spread into `TrackSchema` and `KeyEventSchema` from one place
|
|
14555
|
+
* so the two surfaces cannot drift.
|
|
14556
|
+
*
|
|
14557
|
+
* **Absent ≠ false.** A track that has never been touched omits the field; an
|
|
14558
|
+
* explicitly un-flagged track carries `false`. Legacy rows written before the
|
|
14559
|
+
* columns existed read as absent, and a consumer that needs a boolean should say
|
|
14560
|
+
* `flag === true`, not `flag !== false`.
|
|
14561
|
+
*
|
|
14562
|
+
* What the flags DO is deliberately UNDEFINED at the time of writing: they are
|
|
14563
|
+
* operator curation, and the behaviour they drive will be specified separately.
|
|
14564
|
+
* In particular a `markForTrain` track is NOT pinned against retention — see
|
|
14565
|
+
* `docs/decisions/adr-0059.md` for why that is a store-level change, not a flag.
|
|
14297
14566
|
*/
|
|
14298
|
-
var
|
|
14567
|
+
var TrackFlagFields = {
|
|
14568
|
+
/** Operator marked this track as training material. */
|
|
14569
|
+
markForTrain: boolean().optional(),
|
|
14570
|
+
/** Operator marked this track for diagnostic attention. */
|
|
14571
|
+
debug: boolean().optional()
|
|
14572
|
+
};
|
|
14573
|
+
/**
|
|
14574
|
+
* The write half: a PARTIAL patch. An omitted key is left untouched, so setting
|
|
14575
|
+
* one flag can never clear the other — the toggles are independent and are
|
|
14576
|
+
* driven from three surfaces that do not know about each other.
|
|
14577
|
+
*/
|
|
14578
|
+
var TrackFlagsPatchSchema = object(TrackFlagFields);
|
|
14579
|
+
/**
|
|
14580
|
+
* The resolved flag state after a write. Both fields are REQUIRED here (absent
|
|
14581
|
+
* collapses to `false`) so a caller can drive a toggle's checked state off the
|
|
14582
|
+
* mutation result without a re-fetch.
|
|
14583
|
+
*/
|
|
14584
|
+
var TrackFlagsSchema = object({
|
|
14585
|
+
trackId: string(),
|
|
14586
|
+
markForTrain: boolean(),
|
|
14587
|
+
debug: boolean()
|
|
14588
|
+
});
|
|
14299
14589
|
var TrackSchema = object({
|
|
14300
14590
|
trackId: string(),
|
|
14301
14591
|
deviceId: number(),
|
|
@@ -14338,7 +14628,8 @@ var TrackSchema = object({
|
|
|
14338
14628
|
/** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
|
|
14339
14629
|
* Populated from the persisted envelope columns on historical reads;
|
|
14340
14630
|
* absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
|
|
14341
|
-
envelope: TrackEnvelopeSchema.optional()
|
|
14631
|
+
envelope: TrackEnvelopeSchema.optional(),
|
|
14632
|
+
...TrackFlagFields
|
|
14342
14633
|
});
|
|
14343
14634
|
var BaseEventFields = {
|
|
14344
14635
|
id: string(),
|
|
@@ -14551,7 +14842,8 @@ var KeyEventSchema = object({
|
|
|
14551
14842
|
/** Highest-confidence ObjectEvent id for the track (empty when none). */
|
|
14552
14843
|
bestEventId: string(),
|
|
14553
14844
|
/** Track lifetime in ms (lastSeen - firstSeen). */
|
|
14554
|
-
windowMs: number().optional()
|
|
14845
|
+
windowMs: number().optional(),
|
|
14846
|
+
...TrackFlagFields
|
|
14555
14847
|
});
|
|
14556
14848
|
object({
|
|
14557
14849
|
trackId: string(),
|
|
@@ -14622,6 +14914,104 @@ var EventPruneCountsSchema = object({
|
|
|
14622
14914
|
object: number().int(),
|
|
14623
14915
|
audio: number().int()
|
|
14624
14916
|
});
|
|
14917
|
+
/**
|
|
14918
|
+
* Re-embed stored tracks from their key frames.
|
|
14919
|
+
*
|
|
14920
|
+
* The reason this is an operator-callable method and not a migration script:
|
|
14921
|
+
* every knob that decides what a vector MEANS — encoder model, crop margin,
|
|
14922
|
+
* squaring — is only changeable if the existing vectors can be regenerated.
|
|
14923
|
+
* Mixing feature spaces in one index makes cosine scores incomparable, and the
|
|
14924
|
+
* symptom is a quality regression with no visible cause.
|
|
14925
|
+
*/
|
|
14926
|
+
var RebuildObjectEmbeddingsInput = object({
|
|
14927
|
+
/** Restrict to one camera. Omit for the whole fleet. */
|
|
14928
|
+
deviceId: number().optional(),
|
|
14929
|
+
since: number().optional(),
|
|
14930
|
+
until: number().optional(),
|
|
14931
|
+
/** Stop after this many tracks; the result reports whether more remain. */
|
|
14932
|
+
maxTracks: number().int().positive().optional(),
|
|
14933
|
+
/**
|
|
14934
|
+
* Run every embedding on THIS node instead of round-robining the fleet.
|
|
14935
|
+
*
|
|
14936
|
+
* Named `executeOnNodeId` and not `nodeId` on purpose: an inline `nodeId`
|
|
14937
|
+
* field in cap args is read by `parent-unowned-call.ts` as a ROUTING PIN, so
|
|
14938
|
+
* calling it that would pin the rebuild REQUEST itself to that node — the
|
|
14939
|
+
* rebuild orchestration lives on the hub, and only the per-track step runs
|
|
14940
|
+
* remotely. This field is data; the per-track pin is applied inside.
|
|
14941
|
+
*
|
|
14942
|
+
* Absent ⇒ round-robin over every online node whose runner can serve the
|
|
14943
|
+
* pinned model.
|
|
14944
|
+
*/
|
|
14945
|
+
executeOnNodeId: string().optional(),
|
|
14946
|
+
/**
|
|
14947
|
+
* Milliseconds to wait between tracks; omit for the built-in default, `0` to
|
|
14948
|
+
* run flat out.
|
|
14949
|
+
*
|
|
14950
|
+
* A rebuild is bulk maintenance on hub-main's single thread. Measured
|
|
14951
|
+
* 2026-08-06, an unpaced pass held that thread busy 82.2 s out of 120 and
|
|
14952
|
+
* pushed `nodes.topology` from 0.25 s to 26 s for 43 minutes. The value in
|
|
14953
|
+
* force is logged at start and finish so a deliberately slow pass reads
|
|
14954
|
+
* differently from a stalled one.
|
|
14955
|
+
*/
|
|
14956
|
+
pacingMs: number().int().nonnegative().optional()
|
|
14957
|
+
});
|
|
14958
|
+
/**
|
|
14959
|
+
* Result of emptying the CLIP index.
|
|
14960
|
+
*
|
|
14961
|
+
* The clean slate before a policy change: a new crop margin or encoder model
|
|
14962
|
+
* leaves two feature spaces in one index whose cosine scores are not
|
|
14963
|
+
* comparable, so wiping and rebuilding is the only way to be sure every vector
|
|
14964
|
+
* means the same thing.
|
|
14965
|
+
*/
|
|
14966
|
+
var WipeObjectEmbeddingsResultSchema = object({ deleted: number() });
|
|
14967
|
+
/**
|
|
14968
|
+
* Acknowledgement that a rebuild STARTED.
|
|
14969
|
+
*
|
|
14970
|
+
* The pass costs ~1s per track — 45 minutes for a 2,600-track fleet — so it
|
|
14971
|
+
* runs detached and this returns immediately. Waiting for it made the client
|
|
14972
|
+
* time out while the work carried on server-side, which is the worst of both:
|
|
14973
|
+
* no result and no way to know it was still going. Poll
|
|
14974
|
+
* `getObjectEmbeddingRebuildStatus` for progress.
|
|
14975
|
+
*/
|
|
14976
|
+
var RebuildObjectEmbeddingsResultSchema = object({
|
|
14977
|
+
started: boolean(),
|
|
14978
|
+
/** True when a pass was already running; the new request is ignored. */
|
|
14979
|
+
alreadyRunning: boolean()
|
|
14980
|
+
});
|
|
14981
|
+
var RebuildStatusSchema = object({
|
|
14982
|
+
running: boolean(),
|
|
14983
|
+
scanned: number(),
|
|
14984
|
+
rebuilt: number(),
|
|
14985
|
+
/** Tracks whose key frame is gone — nothing to re-embed from. */
|
|
14986
|
+
missingKeyFrame: number(),
|
|
14987
|
+
/** Tracks with no usable detection box. */
|
|
14988
|
+
missingBbox: number(),
|
|
14989
|
+
/**
|
|
14990
|
+
* Tracks an executing node REFUSED rather than broke on — an unreadable key
|
|
14991
|
+
* frame, a step that threw. Separate from `failed` because the remedy is
|
|
14992
|
+
* different, and because a whole camera silently contributing zero vectors
|
|
14993
|
+
* is the shape of failure a rebuild must never hide.
|
|
14994
|
+
*/
|
|
14995
|
+
notRunnable: number(),
|
|
14996
|
+
/**
|
|
14997
|
+
* The pass stopped because NO node could serve the pinned model.
|
|
14998
|
+
*
|
|
14999
|
+
* Distinct from `notRunnable` on purpose: that one says "this track was
|
|
15000
|
+
* refused", this one says "the cluster cannot do this work at all" — every
|
|
15001
|
+
* candidate node either lacks the `clip-embedding` step, lacks a build of the
|
|
15002
|
+
* pinned model for its engine format, or dropped out. The remedy is a model /
|
|
15003
|
+
* engine change, not a per-camera one. Non-zero here always comes with
|
|
15004
|
+
* `complete: false`.
|
|
15005
|
+
*/
|
|
15006
|
+
noCapableNode: number(),
|
|
15007
|
+
failed: number(),
|
|
15008
|
+
/** Set once a pass ends: true only when EVERYTHING was covered. */
|
|
15009
|
+
complete: boolean().nullable(),
|
|
15010
|
+
startedAtMs: number().nullable(),
|
|
15011
|
+
finishedAtMs: number().nullable(),
|
|
15012
|
+
/** Present when the pass ended by throwing. */
|
|
15013
|
+
error: string().nullable()
|
|
15014
|
+
});
|
|
14625
15015
|
DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
|
|
14626
15016
|
deviceId: number(),
|
|
14627
15017
|
trackId: string()
|
|
@@ -14685,7 +15075,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
14685
15075
|
}), {
|
|
14686
15076
|
kind: "mutation",
|
|
14687
15077
|
auth: "admin"
|
|
14688
|
-
}), method(object({
|
|
15078
|
+
}), method(object({
|
|
15079
|
+
/** Log/audit scope only — the trackId is globally unique on its own. */
|
|
15080
|
+
deviceId: number(),
|
|
15081
|
+
trackId: string(),
|
|
15082
|
+
flags: TrackFlagsPatchSchema
|
|
15083
|
+
}), TrackFlagsSchema, { kind: "mutation" }), method(object({}), EventStoreFootprintSchema, {
|
|
14689
15084
|
kind: "query",
|
|
14690
15085
|
auth: "admin"
|
|
14691
15086
|
}), method(object({
|
|
@@ -14715,7 +15110,13 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
14715
15110
|
}), array(MediaFileSchema).readonly()), method(object({
|
|
14716
15111
|
trackId: string(),
|
|
14717
15112
|
kinds: array(MediaFileKindEnum).optional()
|
|
14718
|
-
}), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
|
|
15113
|
+
}), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
|
|
15114
|
+
kind: "mutation",
|
|
15115
|
+
auth: "admin"
|
|
15116
|
+
}), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
|
|
15117
|
+
kind: "mutation",
|
|
15118
|
+
auth: "admin"
|
|
15119
|
+
}), method(object({}), RebuildStatusSchema), object({
|
|
14719
15120
|
deviceId: number(),
|
|
14720
15121
|
timestamp: number(),
|
|
14721
15122
|
frameWidth: number(),
|
|
@@ -15289,6 +15690,53 @@ var DetailResultSchema = object({
|
|
|
15289
15690
|
nativeFaceShortSidePx: number().optional()
|
|
15290
15691
|
});
|
|
15291
15692
|
/**
|
|
15693
|
+
* Why an executing node REFUSED a stateless step run (`runStatelessStep`).
|
|
15694
|
+
*
|
|
15695
|
+
* A refusal is a first-class answer, not an error, because the caller's next
|
|
15696
|
+
* move depends on WHICH one it is — and because "the pass produced nothing"
|
|
15697
|
+
* must never be reachable without a named, counted cause. The two tiers:
|
|
15698
|
+
*
|
|
15699
|
+
* - **node-level** (`unknown-step`, `model-not-servable`) — this node can
|
|
15700
|
+
* never serve this (step, model) pair. The caller drops it from its rotation
|
|
15701
|
+
* and retries the same work elsewhere; nothing about the work changes.
|
|
15702
|
+
* - **work-level** (`unreadable-frame`, `execution-failed`) — this node is
|
|
15703
|
+
* fine, this one request is not. Retrying it on another node would only
|
|
15704
|
+
* spread the same failure.
|
|
15705
|
+
*/
|
|
15706
|
+
var StatelessStepRefusalSchema = _enum([
|
|
15707
|
+
"unknown-step",
|
|
15708
|
+
"model-not-servable",
|
|
15709
|
+
"unreadable-frame",
|
|
15710
|
+
"execution-failed"
|
|
15711
|
+
]);
|
|
15712
|
+
/**
|
|
15713
|
+
* Answer to `runStatelessStep` — a discriminated union rather than a nullable
|
|
15714
|
+
* result, because `null` is exactly what made the camera-bound detail path
|
|
15715
|
+
* unable to tell "refused" from "never asked".
|
|
15716
|
+
*/
|
|
15717
|
+
var RunStatelessStepResultSchema = discriminatedUnion("kind", [object({
|
|
15718
|
+
kind: literal("ran"),
|
|
15719
|
+
/** The node that actually executed it — the pin, echoed back for the log. */
|
|
15720
|
+
nodeId: string(),
|
|
15721
|
+
/**
|
|
15722
|
+
* The model the step ran with.
|
|
15723
|
+
*
|
|
15724
|
+
* The node verified this exact id has a build for the format it dispatched
|
|
15725
|
+
* on BEFORE running, so the executor's format resolution returns it
|
|
15726
|
+
* unchanged. A caller that pinned a model must compare this field and
|
|
15727
|
+
* treat a mismatch as a refusal — the whole point of the pin is that a
|
|
15728
|
+
* pass writes one feature space.
|
|
15729
|
+
*/
|
|
15730
|
+
modelId: string(),
|
|
15731
|
+
details: array(DetailResultSchema)
|
|
15732
|
+
}), object({
|
|
15733
|
+
kind: literal("refused"),
|
|
15734
|
+
nodeId: string(),
|
|
15735
|
+
reason: StatelessStepRefusalSchema,
|
|
15736
|
+
/** Human-readable specifics — the format tried, the formats shipped, etc. */
|
|
15737
|
+
detail: string()
|
|
15738
|
+
})]);
|
|
15739
|
+
/**
|
|
15292
15740
|
* Per-camera tunable ranges + defaults. Single source of truth used
|
|
15293
15741
|
* by both the Zod data schema (validation + default fallback) and
|
|
15294
15742
|
* the device settings UI (slider min/max/step). Touch one place and
|
|
@@ -15624,10 +16072,46 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
|
|
|
15624
16072
|
}), NativeCropResultSchema.nullable()), method(object({
|
|
15625
16073
|
deviceId: number(),
|
|
15626
16074
|
frameHandle: FrameHandleSchema.optional(),
|
|
16075
|
+
/**
|
|
16076
|
+
* FULL FRAME (base64 JPEG). The runner derives the crop rectangle from
|
|
16077
|
+
* `parent.bbox` with the cluster crop convention and cuts it itself —
|
|
16078
|
+
* do NOT pre-crop for this field, that is what `cropJpeg` is.
|
|
16079
|
+
*/
|
|
16080
|
+
frameJpeg: string().optional(),
|
|
16081
|
+
/**
|
|
16082
|
+
* PRE-CUT tile (base64 JPEG), used verbatim — NO padding is applied.
|
|
16083
|
+
* The fallback when the lease/session backing the frame is gone and the
|
|
16084
|
+
* caller already holds a crop.
|
|
16085
|
+
*/
|
|
15627
16086
|
cropJpeg: string().optional(),
|
|
15628
16087
|
parent: DetailParentSchema,
|
|
15629
16088
|
steps: array(string()).optional()
|
|
15630
|
-
}), object({ details: array(DetailResultSchema) }).nullable(), { kind: "mutation" })
|
|
16089
|
+
}), object({ details: array(DetailResultSchema) }).nullable(), { kind: "mutation" }), method(object({
|
|
16090
|
+
/** Catalog step id, e.g. `clip-embedding`. */
|
|
16091
|
+
stepId: string(),
|
|
16092
|
+
/**
|
|
16093
|
+
* REQUIRED model pin. The node runs this exact model or refuses with
|
|
16094
|
+
* `model-not-servable` — it never substitutes a format default, because
|
|
16095
|
+
* a fleet pass that round-robins across nodes would then fill one index
|
|
16096
|
+
* from several encoders.
|
|
16097
|
+
*/
|
|
16098
|
+
modelId: string(),
|
|
16099
|
+
/** FULL FRAME, base64 JPEG. The runner cuts — do NOT pre-crop. */
|
|
16100
|
+
frameJpeg: string(),
|
|
16101
|
+
/**
|
|
16102
|
+
* The subject box, NORMALISED [0,1] against `frameJpeg`. Normalised on
|
|
16103
|
+
* purpose: the caller stores boxes against a downscaled analysis frame
|
|
16104
|
+
* while the stored key frame is native-resolution, and the only side
|
|
16105
|
+
* that reliably knows the image's pixel dimensions is the side that
|
|
16106
|
+
* decodes it. Denormalising here removes a second reader of the
|
|
16107
|
+
* dimensions and the class of mismatch that comes with it.
|
|
16108
|
+
*/
|
|
16109
|
+
bbox: NativeCropBboxSchema,
|
|
16110
|
+
/** Parent class of the subject (`person`, `vehicle`, …) — carried into the result. */
|
|
16111
|
+
className: string(),
|
|
16112
|
+
/** Camera the pixels came from. Diagnostics + log tags ONLY — never routing. */
|
|
16113
|
+
sourceDeviceId: number()
|
|
16114
|
+
}), RunStatelessStepResultSchema, { kind: "mutation" });
|
|
15631
16115
|
var CameraPipelineConfigSchema = object({
|
|
15632
16116
|
engine: PipelineEngineChoiceSchema.optional(),
|
|
15633
16117
|
steps: array(PipelineStepInputSchema).readonly(),
|
|
@@ -15925,6 +16409,20 @@ var CameraStatusSchema = object({
|
|
|
15925
16409
|
detection: CameraDetectionStatusSchema.nullable(),
|
|
15926
16410
|
audio: CameraAudioStatusSchema.nullable(),
|
|
15927
16411
|
recording: CameraRecordingStatusSchema.nullable(),
|
|
16412
|
+
/**
|
|
16413
|
+
* Per-camera function switches an OPERATOR has turned off
|
|
16414
|
+
* ([D61](../../../../docs/decisions/adr-0067.md)).
|
|
16415
|
+
*
|
|
16416
|
+
* This is the difference between DISABLED and BROKEN. A camera whose
|
|
16417
|
+
* `detection` block reports zero fps and whose `switchedOff` contains
|
|
16418
|
+
* `'object-detection'` was switched off by a person; the same camera with an
|
|
16419
|
+
* empty list is failing. Every status surface must render the two
|
|
16420
|
+
* differently — a quiet camera that looks identical to a dead one is the
|
|
16421
|
+
* silence-reads-as-never-happened trap this repo keeps paying for.
|
|
16422
|
+
*
|
|
16423
|
+
* Empty when nothing is off. Never contains a switch no provider offers.
|
|
16424
|
+
*/
|
|
16425
|
+
switchedOff: array(CameraSwitchIdSchema).readonly(),
|
|
15928
16426
|
/** Unix timestamp (ms) when this snapshot was composed server-side. */
|
|
15929
16427
|
fetchedAt: number()
|
|
15930
16428
|
});
|
|
@@ -16093,7 +16591,14 @@ method(object({
|
|
|
16093
16591
|
}), method(object({
|
|
16094
16592
|
deviceId: number(),
|
|
16095
16593
|
agentNodeId: string().optional()
|
|
16096
|
-
}), CameraPipelineConfigSchema), method(object({ deviceId: number() }),
|
|
16594
|
+
}), CameraPipelineConfigSchema), method(object({ deviceId: number() }), CameraSwitchGroupSchema), method(object({
|
|
16595
|
+
deviceId: number(),
|
|
16596
|
+
switchId: CameraSwitchIdSchema,
|
|
16597
|
+
enabled: boolean()
|
|
16598
|
+
}), CameraSwitchGroupSchema, {
|
|
16599
|
+
kind: "mutation",
|
|
16600
|
+
auth: "admin"
|
|
16601
|
+
}), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
|
|
16097
16602
|
name: string(),
|
|
16098
16603
|
description: string().optional(),
|
|
16099
16604
|
config: CameraPipelineConfigSchema
|
|
@@ -16949,6 +17454,24 @@ var VectorDeleteByFilterInputSchema = object({
|
|
|
16949
17454
|
filter: VectorFilterSchema
|
|
16950
17455
|
});
|
|
16951
17456
|
var VectorDeleteResultSchema = object({ deleted: number() });
|
|
17457
|
+
var VectorGetInputSchema = object({
|
|
17458
|
+
index: string(),
|
|
17459
|
+
ids: array(string())
|
|
17460
|
+
});
|
|
17461
|
+
/**
|
|
17462
|
+
* Metadata for the requested ids, WITHOUT their vectors.
|
|
17463
|
+
*
|
|
17464
|
+
* The only caller is a best-of gate that compares a candidate's confidence
|
|
17465
|
+
* against the stored one, and shipping 512 floats back to answer "is 0.91 >
|
|
17466
|
+
* 0.87" would undo the point of the compact encoding. Ids with no row are
|
|
17467
|
+
* simply absent — a caller distinguishing "not stored" from "stored" reads the
|
|
17468
|
+
* length, and a null placeholder would invite a `?? 0` that treats a missing
|
|
17469
|
+
* row as confidence zero.
|
|
17470
|
+
*/
|
|
17471
|
+
var VectorGetResultSchema = object({ items: array(object({
|
|
17472
|
+
id: string(),
|
|
17473
|
+
metadata: VectorMetadataSchema
|
|
17474
|
+
})) });
|
|
16952
17475
|
var VectorStatsInputSchema = object({ index: string() });
|
|
16953
17476
|
var VectorStatsResultSchema = object({
|
|
16954
17477
|
/** Provider id, so an operator can tell brute force from an ANN index. */
|
|
@@ -16961,7 +17484,7 @@ var VectorStatsResultSchema = object({
|
|
|
16961
17484
|
/** False when the backend ranks approximately. */
|
|
16962
17485
|
exact: boolean()
|
|
16963
17486
|
});
|
|
16964
|
-
method(VectorDeclareIndexInputSchema, _void(), { kind: "mutation" }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, { kind: "mutation" }), method(VectorQueryInputSchema, VectorQueryResultSchema), method(VectorDeleteInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorStatsInputSchema, VectorStatsResultSchema);
|
|
17487
|
+
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);
|
|
16965
17488
|
/**
|
|
16966
17489
|
* `videoclips` — the unified, navigable-clip surface for a camera.
|
|
16967
17490
|
*
|
|
@@ -25032,6 +25555,12 @@ Object.freeze({
|
|
|
25032
25555
|
addonId: null,
|
|
25033
25556
|
access: "view"
|
|
25034
25557
|
},
|
|
25558
|
+
"notificationRules.listDeviceMutes": {
|
|
25559
|
+
capName: "notification-rules",
|
|
25560
|
+
capScope: "system",
|
|
25561
|
+
addonId: null,
|
|
25562
|
+
access: "view"
|
|
25563
|
+
},
|
|
25035
25564
|
"notificationRules.listRules": {
|
|
25036
25565
|
capName: "notification-rules",
|
|
25037
25566
|
capScope: "system",
|
|
@@ -25050,6 +25579,12 @@ Object.freeze({
|
|
|
25050
25579
|
addonId: null,
|
|
25051
25580
|
access: "create"
|
|
25052
25581
|
},
|
|
25582
|
+
"notificationRules.setDeviceMuted": {
|
|
25583
|
+
capName: "notification-rules",
|
|
25584
|
+
capScope: "system",
|
|
25585
|
+
addonId: null,
|
|
25586
|
+
access: "create"
|
|
25587
|
+
},
|
|
25053
25588
|
"notificationRules.setRuleEnabled": {
|
|
25054
25589
|
capName: "notification-rules",
|
|
25055
25590
|
capScope: "system",
|
|
@@ -25224,6 +25759,12 @@ Object.freeze({
|
|
|
25224
25759
|
addonId: null,
|
|
25225
25760
|
access: "view"
|
|
25226
25761
|
},
|
|
25762
|
+
"pipelineAnalytics.getObjectEmbeddingRebuildStatus": {
|
|
25763
|
+
capName: "pipeline-analytics",
|
|
25764
|
+
capScope: "device",
|
|
25765
|
+
addonId: null,
|
|
25766
|
+
access: "view"
|
|
25767
|
+
},
|
|
25227
25768
|
"pipelineAnalytics.getObjectEvents": {
|
|
25228
25769
|
capName: "pipeline-analytics",
|
|
25229
25770
|
capScope: "device",
|
|
@@ -25302,6 +25843,12 @@ Object.freeze({
|
|
|
25302
25843
|
addonId: null,
|
|
25303
25844
|
access: "create"
|
|
25304
25845
|
},
|
|
25846
|
+
"pipelineAnalytics.rebuildObjectEmbeddings": {
|
|
25847
|
+
capName: "pipeline-analytics",
|
|
25848
|
+
capScope: "device",
|
|
25849
|
+
addonId: null,
|
|
25850
|
+
access: "create"
|
|
25851
|
+
},
|
|
25305
25852
|
"pipelineAnalytics.relocateMedia": {
|
|
25306
25853
|
capName: "pipeline-analytics",
|
|
25307
25854
|
capScope: "device",
|
|
@@ -25314,12 +25861,24 @@ Object.freeze({
|
|
|
25314
25861
|
addonId: null,
|
|
25315
25862
|
access: "view"
|
|
25316
25863
|
},
|
|
25864
|
+
"pipelineAnalytics.setTrackFlags": {
|
|
25865
|
+
capName: "pipeline-analytics",
|
|
25866
|
+
capScope: "device",
|
|
25867
|
+
addonId: null,
|
|
25868
|
+
access: "create"
|
|
25869
|
+
},
|
|
25317
25870
|
"pipelineAnalytics.wipeAllAnalytics": {
|
|
25318
25871
|
capName: "pipeline-analytics",
|
|
25319
25872
|
capScope: "device",
|
|
25320
25873
|
addonId: null,
|
|
25321
25874
|
access: "delete"
|
|
25322
25875
|
},
|
|
25876
|
+
"pipelineAnalytics.wipeObjectEmbeddings": {
|
|
25877
|
+
capName: "pipeline-analytics",
|
|
25878
|
+
capScope: "device",
|
|
25879
|
+
addonId: null,
|
|
25880
|
+
access: "delete"
|
|
25881
|
+
},
|
|
25323
25882
|
"pipelineExecutor.cacheFrameInPool": {
|
|
25324
25883
|
capName: "pipeline-executor",
|
|
25325
25884
|
capScope: "system",
|
|
@@ -25614,6 +26173,12 @@ Object.freeze({
|
|
|
25614
26173
|
addonId: null,
|
|
25615
26174
|
access: "view"
|
|
25616
26175
|
},
|
|
26176
|
+
"pipelineOrchestrator.getCameraSwitches": {
|
|
26177
|
+
capName: "pipeline-orchestrator",
|
|
26178
|
+
capScope: "system",
|
|
26179
|
+
addonId: null,
|
|
26180
|
+
access: "view"
|
|
26181
|
+
},
|
|
25617
26182
|
"pipelineOrchestrator.getCapabilityBindings": {
|
|
25618
26183
|
capName: "pipeline-orchestrator",
|
|
25619
26184
|
capScope: "system",
|
|
@@ -25746,6 +26311,12 @@ Object.freeze({
|
|
|
25746
26311
|
addonId: null,
|
|
25747
26312
|
access: "create"
|
|
25748
26313
|
},
|
|
26314
|
+
"pipelineOrchestrator.setCameraSwitch": {
|
|
26315
|
+
capName: "pipeline-orchestrator",
|
|
26316
|
+
capScope: "system",
|
|
26317
|
+
addonId: null,
|
|
26318
|
+
access: "create"
|
|
26319
|
+
},
|
|
25749
26320
|
"pipelineOrchestrator.setCapabilityBinding": {
|
|
25750
26321
|
capName: "pipeline-orchestrator",
|
|
25751
26322
|
capScope: "system",
|
|
@@ -25836,6 +26407,12 @@ Object.freeze({
|
|
|
25836
26407
|
addonId: null,
|
|
25837
26408
|
access: "create"
|
|
25838
26409
|
},
|
|
26410
|
+
"pipelineRunner.runStatelessStep": {
|
|
26411
|
+
capName: "pipeline-runner",
|
|
26412
|
+
capScope: "system",
|
|
26413
|
+
addonId: null,
|
|
26414
|
+
access: "create"
|
|
26415
|
+
},
|
|
25839
26416
|
"plateGallery.assignPlate": {
|
|
25840
26417
|
capName: "plate-gallery",
|
|
25841
26418
|
capScope: "system",
|
|
@@ -26652,6 +27229,12 @@ Object.freeze({
|
|
|
26652
27229
|
addonId: null,
|
|
26653
27230
|
access: "create"
|
|
26654
27231
|
},
|
|
27232
|
+
"streamBroker.acquireEgressTranscode": {
|
|
27233
|
+
capName: "stream-broker",
|
|
27234
|
+
capScope: "system",
|
|
27235
|
+
addonId: null,
|
|
27236
|
+
access: "create"
|
|
27237
|
+
},
|
|
26655
27238
|
"streamBroker.assignProfile": {
|
|
26656
27239
|
capName: "stream-broker",
|
|
26657
27240
|
capScope: "system",
|
|
@@ -26760,6 +27343,12 @@ Object.freeze({
|
|
|
26760
27343
|
addonId: null,
|
|
26761
27344
|
access: "create"
|
|
26762
27345
|
},
|
|
27346
|
+
"streamBroker.releaseEgressTranscode": {
|
|
27347
|
+
capName: "stream-broker",
|
|
27348
|
+
capScope: "system",
|
|
27349
|
+
addonId: null,
|
|
27350
|
+
access: "create"
|
|
27351
|
+
},
|
|
26763
27352
|
"streamBroker.releaseStreamWithCodec": {
|
|
26764
27353
|
capName: "stream-broker",
|
|
26765
27354
|
capScope: "system",
|
|
@@ -27240,6 +27829,12 @@ Object.freeze({
|
|
|
27240
27829
|
addonId: null,
|
|
27241
27830
|
access: "delete"
|
|
27242
27831
|
},
|
|
27832
|
+
"vectorStore.getByIds": {
|
|
27833
|
+
capName: "vector-store",
|
|
27834
|
+
capScope: "system",
|
|
27835
|
+
addonId: null,
|
|
27836
|
+
access: "view"
|
|
27837
|
+
},
|
|
27243
27838
|
"vectorStore.query": {
|
|
27244
27839
|
capName: "vector-store",
|
|
27245
27840
|
capScope: "system",
|
|
@@ -27514,6 +28109,30 @@ TimelapseRuleInputSchema.extend({
|
|
|
27514
28109
|
createdAt: number(),
|
|
27515
28110
|
updatedAt: number()
|
|
27516
28111
|
});
|
|
28112
|
+
object({
|
|
28113
|
+
/**
|
|
28114
|
+
* Fraction of the box's own size added on EACH side before cutting.
|
|
28115
|
+
*
|
|
28116
|
+
* CLIP is trained on natural images WITH surroundings; a pixel-tight crop
|
|
28117
|
+
* removes exactly the context it is strongest on (a dog cut to its outline
|
|
28118
|
+
* is a dark blob). The right value is an empirical question, which is why it
|
|
28119
|
+
* is a setting: 0 / 0.15 / 0.2 / 0.5 are the interesting points.
|
|
28120
|
+
*/
|
|
28121
|
+
paddingRatio: number().min(0).max(4),
|
|
28122
|
+
/**
|
|
28123
|
+
* Square the window (in PIXELS) before cutting.
|
|
28124
|
+
*
|
|
28125
|
+
* CLIP's input is square, so a tall bbox resized straight to NxN is squashed
|
|
28126
|
+
* — a standing person becomes a shape the model never saw. Squaring costs
|
|
28127
|
+
* extra background, which is context the model wants anyway. Off by default
|
|
28128
|
+
* because the live path has never squared and the stored index reflects that.
|
|
28129
|
+
*/
|
|
28130
|
+
square: boolean()
|
|
28131
|
+
});
|
|
28132
|
+
({
|
|
28133
|
+
paddingRatio: .15,
|
|
28134
|
+
square: false
|
|
28135
|
+
}).paddingRatio;
|
|
27517
28136
|
//#endregion
|
|
27518
28137
|
//#region src/server/addon.ts
|
|
27519
28138
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -27533,7 +28152,7 @@ var AdminUIAddon = class extends BaseAddon {
|
|
|
27533
28152
|
capability: adminUiCapability,
|
|
27534
28153
|
provider: {
|
|
27535
28154
|
getStaticDir: async () => ({ staticDir: resolveAdminUiDistDir() }),
|
|
27536
|
-
getVersion: async () => ({ version: "1.2.
|
|
28155
|
+
getVersion: async () => ({ version: "1.2.32" })
|
|
27537
28156
|
}
|
|
27538
28157
|
}];
|
|
27539
28158
|
}
|