@camstack/addon-pipeline-orchestrator 1.2.127 → 1.2.129
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/_stub.js +2 -2
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-BbK-mPCs.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-ChSa_Eh-.mjs} +3 -3
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BlzMZjfz.mjs +26 -0
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DHxtMqJ3.mjs +26 -0
- package/dist/{hostInit-C7JAXCVu.mjs → hostInit-DomAMTeV.mjs} +3 -3
- package/dist/index.js +475 -34
- package/dist/index.mjs +475 -34
- package/dist/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Cb3dN7Fo.mjs +0 -26
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-B4f8pBZg.mjs +0 -26
package/dist/index.js
CHANGED
|
@@ -8894,6 +8894,20 @@ var RelocateJobSchema = object({
|
|
|
8894
8894
|
* in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
|
|
8895
8895
|
*/
|
|
8896
8896
|
rowsReconciled: number().int().nonnegative().optional(),
|
|
8897
|
+
/**
|
|
8898
|
+
* Rows this run FORGOT because the file they name is not on disk.
|
|
8899
|
+
*
|
|
8900
|
+
* The mover derived the path from the row's own fields and `stat`ed it; an
|
|
8901
|
+
* ENOENT there is a per-path confirmation that the segment is gone (D296),
|
|
8902
|
+
* and the durable row is dropped through the same channel eviction uses. It
|
|
8903
|
+
* is reported for the same reason `rowsReconciled` is: this is a durable
|
|
8904
|
+
* mutation nobody asked for, and a migration that quietly erases hour rows is
|
|
8905
|
+
* the same failure as one that quietly skips them (D295).
|
|
8906
|
+
*
|
|
8907
|
+
* The production drain of 2026-08-30 would have reported 11 074 here — the
|
|
8908
|
+
* ledger claimed 5.65 GB of footage that no longer existed.
|
|
8909
|
+
*/
|
|
8910
|
+
rowsForgotten: number().int().nonnegative().optional(),
|
|
8897
8911
|
startedAt: number(),
|
|
8898
8912
|
finishedAt: number().nullable(),
|
|
8899
8913
|
error: string().nullable()
|
|
@@ -9257,6 +9271,91 @@ var RelocateResidueSchema = object({
|
|
|
9257
9271
|
segments: number().int().nonnegative(),
|
|
9258
9272
|
bytes: number().int().nonnegative()
|
|
9259
9273
|
}).nullable();
|
|
9274
|
+
/**
|
|
9275
|
+
* Ask one location whether its durable hour rows describe the disk — the walk
|
|
9276
|
+
* (D319).
|
|
9277
|
+
*
|
|
9278
|
+
* `apply` DEFAULTS TO FALSE and that default is the product: the operator's
|
|
9279
|
+
* missing tool is the question, and the dry run is how they sanity-check the
|
|
9280
|
+
* destructive run before authorising it.
|
|
9281
|
+
*/
|
|
9282
|
+
var LedgerWalkInputSchema = object({
|
|
9283
|
+
locationId: string().min(1),
|
|
9284
|
+
/** Forget the confirmed-absent rows, rather than only counting them. */
|
|
9285
|
+
apply: boolean().optional(),
|
|
9286
|
+
/** Narrow to one camera. */
|
|
9287
|
+
deviceId: number().int().positive().optional(),
|
|
9288
|
+
/** Narrow to these recording profiles; empty/absent = every profile. */
|
|
9289
|
+
profiles: array(string().min(1)).optional()
|
|
9290
|
+
});
|
|
9291
|
+
/** Why a whole walk did nothing. Every one leaves the ledger untouched. */
|
|
9292
|
+
var LedgerWalkRefusalSchema = _enum([
|
|
9293
|
+
"location-unknown",
|
|
9294
|
+
"source-writable",
|
|
9295
|
+
"no-ledger",
|
|
9296
|
+
"archive-unreadable",
|
|
9297
|
+
"anchor-absent",
|
|
9298
|
+
"anchor-unreadable",
|
|
9299
|
+
"anchor-moved"
|
|
9300
|
+
]);
|
|
9301
|
+
_enum([
|
|
9302
|
+
"live-tail",
|
|
9303
|
+
"listing-error",
|
|
9304
|
+
"path-mismatch",
|
|
9305
|
+
"durable-refused"
|
|
9306
|
+
]);
|
|
9307
|
+
/** Every skip reason, always present, always a number — so a reason that never
|
|
9308
|
+
* fired reports as zero rather than absent and the report shape is constant
|
|
9309
|
+
* between passes. Spelled out rather than `z.record` for exactly that. */
|
|
9310
|
+
var LedgerWalkSkipCountsSchema = object({
|
|
9311
|
+
"live-tail": number().int().nonnegative(),
|
|
9312
|
+
"listing-error": number().int().nonnegative(),
|
|
9313
|
+
"path-mismatch": number().int().nonnegative(),
|
|
9314
|
+
"durable-refused": number().int().nonnegative()
|
|
9315
|
+
});
|
|
9316
|
+
/** One camera's share of a walk, so a report names cameras and not rows. */
|
|
9317
|
+
var LedgerWalkDeviceReportSchema = object({
|
|
9318
|
+
deviceId: number().int(),
|
|
9319
|
+
hoursWalked: number().int().nonnegative(),
|
|
9320
|
+
hoursMissing: number().int().nonnegative(),
|
|
9321
|
+
ghostSegments: number().int().nonnegative(),
|
|
9322
|
+
ghostBytes: number().int().nonnegative(),
|
|
9323
|
+
forgottenSegments: number().int().nonnegative(),
|
|
9324
|
+
orphanFiles: number().int().nonnegative()
|
|
9325
|
+
});
|
|
9326
|
+
/**
|
|
9327
|
+
* What one walk claimed, listed, found and (only when armed) forgot.
|
|
9328
|
+
*
|
|
9329
|
+
* `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
|
|
9330
|
+
* walk that saw a fraction of the location is visible in its own report rather
|
|
9331
|
+
* than in the absence of one.
|
|
9332
|
+
*/
|
|
9333
|
+
var LedgerWalkReportSchema = object({
|
|
9334
|
+
locationId: string(),
|
|
9335
|
+
applied: boolean(),
|
|
9336
|
+
refused: LedgerWalkRefusalSchema.nullable(),
|
|
9337
|
+
archiveSegments: number().int().nonnegative().nullable(),
|
|
9338
|
+
archiveBytes: number().int().nonnegative().nullable(),
|
|
9339
|
+
hoursClaimed: number().int().nonnegative(),
|
|
9340
|
+
hoursWalked: number().int().nonnegative(),
|
|
9341
|
+
hoursMissing: number().int().nonnegative(),
|
|
9342
|
+
/** `readdir` calls issued — the cost, stated in the unit that is paid. */
|
|
9343
|
+
listings: number().int().nonnegative(),
|
|
9344
|
+
segmentsClaimed: number().int().nonnegative(),
|
|
9345
|
+
ghostSegments: number().int().nonnegative(),
|
|
9346
|
+
ghostBytes: number().int().nonnegative(),
|
|
9347
|
+
ghostHoursWhole: number().int().nonnegative(),
|
|
9348
|
+
forgottenSegments: number().int().nonnegative(),
|
|
9349
|
+
forgottenBytes: number().int().nonnegative(),
|
|
9350
|
+
/** Files under a claimed hour that no durable row names. Never deleted. */
|
|
9351
|
+
orphanFiles: number().int().nonnegative(),
|
|
9352
|
+
orphanSample: array(string()).readonly(),
|
|
9353
|
+
hoursSkipped: number().int().nonnegative(),
|
|
9354
|
+
skippedByReason: LedgerWalkSkipCountsSchema,
|
|
9355
|
+
/** The walk stopped at its per-pass hour bound with claims unwalked. */
|
|
9356
|
+
bounded: boolean(),
|
|
9357
|
+
byDevice: array(LedgerWalkDeviceReportSchema).readonly()
|
|
9358
|
+
});
|
|
9260
9359
|
/** How many rows a media pass would still act on against a given target — the
|
|
9261
9360
|
* media lane's denominator AND its residue, from ONE derivation so the two can
|
|
9262
9361
|
* never disagree. `null` = the count could not be taken. */
|
|
@@ -19833,13 +19932,15 @@ var ListGroupsPageSchema = object({
|
|
|
19833
19932
|
groups: array(AnalyticsGroupRecordSchema).readonly(),
|
|
19834
19933
|
nextCursor: string().nullable()
|
|
19835
19934
|
});
|
|
19935
|
+
var KEY_EVENTS_DEFAULT_LIMIT = 50;
|
|
19936
|
+
var KEY_EVENTS_MAX_LIMIT = 200;
|
|
19836
19937
|
var KeyEventQueryInput = object({
|
|
19837
19938
|
deviceId: number(),
|
|
19838
19939
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
19839
19940
|
since: number(),
|
|
19840
19941
|
/** Window upper bound (track firstSeen ≤ until). */
|
|
19841
19942
|
until: number(),
|
|
19842
|
-
limit: number().int().min(1).max(
|
|
19943
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
19843
19944
|
/** Drop tracks scoring below this importance. */
|
|
19844
19945
|
minImportance: number().min(0).max(1).optional(),
|
|
19845
19946
|
/** Restrict to a single class (e.g. 'person'). */
|
|
@@ -19861,6 +19962,32 @@ var KeyEventSchema = object({
|
|
|
19861
19962
|
...TrackFlagFields,
|
|
19862
19963
|
...TrackRetrainFields
|
|
19863
19964
|
});
|
|
19965
|
+
/** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
|
|
19966
|
+
var KeyEventBatchQueryInput = object({
|
|
19967
|
+
deviceIds: array(number()).min(1).max(200),
|
|
19968
|
+
since: number(),
|
|
19969
|
+
until: number(),
|
|
19970
|
+
/** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
|
|
19971
|
+
* across the set, which would let a busy camera starve a quiet one of its
|
|
19972
|
+
* rows and change what the merged feed contains. */
|
|
19973
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
19974
|
+
minImportance: number().min(0).max(1).optional(),
|
|
19975
|
+
classFilter: string().optional()
|
|
19976
|
+
});
|
|
19977
|
+
/**
|
|
19978
|
+
* One camera's key events in a batch answer.
|
|
19979
|
+
*
|
|
19980
|
+
* The row exists for every requested id. `getKeyEvents` degrades to `[]` on
|
|
19981
|
+
* error rather than throwing, so a camera whose store read failed and one with
|
|
19982
|
+
* no events in the window were ALREADY indistinguishable per camera — the
|
|
19983
|
+
* batch does not make that worse, and the row keeps the deviceId the single
|
|
19984
|
+
* method's output never carried (the caller used to stamp it from the fan-out
|
|
19985
|
+
* key, which only worked because there was one query per camera).
|
|
19986
|
+
*/
|
|
19987
|
+
var KeyEventsForDeviceSchema = object({
|
|
19988
|
+
deviceId: number(),
|
|
19989
|
+
events: array(KeyEventSchema).readonly()
|
|
19990
|
+
});
|
|
19864
19991
|
object({
|
|
19865
19992
|
trackId: string(),
|
|
19866
19993
|
className: string(),
|
|
@@ -19932,6 +20059,50 @@ var EventStoreFootprintSchema = object({
|
|
|
19932
20059
|
totalBytes: number().int(),
|
|
19933
20060
|
devices: array(EventStoreDeviceFootprintSchema).readonly()
|
|
19934
20061
|
});
|
|
20062
|
+
/** Event-media footprint for one {@link MediaFileKind}. */
|
|
20063
|
+
var EventMediaKindFootprintSchema = object({
|
|
20064
|
+
kind: MediaFileKindEnum,
|
|
20065
|
+
/** Media rows of this kind. */
|
|
20066
|
+
rows: number().int(),
|
|
20067
|
+
/** Bytes on disk held by those rows. */
|
|
20068
|
+
bytes: number().int()
|
|
20069
|
+
});
|
|
20070
|
+
/**
|
|
20071
|
+
* The media footprint broken down by KIND — the axis a deletion decision
|
|
20072
|
+
* actually turns on.
|
|
20073
|
+
*
|
|
20074
|
+
* A byte total says how much there is; it cannot say what is safe to remove.
|
|
20075
|
+
* The deletable set (the periodic `snapshot` filmstrip, the surplus per-edge
|
|
20076
|
+
* motion stills) and the keep set (`firstFrame`, rolling `lastFrame`,
|
|
20077
|
+
* `thumbnail`/`thumbnailSmall`, `keyFrame`/`keyFrameSmall`, the face/plate
|
|
20078
|
+
* buffers, gallery media, the CLIP `crop`) are distinguished by `kind` and by
|
|
20079
|
+
* nothing else, so sizing a deletion means summing per kind.
|
|
20080
|
+
*
|
|
20081
|
+
* ## Why `unaccounted*` exists
|
|
20082
|
+
*
|
|
20083
|
+
* `kinds` is enumerated from {@link MediaFileKindEnum} — the closed set the
|
|
20084
|
+
* writers use — and summed one kind at a time. `totalRows` / `totalBytes` come
|
|
20085
|
+
* from a SEPARATE unfiltered aggregate over the same rows, never from adding
|
|
20086
|
+
* `kinds` up. A row whose stored `kind` is not in the enum (written by a
|
|
20087
|
+
* retired code path, or by a version that knew a kind this one does not) would
|
|
20088
|
+
* otherwise vanish from the total silently, and an operator would delete
|
|
20089
|
+
* against a denominator smaller than the disk.
|
|
20090
|
+
*
|
|
20091
|
+
* `unaccountedRows` / `unaccountedBytes` are the difference. They are normally
|
|
20092
|
+
* zero; a non-zero value is a real finding and must be shown, not rounded away.
|
|
20093
|
+
*/
|
|
20094
|
+
var EventMediaKindBreakdownSchema = object({
|
|
20095
|
+
/** Every media row in scope, from one unfiltered aggregate. */
|
|
20096
|
+
totalRows: number().int(),
|
|
20097
|
+
/** Every media byte in scope, from that same aggregate. */
|
|
20098
|
+
totalBytes: number().int(),
|
|
20099
|
+
/** Per-kind footprint, ordered by bytes descending. */
|
|
20100
|
+
kinds: array(EventMediaKindFootprintSchema).readonly(),
|
|
20101
|
+
/** `totalRows` minus the summed `kinds` rows — see the schema note. */
|
|
20102
|
+
unaccountedRows: number().int(),
|
|
20103
|
+
/** `totalBytes` minus the summed `kinds` bytes — see the schema note. */
|
|
20104
|
+
unaccountedBytes: number().int()
|
|
20105
|
+
});
|
|
19935
20106
|
/** Per-kind counts returned by the event-prune / device-delete mutations. */
|
|
19936
20107
|
var EventPruneCountsSchema = object({
|
|
19937
20108
|
motion: number().int(),
|
|
@@ -20086,7 +20257,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
20086
20257
|
until: number().optional(),
|
|
20087
20258
|
kinds: array(string()).optional(),
|
|
20088
20259
|
limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
|
|
20089
|
-
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
|
|
20260
|
+
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
|
|
20090
20261
|
deviceId: number(),
|
|
20091
20262
|
since: number(),
|
|
20092
20263
|
until: number(),
|
|
@@ -20135,6 +20306,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
20135
20306
|
}), TrackFlagsSchema, { kind: "mutation" }), method(object({}), EventStoreFootprintSchema, {
|
|
20136
20307
|
kind: "query",
|
|
20137
20308
|
auth: "admin"
|
|
20309
|
+
}), method(object({ deviceId: number().int().optional() }), EventMediaKindBreakdownSchema, {
|
|
20310
|
+
kind: "query",
|
|
20311
|
+
auth: "admin"
|
|
20138
20312
|
}), method(object({
|
|
20139
20313
|
olderThanMs: number(),
|
|
20140
20314
|
reason: OpsLogReasonSchema.optional()
|
|
@@ -22567,6 +22741,20 @@ method(object({
|
|
|
22567
22741
|
error: string().optional()
|
|
22568
22742
|
}), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
|
|
22569
22743
|
providerId: string(),
|
|
22744
|
+
/**
|
|
22745
|
+
* The location this config is an UNSAVED edit of, when there is one.
|
|
22746
|
+
*
|
|
22747
|
+
* `listLocations` replaces every declared secret with the redaction
|
|
22748
|
+
* sentinel, so the edit modal's form state holds the sentinel for any
|
|
22749
|
+
* credential the operator did not retype — and posting that here
|
|
22750
|
+
* without a way to resolve it makes the provider try to authenticate
|
|
22751
|
+
* as `__camstack_redacted__` and report the operator's own working
|
|
22752
|
+
* password as wrong. Given this id, the orchestrator restores each
|
|
22753
|
+
* sentinel from the stored config (same rule as `upsertLocation`)
|
|
22754
|
+
* before dispatching. Omitted by the "Add location" wizard, where
|
|
22755
|
+
* every value was typed just now and nothing is stored yet.
|
|
22756
|
+
*/
|
|
22757
|
+
locationId: string().optional(),
|
|
22570
22758
|
config: record(string(), unknown())
|
|
22571
22759
|
}), object({
|
|
22572
22760
|
ok: boolean(),
|
|
@@ -28202,6 +28390,9 @@ method(object({
|
|
|
28202
28390
|
}), method(RelocateResidueInputSchema, RelocateResidueSchema, {
|
|
28203
28391
|
kind: "query",
|
|
28204
28392
|
auth: "admin"
|
|
28393
|
+
}), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
|
|
28394
|
+
kind: "mutation",
|
|
28395
|
+
auth: "admin"
|
|
28205
28396
|
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
28206
28397
|
kind: "mutation",
|
|
28207
28398
|
auth: "admin"
|
|
@@ -28593,7 +28784,26 @@ var SceneMonitorStatusSchema = object({
|
|
|
28593
28784
|
monitors: array(SceneMonitorSchema),
|
|
28594
28785
|
lastFetchedAt: number()
|
|
28595
28786
|
});
|
|
28596
|
-
|
|
28787
|
+
/**
|
|
28788
|
+
* One camera's row in a `listScenesBatch` answer.
|
|
28789
|
+
*
|
|
28790
|
+
* `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
|
|
28791
|
+
* is a `defaultActive` wrapper, so every camera is asked; a camera whose
|
|
28792
|
+
* provider is absent (pipeline-analytics not deployed, the post-processing node
|
|
28793
|
+
* down) cannot answer, and that is not the same fact as a camera with no scenes
|
|
28794
|
+
* configured. Fanned out per camera the difference was visible — one query
|
|
28795
|
+
* errored while the others resolved — and a batch that returned only the rows
|
|
28796
|
+
* it managed would have destroyed it, silently, by making an unreachable camera
|
|
28797
|
+
* indistinguishable from one that answered `monitors: []`.
|
|
28798
|
+
*
|
|
28799
|
+
* So: EVERY requested deviceId gets a row. `status: null` means "this camera
|
|
28800
|
+
* could not be read"; `status.monitors: []` means "read, and it has none".
|
|
28801
|
+
*/
|
|
28802
|
+
var SceneMonitorStatusForDeviceSchema = object({
|
|
28803
|
+
deviceId: number(),
|
|
28804
|
+
status: SceneMonitorStatusSchema.nullable()
|
|
28805
|
+
});
|
|
28806
|
+
DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
|
|
28597
28807
|
deviceId: number(),
|
|
28598
28808
|
label: string(),
|
|
28599
28809
|
roi: MaskRectShapeSchema,
|
|
@@ -29917,6 +30127,27 @@ var CameraOccupancySnapshotSchema = object({
|
|
|
29917
30127
|
stationaryObjects: array(StationaryObjectSchema).readonly().optional()
|
|
29918
30128
|
});
|
|
29919
30129
|
/**
|
|
30130
|
+
* One camera's row in a `getCurrentSnapshotBatch` answer.
|
|
30131
|
+
*
|
|
30132
|
+
* THREE outcomes, and the single-camera method could only express two of them
|
|
30133
|
+
* because `snapshot: null` was already spoken for:
|
|
30134
|
+
*
|
|
30135
|
+
* - `read: 'read'`, `snapshot` present — the live occupancy reading.
|
|
30136
|
+
* - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
|
|
30137
|
+
* yet: no frame since boot, and no parked-object registry to hydrate from.
|
|
30138
|
+
* - `read: 'unreadable'` — the owner could not answer for this
|
|
30139
|
+
* camera. `snapshot` is null, and it does NOT mean "nothing parked here".
|
|
30140
|
+
*
|
|
30141
|
+
* Collapsing the last two is the failure this field exists to prevent: a
|
|
30142
|
+
* hydration that threw would otherwise render as an empty Stationary section,
|
|
30143
|
+
* which is a definite claim about a camera nobody could read.
|
|
30144
|
+
*/
|
|
30145
|
+
var CameraOccupancySnapshotForDeviceSchema = object({
|
|
30146
|
+
deviceId: number(),
|
|
30147
|
+
read: _enum(["read", "unreadable"]),
|
|
30148
|
+
snapshot: CameraOccupancySnapshotSchema.nullable()
|
|
30149
|
+
});
|
|
30150
|
+
/**
|
|
29920
30151
|
* Time-series resolution. The history methods return one bucket per
|
|
29921
30152
|
* step over the requested range. Smaller resolutions cost more
|
|
29922
30153
|
* memory + bandwidth; bound to discrete steps so caller cannot ask
|
|
@@ -29940,7 +30171,7 @@ var HistoryPointSchema = object({
|
|
|
29940
30171
|
/** Object count averaged over the bucket (rounded to nearest integer). */
|
|
29941
30172
|
count: number().int().nonnegative()
|
|
29942
30173
|
});
|
|
29943
|
-
DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
|
|
30174
|
+
DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
|
|
29944
30175
|
deviceId: number(),
|
|
29945
30176
|
zoneId: string(),
|
|
29946
30177
|
className: string().optional()
|
|
@@ -33305,6 +33536,12 @@ Object.freeze({
|
|
|
33305
33536
|
addonId: null,
|
|
33306
33537
|
access: "view"
|
|
33307
33538
|
},
|
|
33539
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": {
|
|
33540
|
+
capName: "pipeline-analytics",
|
|
33541
|
+
capScope: "device",
|
|
33542
|
+
addonId: null,
|
|
33543
|
+
access: "view"
|
|
33544
|
+
},
|
|
33308
33545
|
"pipelineAnalytics.getEventStoreFootprint": {
|
|
33309
33546
|
capName: "pipeline-analytics",
|
|
33310
33547
|
capScope: "device",
|
|
@@ -33323,6 +33560,12 @@ Object.freeze({
|
|
|
33323
33560
|
addonId: null,
|
|
33324
33561
|
access: "view"
|
|
33325
33562
|
},
|
|
33563
|
+
"pipelineAnalytics.getKeyEventsBatch": {
|
|
33564
|
+
capName: "pipeline-analytics",
|
|
33565
|
+
capScope: "device",
|
|
33566
|
+
addonId: null,
|
|
33567
|
+
access: "view"
|
|
33568
|
+
},
|
|
33326
33569
|
"pipelineAnalytics.getMotionEvents": {
|
|
33327
33570
|
capName: "pipeline-analytics",
|
|
33328
33571
|
capScope: "device",
|
|
@@ -34499,6 +34742,12 @@ Object.freeze({
|
|
|
34499
34742
|
addonId: null,
|
|
34500
34743
|
access: "view"
|
|
34501
34744
|
},
|
|
34745
|
+
"recording.reconcileLedgerAgainstDisk": {
|
|
34746
|
+
capName: "recording",
|
|
34747
|
+
capScope: "system",
|
|
34748
|
+
addonId: null,
|
|
34749
|
+
access: "create"
|
|
34750
|
+
},
|
|
34502
34751
|
"recording.refreshStorageLocationsForMigration": {
|
|
34503
34752
|
capName: "recording",
|
|
34504
34753
|
capScope: "system",
|
|
@@ -34625,6 +34874,12 @@ Object.freeze({
|
|
|
34625
34874
|
addonId: null,
|
|
34626
34875
|
access: "view"
|
|
34627
34876
|
},
|
|
34877
|
+
"sceneMonitor.listScenesBatch": {
|
|
34878
|
+
capName: "scene-monitor",
|
|
34879
|
+
capScope: "device",
|
|
34880
|
+
addonId: null,
|
|
34881
|
+
access: "view"
|
|
34882
|
+
},
|
|
34628
34883
|
"sceneMonitor.recheckNow": {
|
|
34629
34884
|
capName: "scene-monitor",
|
|
34630
34885
|
capScope: "device",
|
|
@@ -35981,6 +36236,12 @@ Object.freeze({
|
|
|
35981
36236
|
addonId: null,
|
|
35982
36237
|
access: "view"
|
|
35983
36238
|
},
|
|
36239
|
+
"zoneAnalytics.getCurrentSnapshotBatch": {
|
|
36240
|
+
capName: "zone-analytics",
|
|
36241
|
+
capScope: "device",
|
|
36242
|
+
addonId: null,
|
|
36243
|
+
access: "view"
|
|
36244
|
+
},
|
|
35984
36245
|
"zoneAnalytics.getUnzonedHistory": {
|
|
35985
36246
|
capName: "zone-analytics",
|
|
35986
36247
|
capScope: "device",
|
|
@@ -36970,6 +37231,11 @@ Object.freeze({
|
|
|
36970
37231
|
form: "single",
|
|
36971
37232
|
optional: false
|
|
36972
37233
|
}],
|
|
37234
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": [{
|
|
37235
|
+
name: "deviceId",
|
|
37236
|
+
form: "single",
|
|
37237
|
+
optional: true
|
|
37238
|
+
}],
|
|
36973
37239
|
"pipelineAnalytics.getGroup": [{
|
|
36974
37240
|
name: "deviceId",
|
|
36975
37241
|
form: "single",
|
|
@@ -36980,6 +37246,11 @@ Object.freeze({
|
|
|
36980
37246
|
form: "single",
|
|
36981
37247
|
optional: false
|
|
36982
37248
|
}],
|
|
37249
|
+
"pipelineAnalytics.getKeyEventsBatch": [{
|
|
37250
|
+
name: "deviceIds",
|
|
37251
|
+
form: "array",
|
|
37252
|
+
optional: false
|
|
37253
|
+
}],
|
|
36983
37254
|
"pipelineAnalytics.getMotionEvents": [{
|
|
36984
37255
|
name: "deviceId",
|
|
36985
37256
|
form: "single",
|
|
@@ -37420,6 +37691,11 @@ Object.freeze({
|
|
|
37420
37691
|
form: "single",
|
|
37421
37692
|
optional: false
|
|
37422
37693
|
}],
|
|
37694
|
+
"recording.reconcileLedgerAgainstDisk": [{
|
|
37695
|
+
name: "deviceId",
|
|
37696
|
+
form: "single",
|
|
37697
|
+
optional: true
|
|
37698
|
+
}],
|
|
37423
37699
|
"recording.relocateFootage": [{
|
|
37424
37700
|
name: "deviceId",
|
|
37425
37701
|
form: "single",
|
|
@@ -37485,6 +37761,11 @@ Object.freeze({
|
|
|
37485
37761
|
form: "single",
|
|
37486
37762
|
optional: false
|
|
37487
37763
|
}],
|
|
37764
|
+
"sceneMonitor.listScenesBatch": [{
|
|
37765
|
+
name: "deviceIds",
|
|
37766
|
+
form: "array",
|
|
37767
|
+
optional: false
|
|
37768
|
+
}],
|
|
37488
37769
|
"sceneMonitor.recheckNow": [{
|
|
37489
37770
|
name: "deviceId",
|
|
37490
37771
|
form: "single",
|
|
@@ -37746,6 +38027,11 @@ Object.freeze({
|
|
|
37746
38027
|
form: "single",
|
|
37747
38028
|
optional: false
|
|
37748
38029
|
}],
|
|
38030
|
+
"zoneAnalytics.getCurrentSnapshotBatch": [{
|
|
38031
|
+
name: "deviceIds",
|
|
38032
|
+
form: "array",
|
|
38033
|
+
optional: false
|
|
38034
|
+
}],
|
|
37749
38035
|
"zoneAnalytics.getUnzonedHistory": [{
|
|
37750
38036
|
name: "deviceId",
|
|
37751
38037
|
form: "single",
|
|
@@ -40855,6 +41141,124 @@ function composeCameraStatus(input) {
|
|
|
40855
41141
|
};
|
|
40856
41142
|
}
|
|
40857
41143
|
//#endregion
|
|
41144
|
+
//#region src/camera-status/fleet-read-pass.ts
|
|
41145
|
+
/**
|
|
41146
|
+
* Keep a rejection from becoming an UNHANDLED one.
|
|
41147
|
+
*
|
|
41148
|
+
* A shared promise is created by the first camera to ask and consumed by the
|
|
41149
|
+
* others later — `listBindableCapsForDeviceType` is reached in stage 2 of
|
|
41150
|
+
* `readSwitchAuthorities`, after an await, so two cameras genuinely attach
|
|
41151
|
+
* their handlers in different ticks. Without this the first rejection would be
|
|
41152
|
+
* unhandled for the window in between.
|
|
41153
|
+
*
|
|
41154
|
+
* The handler is a no-op ON PURPOSE: it never becomes the answer. Every real
|
|
41155
|
+
* consumer attaches its own `.catch` and reports the failure per camera.
|
|
41156
|
+
*/
|
|
41157
|
+
function keepAlive(p) {
|
|
41158
|
+
p.catch(() => void 0);
|
|
41159
|
+
return p;
|
|
41160
|
+
}
|
|
41161
|
+
var FleetReadPass = class {
|
|
41162
|
+
deps;
|
|
41163
|
+
roster = null;
|
|
41164
|
+
bindings = null;
|
|
41165
|
+
mutes = null;
|
|
41166
|
+
bindableByType = /* @__PURE__ */ new Map();
|
|
41167
|
+
slotsByNode = /* @__PURE__ */ new Map();
|
|
41168
|
+
rtspByNode = /* @__PURE__ */ new Map();
|
|
41169
|
+
provisioningByNode = /* @__PURE__ */ new Map();
|
|
41170
|
+
selectedEngineByNode = /* @__PURE__ */ new Map();
|
|
41171
|
+
constructor(deps) {
|
|
41172
|
+
this.deps = deps;
|
|
41173
|
+
}
|
|
41174
|
+
/** The cameras this pass covers — the set both batch reads are issued for. */
|
|
41175
|
+
get deviceIds() {
|
|
41176
|
+
return this.deps.deviceIds;
|
|
41177
|
+
}
|
|
41178
|
+
/**
|
|
41179
|
+
* The device rows for the pass's set.
|
|
41180
|
+
*
|
|
41181
|
+
* **The shape follows the SET, not the caller.** A pass of one asks
|
|
41182
|
+
* `getDevice` — the exact question, and the one the write path
|
|
41183
|
+
* (`CameraSwitchService.setCameraSwitch`) has always asked. A pass of many
|
|
41184
|
+
* asks `listAll({deviceIds})`, which exists precisely because "these N" was
|
|
41185
|
+
* otherwise either N round trips or the whole fleet. Answering a set of one
|
|
41186
|
+
* with the fleet-shaped method would be no cheaper and would move the write
|
|
41187
|
+
* path onto a read it does not need.
|
|
41188
|
+
*
|
|
41189
|
+
* `projection: 'slim'` because the only fields read off it are `type` and
|
|
41190
|
+
* `disabled`: the full projection reads each device's settings row, which is
|
|
41191
|
+
* the per-device round trip this whole change exists to remove.
|
|
41192
|
+
*/
|
|
41193
|
+
deviceRoster(api) {
|
|
41194
|
+
const single = this.deps.deviceIds.length === 1 ? this.deps.deviceIds[0] : void 0;
|
|
41195
|
+
const source = single === void 0 ? "deviceManager.listAll" : "deviceManager.getDevice";
|
|
41196
|
+
if (this.roster === null) this.roster = keepAlive(single === void 0 ? api.deviceManager.listAll.query({
|
|
41197
|
+
deviceIds: [...this.deps.deviceIds],
|
|
41198
|
+
projection: "slim"
|
|
41199
|
+
}) : api.deviceManager.getDevice.query({ deviceId: single }).then((row) => row === null ? [] : [row]));
|
|
41200
|
+
return {
|
|
41201
|
+
source,
|
|
41202
|
+
result: this.roster
|
|
41203
|
+
};
|
|
41204
|
+
}
|
|
41205
|
+
/** The binding rows for the pass's set — `getBindings` for one, the batch for many. */
|
|
41206
|
+
bindingRows(api) {
|
|
41207
|
+
const single = this.deps.deviceIds.length === 1 ? this.deps.deviceIds[0] : void 0;
|
|
41208
|
+
const source = single === void 0 ? "deviceManager.getBindingsBatch" : "deviceManager.getBindings";
|
|
41209
|
+
if (this.bindings === null) this.bindings = keepAlive(single === void 0 ? api.deviceManager.getBindingsBatch.query({ deviceIds: [...this.deps.deviceIds] }) : api.deviceManager.getBindings.query({ deviceId: single }).then((row) => [row]));
|
|
41210
|
+
return {
|
|
41211
|
+
source,
|
|
41212
|
+
result: this.bindings
|
|
41213
|
+
};
|
|
41214
|
+
}
|
|
41215
|
+
/** The fleet's muted-camera list. One answer for every camera in the pass. */
|
|
41216
|
+
mutedDevices(api) {
|
|
41217
|
+
if (this.mutes === null) this.mutes = keepAlive(api.notificationRules.listDeviceMutes.query({}));
|
|
41218
|
+
return this.mutes;
|
|
41219
|
+
}
|
|
41220
|
+
/** The bindable caps for one device TYPE. Every camera shares one answer. */
|
|
41221
|
+
bindableCapsFor(api, deviceType) {
|
|
41222
|
+
const existing = this.bindableByType.get(deviceType);
|
|
41223
|
+
if (existing !== void 0) return existing;
|
|
41224
|
+
const created = keepAlive(api.deviceManager.listBindableCapsForDeviceType.query({ deviceType }));
|
|
41225
|
+
this.bindableByType.set(deviceType, created);
|
|
41226
|
+
return created;
|
|
41227
|
+
}
|
|
41228
|
+
/** One source node's whole profile-slot table. */
|
|
41229
|
+
profileSlots(api, sourceNodeId) {
|
|
41230
|
+
const existing = this.slotsByNode.get(sourceNodeId);
|
|
41231
|
+
if (existing !== void 0) return existing;
|
|
41232
|
+
const created = keepAlive(api.streamBroker.listAllProfileSlots.query(void 0, nodePin(sourceNodeId)));
|
|
41233
|
+
this.slotsByNode.set(sourceNodeId, created);
|
|
41234
|
+
return created;
|
|
41235
|
+
}
|
|
41236
|
+
/** One source node's whole RTSP-restream table. */
|
|
41237
|
+
rtspEntries(api, sourceNodeId) {
|
|
41238
|
+
const existing = this.rtspByNode.get(sourceNodeId);
|
|
41239
|
+
if (existing !== void 0) return existing;
|
|
41240
|
+
const created = keepAlive(api.streamBroker.getAllRtspEntries.query({}, nodePin(sourceNodeId)));
|
|
41241
|
+
this.rtspByNode.set(sourceNodeId, created);
|
|
41242
|
+
return created;
|
|
41243
|
+
}
|
|
41244
|
+
/** One detection node's runtime-provisioning snapshot. */
|
|
41245
|
+
engineProvisioning(api, detectionNodeId) {
|
|
41246
|
+
const existing = this.provisioningByNode.get(detectionNodeId);
|
|
41247
|
+
if (existing !== void 0) return existing;
|
|
41248
|
+
const created = keepAlive(api.pipelineExecutor.getEngineProvisioning.query({ nodeId: detectionNodeId }));
|
|
41249
|
+
this.provisioningByNode.set(detectionNodeId, created);
|
|
41250
|
+
return created;
|
|
41251
|
+
}
|
|
41252
|
+
/** The executor's bootstrap engine, as the detection stage reads it. */
|
|
41253
|
+
selectedEngine(api, detectionNodeId) {
|
|
41254
|
+
const existing = this.selectedEngineByNode.get(detectionNodeId);
|
|
41255
|
+
if (existing !== void 0) return existing;
|
|
41256
|
+
const created = keepAlive(api.pipelineExecutor.getSelectedEngine.query({ nodeId: detectionNodeId }));
|
|
41257
|
+
this.selectedEngineByNode.set(detectionNodeId, created);
|
|
41258
|
+
return created;
|
|
41259
|
+
}
|
|
41260
|
+
};
|
|
41261
|
+
//#endregion
|
|
40858
41262
|
//#region src/camera-status-service.ts
|
|
40859
41263
|
/** WebRTC consumer kinds counted toward `BrokerResult.webrtcSessions`. */
|
|
40860
41264
|
var WEBRTC_KINDS = new Set([
|
|
@@ -41043,7 +41447,7 @@ var CameraStatusService = class {
|
|
|
41043
41447
|
* broker's actual decode-session node into `liveDecoder` (T6) — the first
|
|
41044
41448
|
* slot that reports one wins.
|
|
41045
41449
|
*/
|
|
41046
|
-
buildBrokerStage(api, allSlotsFetch, deviceId, sourceNodeId, liveDecoder, sink) {
|
|
41450
|
+
buildBrokerStage(api, allSlotsFetch, deviceId, sourceNodeId, pass, liveDecoder, sink) {
|
|
41047
41451
|
if (!api || !allSlotsFetch) return Promise.resolve(null);
|
|
41048
41452
|
return this.boundedStage(allSlotsFetch.then(async (slots) => {
|
|
41049
41453
|
const deviceSlots = slots.filter((s) => s.deviceId === deviceId);
|
|
@@ -41053,12 +41457,13 @@ var CameraStatusService = class {
|
|
|
41053
41457
|
rtspRestream: false
|
|
41054
41458
|
};
|
|
41055
41459
|
const [statsAndClients, rtspEntry] = await Promise.all([Promise.all(deviceSlots.map(async (slot) => {
|
|
41460
|
+
const [stats, clients] = await Promise.all([api.streamBroker.getBrokerStats.query({ brokerId: slot.brokerId }, nodePin(sourceNodeId)).catch(() => null), api.streamBroker.listClients.query({ brokerId: slot.brokerId }, nodePin(sourceNodeId)).catch(() => null)]);
|
|
41056
41461
|
return {
|
|
41057
41462
|
slot,
|
|
41058
|
-
stats
|
|
41059
|
-
clients
|
|
41463
|
+
stats,
|
|
41464
|
+
clients
|
|
41060
41465
|
};
|
|
41061
|
-
})),
|
|
41466
|
+
})), pass.rtspEntries(api, sourceNodeId).catch(() => null)]);
|
|
41062
41467
|
const profileDetails = statsAndClients.map(({ slot, stats, clients }) => ({
|
|
41063
41468
|
profile: slot.profile,
|
|
41064
41469
|
status: slot.status,
|
|
@@ -41119,13 +41524,16 @@ var CameraStatusService = class {
|
|
|
41119
41524
|
};
|
|
41120
41525
|
}
|
|
41121
41526
|
/** Detection stage (pipeline-executor + runner metrics). */
|
|
41122
|
-
buildDetectionStage(api, detectionNodeId, deviceId, sink) {
|
|
41527
|
+
buildDetectionStage(api, detectionNodeId, deviceId, pass, sink) {
|
|
41123
41528
|
if (!api || !detectionNodeId) return Promise.resolve(null);
|
|
41124
|
-
return this.boundedStage(Promise.all([
|
|
41125
|
-
|
|
41529
|
+
return this.boundedStage(Promise.all([
|
|
41530
|
+
pass.engineProvisioning(api, detectionNodeId).catch(() => null),
|
|
41531
|
+
pass.selectedEngine(api, detectionNodeId).catch(() => null),
|
|
41532
|
+
api.pipelineRunner.getCameraMetrics.query({
|
|
41126
41533
|
deviceId,
|
|
41127
41534
|
nodeId: detectionNodeId
|
|
41128
|
-
}).catch(() => null)
|
|
41535
|
+
}).catch(() => null)
|
|
41536
|
+
]).then(async ([provisioning, engine, metrics]) => {
|
|
41129
41537
|
const phase = (() => {
|
|
41130
41538
|
const p = metrics?.phase;
|
|
41131
41539
|
if (p === "active") return "active";
|
|
@@ -41237,9 +41645,9 @@ var CameraStatusService = class {
|
|
|
41237
41645
|
* badge on a working camera); what matters is that the emptiness travels with
|
|
41238
41646
|
* the reason it is empty.
|
|
41239
41647
|
*/
|
|
41240
|
-
buildSwitchStage(deviceId, sink) {
|
|
41648
|
+
buildSwitchStage(deviceId, pass, sink) {
|
|
41241
41649
|
const startedAt = Date.now();
|
|
41242
|
-
return this.boundedStage(this.deps.switchAuthoritiesFor(deviceId), STAGE_TIMEOUT_MS, "switches", deviceId, sink).then((reads) => {
|
|
41650
|
+
return this.boundedStage(this.deps.switchAuthoritiesFor(deviceId, pass), STAGE_TIMEOUT_MS, "switches", deviceId, sink).then((reads) => {
|
|
41243
41651
|
if (reads === null) return [];
|
|
41244
41652
|
const { switchedOff, unreadable } = composeSwitchedOff(reads);
|
|
41245
41653
|
if (unreadable.length > 0) this.recordDegraded(sink, deviceId, "switches", "partial", Date.now() - startedAt, { unreadableAuthorities: unreadable });
|
|
@@ -41257,18 +41665,28 @@ var CameraStatusService = class {
|
|
|
41257
41665
|
* `null` of a camera that legitimately has no such stage.
|
|
41258
41666
|
*/
|
|
41259
41667
|
async getCameraStatus(deviceId) {
|
|
41668
|
+
return this.composeOne(deviceId, new FleetReadPass({ deviceIds: [deviceId] }));
|
|
41669
|
+
}
|
|
41670
|
+
/**
|
|
41671
|
+
* One camera's status, composed WITHIN a pass.
|
|
41672
|
+
*
|
|
41673
|
+
* Everything the camera alone can answer is fetched here; everything the pass
|
|
41674
|
+
* already knows (a node's slot table, the fleet's mute list, a device type's
|
|
41675
|
+
* bindable caps) is asked of `pass`, which issues it once for the whole call.
|
|
41676
|
+
*/
|
|
41677
|
+
async composeOne(deviceId, pass) {
|
|
41260
41678
|
const api = this.deps.api();
|
|
41261
41679
|
const degradations = { entries: [] };
|
|
41262
41680
|
const { detectionNodeId, sourceNodeId, pinned, detectionReason, audioNodeId, audioPinned } = this.buildAssignmentContext(deviceId);
|
|
41263
41681
|
const liveDecoder = { nodeId: null };
|
|
41264
|
-
const allSlotsFetch = api ?
|
|
41682
|
+
const allSlotsFetch = api ? pass.profileSlots(api, sourceNodeId) : null;
|
|
41265
41683
|
const sourceFetch = this.buildSourceStage(api, allSlotsFetch, deviceId, degradations);
|
|
41266
|
-
const brokerFetch = this.buildBrokerStage(api, allSlotsFetch, deviceId, sourceNodeId, liveDecoder, degradations);
|
|
41684
|
+
const brokerFetch = this.buildBrokerStage(api, allSlotsFetch, deviceId, sourceNodeId, pass, liveDecoder, degradations);
|
|
41267
41685
|
const decoderFetch = this.buildDecoderStage(detectionNodeId);
|
|
41268
41686
|
const motionResult = this.buildMotionStage(deviceId);
|
|
41269
|
-
const detectionFetch = this.buildDetectionStage(api, detectionNodeId, deviceId, degradations);
|
|
41687
|
+
const detectionFetch = this.buildDetectionStage(api, detectionNodeId, deviceId, pass, degradations);
|
|
41270
41688
|
const recordingFetch = this.buildRecordingStage(api, deviceId, degradations);
|
|
41271
|
-
const switchesFetch = this.buildSwitchStage(deviceId, degradations);
|
|
41689
|
+
const switchesFetch = this.buildSwitchStage(deviceId, pass, degradations);
|
|
41272
41690
|
const [sourceResult, brokerResult, decoderResult, detectionResult, recordingResult, switchedOff] = await Promise.all([
|
|
41273
41691
|
sourceFetch,
|
|
41274
41692
|
brokerFetch,
|
|
@@ -41311,15 +41729,30 @@ var CameraStatusService = class {
|
|
|
41311
41729
|
* `deviceIds` defaults to all cameras currently tracked by the
|
|
41312
41730
|
* orchestrator's assignment map when omitted.
|
|
41313
41731
|
*
|
|
41314
|
-
*
|
|
41315
|
-
*
|
|
41316
|
-
*
|
|
41317
|
-
*
|
|
41318
|
-
*
|
|
41732
|
+
* The whole call runs inside ONE {@link FleetReadPass}, and that is what
|
|
41733
|
+
* makes a five-second poll affordable. Composing a camera reaches seven
|
|
41734
|
+
* stages, but six of the questions those stages ask are about a NODE, a
|
|
41735
|
+
* device TYPE or the whole FLEET — the slot table, the restream table, the
|
|
41736
|
+
* mute list, the engine pair, the bindable caps — and asking them per camera
|
|
41737
|
+
* meant N identical round trips for N identical answers. Two more (the device
|
|
41738
|
+
* row, the binding table) are per-camera questions the device manager already
|
|
41739
|
+
* answers for a set. The pass asks each exactly once and shares the promise;
|
|
41740
|
+
* it holds nothing between calls, so the next poll asks again from scratch.
|
|
41741
|
+
*
|
|
41742
|
+
* What is left is genuinely per camera and stays per camera: the broker slot
|
|
41743
|
+
* stats, the runner metrics, the recorder, the broker mute and the camera's
|
|
41744
|
+
* own privacy provider. Collapsing any of those would be a narrower answer,
|
|
41745
|
+
* not a cheaper one.
|
|
41746
|
+
*
|
|
41747
|
+
* `Promise.all` over per-device composition (no concurrency cap). The fan-out
|
|
41748
|
+
* is now ~8 round trips per camera rather than ~16, and the six fleet reads
|
|
41749
|
+
* no longer multiply — which is the part that made the count grow with the
|
|
41750
|
+
* fleet twice over.
|
|
41319
41751
|
*/
|
|
41320
41752
|
async getCameraStatuses(deviceIds) {
|
|
41321
41753
|
const ids = deviceIds !== void 0 && deviceIds.length > 0 ? deviceIds : this.deps.listAssignedDeviceIds();
|
|
41322
|
-
|
|
41754
|
+
const pass = new FleetReadPass({ deviceIds: ids });
|
|
41755
|
+
return Promise.all(ids.map((deviceId) => this.composeOne(deviceId, pass)));
|
|
41323
41756
|
}
|
|
41324
41757
|
};
|
|
41325
41758
|
//#endregion
|
|
@@ -41568,10 +42001,15 @@ async function probeCamera(api, deviceId, deps) {
|
|
|
41568
42001
|
* @param deviceId The camera.
|
|
41569
42002
|
* @param deps Logger + source-owner resolver.
|
|
41570
42003
|
*/
|
|
41571
|
-
async function readSwitchAuthorities(api, deviceId, deps) {
|
|
42004
|
+
async function readSwitchAuthorities(api, deviceId, deps, sharedPass) {
|
|
41572
42005
|
if (!api) return unknownReads(deviceId);
|
|
41573
|
-
const
|
|
41574
|
-
|
|
42006
|
+
const pass = sharedPass ?? new FleetReadPass({ deviceIds: [deviceId] });
|
|
42007
|
+
const roster = pass.deviceRoster(api);
|
|
42008
|
+
const devicePromise = bounded(deps, deviceId, roster.source, roster.result.then((rows) => {
|
|
42009
|
+
const row = rows.find((d) => d.id === deviceId);
|
|
42010
|
+
return isDeviceShape(row) ? row : null;
|
|
42011
|
+
}).catch((err) => {
|
|
42012
|
+
warnUnreachable(deps, deviceId, roster.source, err);
|
|
41575
42013
|
return null;
|
|
41576
42014
|
}), null);
|
|
41577
42015
|
const unknownBindings = {
|
|
@@ -41579,11 +42017,14 @@ async function readSwitchAuthorities(api, deviceId, deps) {
|
|
|
41579
42017
|
providerAddonIdByCap: /* @__PURE__ */ new Map(),
|
|
41580
42018
|
allCapNames: null
|
|
41581
42019
|
};
|
|
41582
|
-
const
|
|
42020
|
+
const bindingRead = pass.bindingRows(api);
|
|
42021
|
+
const bindingsPromise = bounded(deps, deviceId, bindingRead.source, bindingRead.result.then((rows) => {
|
|
42022
|
+
const row = rows.find((r) => r.deviceId === deviceId);
|
|
42023
|
+
if (row === void 0) return unknownBindings;
|
|
41583
42024
|
const active = [];
|
|
41584
42025
|
const all = [];
|
|
41585
42026
|
const providers = /* @__PURE__ */ new Map();
|
|
41586
|
-
for (const e of
|
|
42027
|
+
for (const e of row.entries) {
|
|
41587
42028
|
all.push(e.capName);
|
|
41588
42029
|
if (e.kind !== "wrapped") continue;
|
|
41589
42030
|
active.push(e.capName);
|
|
@@ -41595,14 +42036,14 @@ async function readSwitchAuthorities(api, deviceId, deps) {
|
|
|
41595
42036
|
allCapNames: all
|
|
41596
42037
|
};
|
|
41597
42038
|
}).catch((err) => {
|
|
41598
|
-
warnUnreachable(deps, deviceId,
|
|
42039
|
+
warnUnreachable(deps, deviceId, bindingRead.source, err);
|
|
41599
42040
|
return unknownBindings;
|
|
41600
42041
|
}), unknownBindings);
|
|
41601
42042
|
const recordingPromise = bounded(deps, deviceId, "recording.getDeviceConfig", api.recording.getDeviceConfig.query({ deviceId }).then((c) => isRecordingConfig(c) ? c : null).catch((err) => {
|
|
41602
42043
|
warnUnreachable(deps, deviceId, "recording.getDeviceConfig", err);
|
|
41603
42044
|
return null;
|
|
41604
42045
|
}), null);
|
|
41605
|
-
const mutesPromise = bounded(deps, deviceId, "notificationRules.listDeviceMutes",
|
|
42046
|
+
const mutesPromise = bounded(deps, deviceId, "notificationRules.listDeviceMutes", pass.mutedDevices(api).then((r) => r.mutedDeviceIds).catch((err) => {
|
|
41606
42047
|
warnUnreachable(deps, deviceId, "notificationRules.listDeviceMutes", err);
|
|
41607
42048
|
return null;
|
|
41608
42049
|
}), null);
|
|
@@ -41617,7 +42058,7 @@ async function readSwitchAuthorities(api, deviceId, deps) {
|
|
|
41617
42058
|
mutesPromise,
|
|
41618
42059
|
brokerAudioPromise
|
|
41619
42060
|
]);
|
|
41620
|
-
const [bindable, privacy] = await Promise.all([device === null ? Promise.resolve(null) : bounded(deps, deviceId, "deviceManager.listBindableCapsForDeviceType",
|
|
42061
|
+
const [bindable, privacy] = await Promise.all([device === null ? Promise.resolve(null) : bounded(deps, deviceId, "deviceManager.listBindableCapsForDeviceType", pass.bindableCapsFor(api, device.type).catch((err) => {
|
|
41621
42062
|
warnUnreachable(deps, deviceId, "listBindableCapsForDeviceType", err);
|
|
41622
42063
|
return null;
|
|
41623
42064
|
}), null), readPrivacyPlanes(api, deviceId, deps, bindings.allCapNames)]);
|
|
@@ -50324,11 +50765,11 @@ async function buildOrchestratorControllers(deps) {
|
|
|
50324
50765
|
assignSource: (deviceId) => topology.assignSource(deviceId),
|
|
50325
50766
|
listAssignedDeviceIds: () => [...new Set([...ledger.listAssignedDeviceIds(), ...detectionWiring.activeDeviceIds()])],
|
|
50326
50767
|
isSessionCamera: (deviceId) => deps.isSessionCamera(deviceId),
|
|
50327
|
-
switchAuthoritiesFor: async (deviceId) => (await readSwitchAuthorities(deps.ctx().api ?? null, deviceId, {
|
|
50768
|
+
switchAuthoritiesFor: async (deviceId, pass) => (await readSwitchAuthorities(deps.ctx().api ?? null, deviceId, {
|
|
50328
50769
|
logger: deps.ctx().logger,
|
|
50329
50770
|
assignSource: (id) => topology.assignSource(id),
|
|
50330
50771
|
warnSampler: switchWarnSampler
|
|
50331
|
-
})).derivation
|
|
50772
|
+
}, pass)).derivation
|
|
50332
50773
|
});
|
|
50333
50774
|
const reconcile = new ReconcileController({
|
|
50334
50775
|
api: () => deps.ctx().api ?? null,
|