@camstack/addon-decoder-nodeav 1.1.8 → 1.1.10
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/index.js +687 -532
- package/dist/index.mjs +673 -528
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,276 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DecoderFrameRingSink, FrameRingReaderCache, RING_BUDGET_MB, SEGMENT_NAME_PREFIX, makeSegmentName, unlinkSegment } from "@camstack/shm-ring";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { readdirSync } from "node:fs";
|
|
4
|
-
//#region src/frame-ring-sink.ts
|
|
5
|
-
/**
|
|
6
|
-
* `DecoderFrameRingSink` — the decoder's shared-memory write side (Phase 5 / D9).
|
|
7
|
-
*
|
|
8
|
-
* When a decoder session is configured with `frameSink: 'shm'`, the decoder
|
|
9
|
-
* **owns** the shared-memory ring segment for that stream: it creates the
|
|
10
|
-
* segment on the first decoded frame (when the output geometry is known),
|
|
11
|
-
* writes every subsequent decoded frame into the ring via a `FrameRingWriter`,
|
|
12
|
-
* and closes + unlinks the segment when the session is destroyed.
|
|
13
|
-
*
|
|
14
|
-
* What leaves the decoder is no longer the pixel `Buffer` — it is a tiny,
|
|
15
|
-
* serialisable `FrameHandle` (`FrameRingWriter.writeFrame`'s return value).
|
|
16
|
-
* Same-host consumers (motion, detection, the WebRTC encoder) open the same
|
|
17
|
-
* segment with a `FrameRingReader` and read the pixels zero-copy.
|
|
18
|
-
*
|
|
19
|
-
* ## Lazy segment creation
|
|
20
|
-
*
|
|
21
|
-
* The segment cannot be sized until the first frame: `slotByteLength` is
|
|
22
|
-
* `width × height × bytesPerPixel`, and the output dimensions are only known
|
|
23
|
-
* once the scaler has produced its first `dstFrame`. So `writeFrame` is a
|
|
24
|
-
* no-op-until-armed: the first call sizes + creates the segment, every later
|
|
25
|
-
* call writes into it.
|
|
26
|
-
*
|
|
27
|
-
* ## Resolution-change decision
|
|
28
|
-
*
|
|
29
|
-
* A live camera stream can change resolution mid-stream (the decoder's scaler
|
|
30
|
-
* is rebuilt on a config toggle, or the source renegotiates). The slot is
|
|
31
|
-
* sized for the **first** frame's geometry. A later frame that no longer fits
|
|
32
|
-
* the slot triggers a **segment re-create**: the old segment is closed +
|
|
33
|
-
* unlinked and a fresh, larger segment is created under a new generation-tagged
|
|
34
|
-
* name. This is simpler and leak-free versus over-allocating slots for a
|
|
35
|
-
* worst-case 4K frame on every stream; resolution changes on a live camera are
|
|
36
|
-
* rare, and a brief gap while consumers re-open the segment is acceptable
|
|
37
|
-
* (latest-wins — a missed frame is correct behaviour).
|
|
38
|
-
*/
|
|
39
|
-
/**
|
|
40
|
-
* Per-ring shared-memory budget (MB) — the slot count is derived per-resolution
|
|
41
|
-
* from this budget via {@link deriveSlotCount}, so a 360p stream gets many
|
|
42
|
-
* slots and a 4K stream a few, both inside the same memory footprint.
|
|
43
|
-
*
|
|
44
|
-
* Read ONCE at module load from `CAMSTACK_SHM_RING_BUDGET_MB`; a non-finite or
|
|
45
|
-
* non-positive value falls back to the 16 MB default.
|
|
46
|
-
*
|
|
47
|
-
* The default is deliberately small (16 MB) so many concurrent per-camera rings
|
|
48
|
-
* fit inside a bounded `/dev/shm`. The decoder output is capped at 640px wide, so
|
|
49
|
-
* a slot is ≤ ~920 KB and 16 MB still yields ~17–70 latest-wins slots. A ring
|
|
50
|
-
* segment larger than the container's `/dev/shm` tmpfs backing (Docker default is
|
|
51
|
-
* only 64 MB) faults an **uncatchable SIGBUS** on write past `st_size` — the old
|
|
52
|
-
* 128 MB default overflowed a 64 MB `/dev/shm` and crashed the decoder on 8MP
|
|
53
|
-
* streams. Pair this with a `--shm-size` that scales with concurrent-decode load.
|
|
54
|
-
*/
|
|
55
|
-
var RING_BUDGET_MB = (() => {
|
|
56
|
-
const raw = Number(process.env["CAMSTACK_SHM_RING_BUDGET_MB"]);
|
|
57
|
-
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 16;
|
|
58
|
-
})();
|
|
59
|
-
/** {@link RING_BUDGET_MB} in bytes — the budget passed to `deriveSlotCount`. */
|
|
60
|
-
var RING_BUDGET_BYTES = RING_BUDGET_MB * 1024 * 1024;
|
|
61
|
-
/** A unique, stable shared-memory segment name for a decoder stream.
|
|
62
|
-
*
|
|
63
|
-
* macOS POSIX shm names are capped at ~31 characters (`PSHMNAMLEN`). A
|
|
64
|
-
* `camstack.frames.<deviceId>.<streamId>` scheme overflows that for realistic
|
|
65
|
-
* ids, so the sink uses a short, collision-resistant scheme instead:
|
|
66
|
-
* `csf.<base36 hash>.<gen>`. The hash folds the device id, the session tag
|
|
67
|
-
* and a per-process random salt; the generation suffix makes a re-created
|
|
68
|
-
* segment (resolution change) a distinct name so a stale consumer mapping is
|
|
69
|
-
* never silently reused.
|
|
70
|
-
*/
|
|
71
|
-
/**
|
|
72
|
-
* Shared prefix for every decoder shm segment name. Startup orphan reclamation
|
|
73
|
-
* (`purgeOrphanSegments`) keys off this to find segments left behind by a
|
|
74
|
-
* crashed prior instance.
|
|
75
|
-
*/
|
|
76
|
-
var SEGMENT_NAME_PREFIX = "csf.";
|
|
77
|
-
function makeSegmentName(seed, generation) {
|
|
78
|
-
let hash = 5381;
|
|
79
|
-
for (let i = 0; i < seed.length; i += 1) hash = (hash << 5) + hash + seed.charCodeAt(i) | 0;
|
|
80
|
-
return `${SEGMENT_NAME_PREFIX}${(hash >>> 0).toString(36)}.${generation}`;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* The decoder-side owner of one stream's shared-memory frame ring.
|
|
84
|
-
*
|
|
85
|
-
* Not constructed until a session actually uses the shm sink; the segment
|
|
86
|
-
* itself is created lazily on the first `writeFrame`.
|
|
87
|
-
*/
|
|
88
|
-
var DecoderFrameRingSink = class {
|
|
89
|
-
seed;
|
|
90
|
-
logger;
|
|
91
|
-
nodeId;
|
|
92
|
-
segment = null;
|
|
93
|
-
writer = null;
|
|
94
|
-
segmentName = null;
|
|
95
|
-
slotByteLength = 0;
|
|
96
|
-
generation = 0;
|
|
97
|
-
destroyed = false;
|
|
98
|
-
/** Frames committed into the ring across this sink's lifetime (all generations). */
|
|
99
|
-
framesWritten = 0;
|
|
100
|
-
constructor(options) {
|
|
101
|
-
const salt = Math.random().toString(36).slice(2, 8);
|
|
102
|
-
this.seed = `${options.seed}.${salt}`;
|
|
103
|
-
this.logger = options.logger;
|
|
104
|
-
this.nodeId = options.nodeId;
|
|
105
|
-
}
|
|
106
|
-
/** Whether a segment has been created (i.e. at least one frame written). */
|
|
107
|
-
get isArmed() {
|
|
108
|
-
return this.writer !== null;
|
|
109
|
-
}
|
|
110
|
-
/** The current segment name, or `null` before the first frame. */
|
|
111
|
-
get currentSegmentName() {
|
|
112
|
-
return this.segmentName;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Write one decoded frame into the ring and return its `FrameHandle`.
|
|
116
|
-
*
|
|
117
|
-
* On the first call (or after a geometry change that overflows the current
|
|
118
|
-
* slot) the segment is created / re-created sized for this frame. Returns
|
|
119
|
-
* `null` only when the sink has been destroyed.
|
|
120
|
-
*
|
|
121
|
-
* This is the copy-in convenience form (it copies `pixels` into the slot).
|
|
122
|
-
* The decoder's hot path uses the zero-copy {@link beginFrame} /
|
|
123
|
-
* {@link commitFrame} scatter-write pair instead — the scaler produces its
|
|
124
|
-
* packed output directly into the slot, eliminating the write-side memcpy.
|
|
125
|
-
*/
|
|
126
|
-
writeFrame(pixels, meta) {
|
|
127
|
-
if (this.destroyed) return null;
|
|
128
|
-
if (this.writer === null || computeSlotByteLength(meta.width, meta.height, meta.format) > this.slotByteLength) this.recreateSegment(computeSlotByteLength(meta.width, meta.height, meta.format));
|
|
129
|
-
const writer = this.writer;
|
|
130
|
-
if (writer === null) return null;
|
|
131
|
-
const handle = writer.writeFrame(pixels, meta);
|
|
132
|
-
this.framesWritten += 1;
|
|
133
|
-
return handle;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Reserve a ring slot for a frame of the given geometry — the **zero-copy**
|
|
137
|
-
* scatter-write entry point (Phase 5 / D9 Task 7c).
|
|
138
|
-
*
|
|
139
|
-
* The segment is created / re-created here if this is the first frame or the
|
|
140
|
-
* geometry overflows the current slot capacity, so the slot is correctly
|
|
141
|
-
* sized before the caller fills it. The returned `buffer` is a writable view
|
|
142
|
-
* **directly over the mapped segment** — the node-av scaler scatters its
|
|
143
|
-
* packed output straight into it, with no intermediate copy. The caller MUST
|
|
144
|
-
* call {@link commitFrame} with the returned `slot` once the slot is filled.
|
|
145
|
-
*
|
|
146
|
-
* Returns `null` when the sink is destroyed or the segment cannot be created.
|
|
147
|
-
*/
|
|
148
|
-
beginFrame(width, height, format) {
|
|
149
|
-
if (this.destroyed) return null;
|
|
150
|
-
const requiredSlotBytes = computeSlotByteLength(width, height, format);
|
|
151
|
-
if (this.writer === null || requiredSlotBytes > this.slotByteLength) this.recreateSegment(requiredSlotBytes);
|
|
152
|
-
const writer = this.writer;
|
|
153
|
-
if (writer === null) return null;
|
|
154
|
-
const { slot, buffer } = writer.beginFrame();
|
|
155
|
-
return {
|
|
156
|
-
slot,
|
|
157
|
-
buffer
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Publish the frame whose slot was reserved by {@link beginFrame} and filled
|
|
162
|
-
* in place by the caller. `slot` MUST be the value from the matching
|
|
163
|
-
* `beginFrame`. Returns the published `FrameHandle`, or `null` if the sink
|
|
164
|
-
* was destroyed (or the segment lost) between begin and commit.
|
|
165
|
-
*/
|
|
166
|
-
commitFrame(slot, meta) {
|
|
167
|
-
if (this.destroyed) return null;
|
|
168
|
-
const writer = this.writer;
|
|
169
|
-
if (writer === null) return null;
|
|
170
|
-
const handle = writer.commitFrame(slot, meta);
|
|
171
|
-
this.framesWritten += 1;
|
|
172
|
-
return handle;
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Current shm ring usage — `null` until the first frame arms the segment.
|
|
176
|
-
* Surfaced through `decoder.getShmStats` so a downstream consumer can
|
|
177
|
-
* observe ring pressure (slot depth, byte budget, frames written).
|
|
178
|
-
*/
|
|
179
|
-
getShmStats() {
|
|
180
|
-
if (this.writer === null) return null;
|
|
181
|
-
return {
|
|
182
|
-
slotCount: this.writer.slotCount,
|
|
183
|
-
slotByteLength: this.slotByteLength,
|
|
184
|
-
segmentBytes: computeSegmentSize(this.writer.slotCount, this.slotByteLength),
|
|
185
|
-
framesWritten: this.framesWritten
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Abandon a slot reserved by {@link beginFrame} **without publishing it** —
|
|
190
|
-
* the degenerate-path counterpart of {@link commitFrame}.
|
|
191
|
-
*
|
|
192
|
-
* A caller that reserved a slot but then could not produce valid pixels (no
|
|
193
|
-
* decoded source planes, or the scaler threw) MUST call this instead of
|
|
194
|
-
* `commitFrame`: it closes the open seqlock without advancing `writeIndex`,
|
|
195
|
-
* so no reader ever sees the slot's uninitialised bytes as a real frame, and
|
|
196
|
-
* no `FrameHandle` is handed downstream. `slot` MUST be the value from the
|
|
197
|
-
* matching `beginFrame`. A no-op if the sink was destroyed (or the segment
|
|
198
|
-
* lost) between begin and abort.
|
|
199
|
-
*/
|
|
200
|
-
abortFrame(slot) {
|
|
201
|
-
if (this.destroyed) return;
|
|
202
|
-
const writer = this.writer;
|
|
203
|
-
if (writer === null) return;
|
|
204
|
-
writer.abortFrame(slot);
|
|
205
|
-
}
|
|
206
|
-
/** Close + unlink the segment. Idempotent. */
|
|
207
|
-
destroy() {
|
|
208
|
-
if (this.destroyed) return;
|
|
209
|
-
this.destroyed = true;
|
|
210
|
-
this.releaseSegment();
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Create a fresh segment sized for at least `slotByteLength` bytes per slot,
|
|
214
|
-
* replacing any prior one. A re-create bumps the generation so the new
|
|
215
|
-
* segment has a distinct name — a consumer holding the old mapping is never
|
|
216
|
-
* silently handed a resized segment.
|
|
217
|
-
*/
|
|
218
|
-
recreateSegment(slotByteLength) {
|
|
219
|
-
this.releaseSegment();
|
|
220
|
-
this.generation += 1;
|
|
221
|
-
const name = makeSegmentName(this.seed, this.generation);
|
|
222
|
-
const slotCount = deriveSlotCount(RING_BUDGET_BYTES, slotByteLength);
|
|
223
|
-
if (slotCount === MIN_RING_SLOTS && MIN_RING_SLOTS * slotByteLength > RING_BUDGET_BYTES) this.logger.warn("decoder shm ring: budget too small for resolution — using MIN slots", { meta: {
|
|
224
|
-
slotByteLength,
|
|
225
|
-
budgetMb: RING_BUDGET_MB
|
|
226
|
-
} });
|
|
227
|
-
const totalBytes = computeSegmentSize(slotCount, slotByteLength);
|
|
228
|
-
try {
|
|
229
|
-
const segment = createSegment(name, totalBytes);
|
|
230
|
-
this.segment = segment;
|
|
231
|
-
this.segmentName = name;
|
|
232
|
-
this.slotByteLength = slotByteLength;
|
|
233
|
-
this.writer = new FrameRingWriter(segment.buffer, name, slotCount, slotByteLength, this.nodeId);
|
|
234
|
-
this.logger.info("decoder shm ring: segment created", { meta: {
|
|
235
|
-
segment: name,
|
|
236
|
-
slotCount,
|
|
237
|
-
slotByteLength,
|
|
238
|
-
totalBytes,
|
|
239
|
-
generation: this.generation
|
|
240
|
-
} });
|
|
241
|
-
} catch (err) {
|
|
242
|
-
this.segment = null;
|
|
243
|
-
this.writer = null;
|
|
244
|
-
this.segmentName = null;
|
|
245
|
-
this.slotByteLength = 0;
|
|
246
|
-
this.logger.error("decoder shm ring: segment create failed", { meta: {
|
|
247
|
-
segment: name,
|
|
248
|
-
slotByteLength,
|
|
249
|
-
error: err instanceof Error ? err.message : String(err)
|
|
250
|
-
} });
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
/** Unmap + unlink the current segment, if any. */
|
|
254
|
-
releaseSegment() {
|
|
255
|
-
const segment = this.segment;
|
|
256
|
-
if (segment === null) return;
|
|
257
|
-
this.segment = null;
|
|
258
|
-
this.writer = null;
|
|
259
|
-
const name = this.segmentName;
|
|
260
|
-
this.segmentName = null;
|
|
261
|
-
try {
|
|
262
|
-
segment.close();
|
|
263
|
-
segment.unlink();
|
|
264
|
-
this.logger.info("decoder shm ring: segment released", { meta: { segment: name } });
|
|
265
|
-
} catch (err) {
|
|
266
|
-
this.logger.warn("decoder shm ring: segment release failed", { meta: {
|
|
267
|
-
segment: name,
|
|
268
|
-
error: err instanceof Error ? err.message : String(err)
|
|
269
|
-
} });
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
//#endregion
|
|
274
4
|
//#region ../../node_modules/zod/v4/core/core.js
|
|
275
5
|
var _a$1;
|
|
276
6
|
function $constructor(name, initializer, params) {
|
|
@@ -4900,7 +4630,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4900
4630
|
return inst;
|
|
4901
4631
|
}
|
|
4902
4632
|
//#endregion
|
|
4903
|
-
//#region ../types/dist/sleep-
|
|
4633
|
+
//#region ../types/dist/sleep-BC9Yqte7.mjs
|
|
4904
4634
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4905
4635
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4906
4636
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -5086,6 +4816,18 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
5086
4816
|
*/
|
|
5087
4817
|
EventCategory["PipelineCameraUpdated"] = "pipeline.camera-updated";
|
|
5088
4818
|
/**
|
|
4819
|
+
* The cluster camera-source OWNER changed (`clusterRoles.ingestNode`).
|
|
4820
|
+
* Emitted by addon-pipeline-orchestrator whenever it (re)derives node
|
|
4821
|
+
* capabilities — at boot, on agent online/offline, and on an ingest-node
|
|
4822
|
+
* flip. Carries the resolved `ownerNodeId`. The stream-broker consumes it to
|
|
4823
|
+
* keep its ingest-owner-gate decision current WITHOUT a per-`ensureBroker`
|
|
4824
|
+
* cross-process `getIngestOwner` query (push the authority's decision instead
|
|
4825
|
+
* of polling it on the hot path). Idempotent state — re-emitted on every
|
|
4826
|
+
* topology change, so a dropped event self-heals on the next one (plus the
|
|
4827
|
+
* broker's long backstop reconcile query).
|
|
4828
|
+
*/
|
|
4829
|
+
EventCategory["PipelineIngestOwnerChanged"] = "pipeline.ingest-owner-changed";
|
|
4830
|
+
/**
|
|
5089
4831
|
* Periodic snapshot of per-node pipeline-runner load
|
|
5090
4832
|
* (`RunnerLocalLoad`). Emitted ~1Hz by every runner so UI dashboards
|
|
5091
4833
|
* subscribe instead of polling `pipelineRunner.getLocalLoad`.
|
|
@@ -5609,10 +5351,6 @@ function hydrateField(field, values) {
|
|
|
5609
5351
|
};
|
|
5610
5352
|
}
|
|
5611
5353
|
const rawValue = storedValue !== void 0 ? storedValue : defaultValue !== void 0 ? defaultValue : null;
|
|
5612
|
-
if (field.type === "password") return {
|
|
5613
|
-
...field,
|
|
5614
|
-
value: ""
|
|
5615
|
-
};
|
|
5616
5354
|
const value = field.type === "textarea" && field.isJson && rawValue !== null && typeof rawValue === "object" ? JSON.stringify(rawValue, null, 2) : rawValue;
|
|
5617
5355
|
return {
|
|
5618
5356
|
...field,
|
|
@@ -6996,6 +6734,21 @@ function method(input, output, options) {
|
|
|
6996
6734
|
timeoutMs: options?.timeoutMs
|
|
6997
6735
|
};
|
|
6998
6736
|
}
|
|
6737
|
+
/**
|
|
6738
|
+
* A wrapper/system-only method: served exclusively by the cap's system-level
|
|
6739
|
+
* provider (`InferProvider`), and OPTIONAL on `InferNativeProvider` so per-device
|
|
6740
|
+
* driver natives don't stub out a wrapper concern (e.g. a cross-device cache
|
|
6741
|
+
* overview). The `systemOnly: true` literal is what `InferNativeProvider` keys on.
|
|
6742
|
+
*/
|
|
6743
|
+
function systemMethod(input, output, options) {
|
|
6744
|
+
return {
|
|
6745
|
+
...method(input, output, options),
|
|
6746
|
+
systemOnly: true
|
|
6747
|
+
};
|
|
6748
|
+
}
|
|
6749
|
+
var StaticDirOutputSchema$1 = object({ staticDir: string() });
|
|
6750
|
+
var VersionOutputSchema$1 = object({ version: string() });
|
|
6751
|
+
method(_void(), StaticDirOutputSchema$1), method(_void(), VersionOutputSchema$1);
|
|
6999
6752
|
var StaticDirOutputSchema = object({ staticDir: string() });
|
|
7000
6753
|
var VersionOutputSchema = object({ version: string() });
|
|
7001
6754
|
method(_void(), StaticDirOutputSchema), method(_void(), VersionOutputSchema);
|
|
@@ -7177,6 +6930,36 @@ var ModelFormatsSchema = object({
|
|
|
7177
6930
|
tflite: ModelFormatEntrySchema.optional(),
|
|
7178
6931
|
pt: ModelFormatEntrySchema.optional()
|
|
7179
6932
|
});
|
|
6933
|
+
/**
|
|
6934
|
+
* Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
|
|
6935
|
+
* the reduced `PipelineModelOption` returned in `pipeline.getSchema()` so the
|
|
6936
|
+
* grouped Family→Tier→Variant picker renders identically in the config UI and
|
|
6937
|
+
* in the pipeline/device steppers. The flat `id` stays the source of truth for
|
|
6938
|
+
* resolution/download/persistence; this is a presentation overlay resolved back
|
|
6939
|
+
* to an `id`.
|
|
6940
|
+
*/
|
|
6941
|
+
var ModelVariantGroupSchema = object({
|
|
6942
|
+
/** Top-level family, e.g. `yolo26` (later `d-fine`, `rf-detr`). */
|
|
6943
|
+
family: string(),
|
|
6944
|
+
/** Size within the family, e.g. `n` | `s` | `m` | `l`. */
|
|
6945
|
+
tier: string(),
|
|
6946
|
+
/** Quantization axis. Omit ⇒ the fp32 base build. */
|
|
6947
|
+
precision: _enum(["fp32", "int8"]).optional(),
|
|
6948
|
+
/**
|
|
6949
|
+
* Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
|
|
6950
|
+
* latency-optimized export (e.g. ReLU-activation variant) — the slot the
|
|
6951
|
+
* future performance variants plug into.
|
|
6952
|
+
*/
|
|
6953
|
+
optimization: _enum(["standard", "fast"]).optional(),
|
|
6954
|
+
/**
|
|
6955
|
+
* Input-resolution axis (square input side, px). Omit ⇒ the family's native
|
|
6956
|
+
* resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
|
|
6957
|
+
* cheap latency lever — especially on Apple ANE and the Intel N100 — at a
|
|
6958
|
+
* small-object accuracy cost. Mirrors the model's `inputSize` but lifted onto
|
|
6959
|
+
* the group so the selector can offer it as a variant axis.
|
|
6960
|
+
*/
|
|
6961
|
+
resolution: number().int().positive().optional()
|
|
6962
|
+
});
|
|
7180
6963
|
var ModelCatalogEntrySchema = object({
|
|
7181
6964
|
id: string(),
|
|
7182
6965
|
name: string(),
|
|
@@ -7206,7 +6989,43 @@ var ModelCatalogEntrySchema = object({
|
|
|
7206
6989
|
* Auxiliary files required at runtime (labels JSON, charset dict, etc.).
|
|
7207
6990
|
* Downloaded into the same modelsDir alongside the model file.
|
|
7208
6991
|
*/
|
|
7209
|
-
extraFiles: array(ModelExtraFileSchema).readonly().optional()
|
|
6992
|
+
extraFiles: array(ModelExtraFileSchema).readonly().optional(),
|
|
6993
|
+
/**
|
|
6994
|
+
* LEGACY entry — retained in the catalog so a persisted operator selection
|
|
6995
|
+
* still RESOLVES (and can be re-activated), but hidden from the selectable
|
|
6996
|
+
* model list and excluded from the auto format-default pick. Set on the
|
|
6997
|
+
* superseded / consolidated models (older lineages, redundant fp16 IRs) so
|
|
6998
|
+
* the active lineup stays the coherent curated ladder without deleting a
|
|
6999
|
+
* model anyone may still be pinned to. `resolveModelForFormat` keeps honoring
|
|
7000
|
+
* an explicit legacy id that has a build for the node's format.
|
|
7001
|
+
*/
|
|
7002
|
+
legacy: boolean().optional(),
|
|
7003
|
+
/**
|
|
7004
|
+
* Measured quality/latency metadata — populated from the benchmark addon on
|
|
7005
|
+
* the real node classes. Absent = not yet measured (most entries today; the
|
|
7006
|
+
* catalog historically carried only `sizeMB`, a poor cross-architecture
|
|
7007
|
+
* speed proxy). `p95LatencyMs` is keyed by node class (e.g. `n100`, `mac`).
|
|
7008
|
+
*/
|
|
7009
|
+
metrics: object({
|
|
7010
|
+
map50: number().optional(),
|
|
7011
|
+
p95LatencyMs: record(string(), number()).optional()
|
|
7012
|
+
}).optional(),
|
|
7013
|
+
/**
|
|
7014
|
+
* SPDX-ish license id of the model weights (e.g. `AGPL-3.0` for Ultralytics
|
|
7015
|
+
* YOLO26, `GPL-3.0` for YOLOv9, `Apache-2.0` for D-FINE/RF-DETR). Matters for
|
|
7016
|
+
* the retraining addon and any future commercial distribution.
|
|
7017
|
+
*/
|
|
7018
|
+
license: string().optional(),
|
|
7019
|
+
/**
|
|
7020
|
+
* Variant-selector grouping. The UI groups models by `family` + `tier` and
|
|
7021
|
+
* offers `precision` / `optimization` as variant axes WITHIN a tier — so all
|
|
7022
|
+
* of a family's sizes and quantizations collapse into one grouped picker
|
|
7023
|
+
* instead of a flat list of `yolo26s`, `yolo26s-int8`, … Absent ⇒ ungrouped
|
|
7024
|
+
* (legacy / custom models) — never shown in the grouped selector. The flat
|
|
7025
|
+
* `id` stays the source of truth for resolution/download/persistence; grouping
|
|
7026
|
+
* is a presentation overlay resolved back to an `id`.
|
|
7027
|
+
*/
|
|
7028
|
+
group: ModelVariantGroupSchema.optional()
|
|
7210
7029
|
});
|
|
7211
7030
|
var ConvertTargetSchema = discriminatedUnion("format", [object({
|
|
7212
7031
|
format: literal("openvino"),
|
|
@@ -7267,8 +7086,8 @@ var RecordingModeSchema = _enum([
|
|
|
7267
7086
|
"onAudioThreshold"
|
|
7268
7087
|
]);
|
|
7269
7088
|
/**
|
|
7270
|
-
* First-class, authoritative per-camera storage mode — the
|
|
7271
|
-
* reads directly (never inferred from `rules`):
|
|
7089
|
+
* First-class, authoritative per-camera storage mode — the explicit choice the
|
|
7090
|
+
* UI reads directly (never inferred from `rules`):
|
|
7272
7091
|
* - `off` — not recording.
|
|
7273
7092
|
* - `events` — record only around triggers (motion / audio threshold),
|
|
7274
7093
|
* with pre/post-buffer.
|
|
@@ -8916,26 +8735,13 @@ DeviceType.Light, method(object({
|
|
|
8916
8735
|
percentage: number().min(0).max(100),
|
|
8917
8736
|
lastChangedAt: number()
|
|
8918
8737
|
});
|
|
8738
|
+
/** Stream delivery format. (Relocated from the retired `streaming-engine` cap.) */
|
|
8919
8739
|
var StreamFormatSchema = _enum([
|
|
8920
8740
|
"webrtc",
|
|
8921
8741
|
"hls",
|
|
8922
8742
|
"mjpeg",
|
|
8923
8743
|
"rtsp"
|
|
8924
8744
|
]);
|
|
8925
|
-
var StreamInfoSchema = object({
|
|
8926
|
-
streamId: string(),
|
|
8927
|
-
format: StreamFormatSchema,
|
|
8928
|
-
url: string().nullable(),
|
|
8929
|
-
active: boolean()
|
|
8930
|
-
});
|
|
8931
|
-
method(object({
|
|
8932
|
-
streamId: string(),
|
|
8933
|
-
sourceUrl: string(),
|
|
8934
|
-
codec: string().optional()
|
|
8935
|
-
}), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
|
|
8936
|
-
streamId: string(),
|
|
8937
|
-
format: StreamFormatSchema
|
|
8938
|
-
}), string().nullable()), method(_void(), array(StreamInfoSchema));
|
|
8939
8745
|
var RtspRestreamEntrySchema = object({
|
|
8940
8746
|
brokerId: string(),
|
|
8941
8747
|
url: string(),
|
|
@@ -9600,7 +9406,7 @@ var ConsumablesStatusSchema = object({
|
|
|
9600
9406
|
})),
|
|
9601
9407
|
lastChangedAt: number()
|
|
9602
9408
|
});
|
|
9603
|
-
|
|
9409
|
+
Object.values(DeviceType), method(object({
|
|
9604
9410
|
deviceId: number().int().nonnegative(),
|
|
9605
9411
|
key: string().min(1)
|
|
9606
9412
|
}), _void(), {
|
|
@@ -10515,7 +10321,7 @@ var BoundingBoxSchema = object({
|
|
|
10515
10321
|
w: number(),
|
|
10516
10322
|
h: number()
|
|
10517
10323
|
});
|
|
10518
|
-
|
|
10324
|
+
object({
|
|
10519
10325
|
class: string(),
|
|
10520
10326
|
originalClass: string(),
|
|
10521
10327
|
score: number(),
|
|
@@ -10650,7 +10456,6 @@ var PipelineDefaultStepSchema = lazy(() => object({
|
|
|
10650
10456
|
enabled: boolean(),
|
|
10651
10457
|
modelId: string(),
|
|
10652
10458
|
children: array(PipelineDefaultStepSchema).readonly(),
|
|
10653
|
-
engine: PipelineEngineChoiceSchema.optional(),
|
|
10654
10459
|
group: string().optional(),
|
|
10655
10460
|
settings: record(string(), unknown()).optional()
|
|
10656
10461
|
}));
|
|
@@ -10675,7 +10480,9 @@ var PipelineModelOptionSchema = object({
|
|
|
10675
10480
|
formats: record(string(), object({
|
|
10676
10481
|
downloaded: boolean(),
|
|
10677
10482
|
sizeMB: number()
|
|
10678
|
-
}))
|
|
10483
|
+
})),
|
|
10484
|
+
group: ModelVariantGroupSchema.optional(),
|
|
10485
|
+
legacy: boolean().optional()
|
|
10679
10486
|
});
|
|
10680
10487
|
var ConfigFieldBridge = custom();
|
|
10681
10488
|
var PipelineAddonSchemaSchema = object({
|
|
@@ -10689,6 +10496,7 @@ var PipelineAddonSchemaSchema = object({
|
|
|
10689
10496
|
defaultModelId: string(),
|
|
10690
10497
|
defaultModelIdByFormat: record(string(), string()).optional(),
|
|
10691
10498
|
enabledByDefault: boolean().optional(),
|
|
10499
|
+
backfillIntoExistingOverrides: boolean().optional(),
|
|
10692
10500
|
defaultConfidence: number(),
|
|
10693
10501
|
group: string().optional(),
|
|
10694
10502
|
configSchema: array(ConfigFieldBridge).readonly().optional()
|
|
@@ -10705,11 +10513,6 @@ var PipelineSchemaSchema = object({
|
|
|
10705
10513
|
selectedEngine: PipelineEngineChoiceSchema,
|
|
10706
10514
|
slots: array(PipelineSlotSchemaSchema).readonly()
|
|
10707
10515
|
});
|
|
10708
|
-
var DetectorOutputSchema = object({
|
|
10709
|
-
detections: array(SpatialDetectionSchema).readonly(),
|
|
10710
|
-
inferenceMs: number(),
|
|
10711
|
-
modelId: string()
|
|
10712
|
-
});
|
|
10713
10516
|
var EngineProvisioningSchema = object({
|
|
10714
10517
|
runtimeId: _enum([
|
|
10715
10518
|
"onnx",
|
|
@@ -10726,15 +10529,42 @@ var EngineProvisioningSchema = object({
|
|
|
10726
10529
|
]),
|
|
10727
10530
|
progress: number().optional(),
|
|
10728
10531
|
error: string().optional(),
|
|
10729
|
-
nextRetryAt: number().optional()
|
|
10532
|
+
nextRetryAt: number().optional(),
|
|
10533
|
+
/**
|
|
10534
|
+
* Gate A (config-correctness gate at engine change): human-readable
|
|
10535
|
+
* config issues surfaced EAGERLY when the node's engine changes — model
|
|
10536
|
+
* substitutions ("chose X, running Y") and zero-build steps ("no model
|
|
10537
|
+
* has a <format> build"). Additive/optional: informational only, never
|
|
10538
|
+
* enforced here — `assertEngineReady` (readiness) still gates inference.
|
|
10539
|
+
* Absent/empty when the node-default tree resolves cleanly.
|
|
10540
|
+
*/
|
|
10541
|
+
configIssues: array(string()).optional()
|
|
10730
10542
|
});
|
|
10731
10543
|
var PipelineStepInputSchema = lazy(() => object({
|
|
10732
10544
|
addonId: string(),
|
|
10733
|
-
modelId: string(),
|
|
10545
|
+
modelId: string().optional(),
|
|
10734
10546
|
enabled: boolean().default(true),
|
|
10735
10547
|
children: array(PipelineStepInputSchema).optional(),
|
|
10736
10548
|
settings: record(string(), unknown()).optional()
|
|
10737
10549
|
}));
|
|
10550
|
+
var ModelSubstitutionSchema = object({
|
|
10551
|
+
addonId: string(),
|
|
10552
|
+
chosen: string(),
|
|
10553
|
+
running: string(),
|
|
10554
|
+
format: string()
|
|
10555
|
+
});
|
|
10556
|
+
var PipelineValidationIssueSchema = object({
|
|
10557
|
+
addonId: string(),
|
|
10558
|
+
kind: _enum(["unknown-addon", "no-format-build"]),
|
|
10559
|
+
detail: string()
|
|
10560
|
+
});
|
|
10561
|
+
var PipelineValidationResultSchema = object({
|
|
10562
|
+
ok: boolean(),
|
|
10563
|
+
issues: array(PipelineValidationIssueSchema).readonly(),
|
|
10564
|
+
substitutions: array(ModelSubstitutionSchema).readonly(),
|
|
10565
|
+
/** The node's `currentEngine.format` this validation ran against. */
|
|
10566
|
+
format: string()
|
|
10567
|
+
});
|
|
10738
10568
|
var ReferenceImageEntrySchema = object({
|
|
10739
10569
|
filename: string(),
|
|
10740
10570
|
stepIds: array(string()).readonly().optional()
|
|
@@ -10805,7 +10635,13 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
10805
10635
|
})) }), object({ success: literal(true) }), {
|
|
10806
10636
|
kind: "mutation",
|
|
10807
10637
|
auth: "admin"
|
|
10808
|
-
}), method(
|
|
10638
|
+
}), method(object({ nodeId: string() }), object({
|
|
10639
|
+
success: literal(true),
|
|
10640
|
+
clearedDevices: number()
|
|
10641
|
+
}), {
|
|
10642
|
+
kind: "mutation",
|
|
10643
|
+
auth: "admin"
|
|
10644
|
+
}), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({ steps: array(PipelineStepInputSchema) }), PipelineValidationResultSchema), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
|
|
10809
10645
|
name: string(),
|
|
10810
10646
|
steps: array(PipelineTemplateStepSchema).readonly(),
|
|
10811
10647
|
engine: PipelineEngineChoiceSchema
|
|
@@ -10822,10 +10658,6 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
10822
10658
|
modelId: string(),
|
|
10823
10659
|
format: ModelFormatSchema$1
|
|
10824
10660
|
}), object({ success: literal(true) }), { kind: "mutation" }), method(object({
|
|
10825
|
-
addonId: string(),
|
|
10826
|
-
frame: FrameInputSchema,
|
|
10827
|
-
config: record(string(), unknown()).optional()
|
|
10828
|
-
}), DetectorOutputSchema), method(object({
|
|
10829
10661
|
engine: PipelineEngineChoiceSchema.optional(),
|
|
10830
10662
|
steps: array(PipelineStepInputSchema).min(1),
|
|
10831
10663
|
frame: FrameInputSchema.optional(),
|
|
@@ -10971,6 +10803,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(ZoneSchema).read
|
|
|
10971
10803
|
auth: "admin"
|
|
10972
10804
|
}), object({ zones: array(ZoneSchema).readonly() });
|
|
10973
10805
|
/**
|
|
10806
|
+
* A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
|
|
10807
|
+
* decode worker resolves it against the RETAINED native frame's real pixel dims,
|
|
10808
|
+
* so the caller supplies only the detection-res bbox divided by the detection
|
|
10809
|
+
* dims — no native resolution to plumb.
|
|
10810
|
+
*/
|
|
10811
|
+
var NativeCropBboxSchema = object({
|
|
10812
|
+
x: number(),
|
|
10813
|
+
y: number(),
|
|
10814
|
+
w: number(),
|
|
10815
|
+
h: number()
|
|
10816
|
+
});
|
|
10817
|
+
/** Result of a best-effort native-resolution crop (`getNativeCrop`). */
|
|
10818
|
+
var NativeCropResultSchema = object({
|
|
10819
|
+
/** Packed rgb (24-bit) pixels of the crop. */
|
|
10820
|
+
bytes: _instanceof(Uint8Array),
|
|
10821
|
+
width: number().int().positive(),
|
|
10822
|
+
height: number().int().positive()
|
|
10823
|
+
});
|
|
10824
|
+
/**
|
|
10974
10825
|
* Per-camera tunable ranges + defaults. Single source of truth used
|
|
10975
10826
|
* by both the Zod data schema (validation + default fallback) and
|
|
10976
10827
|
* the device settings UI (slider min/max/step). Touch one place and
|
|
@@ -11065,6 +10916,13 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
|
|
|
11065
10916
|
kind: literal("remote-restream"),
|
|
11066
10917
|
/** The camera's source-owner node (slice 1: always the hub). */
|
|
11067
10918
|
ownerNodeId: string(),
|
|
10919
|
+
/**
|
|
10920
|
+
* The owner's LAN-reachable host, resolved by the orchestrator from the
|
|
10921
|
+
* per-node `reachableHost` override (Cluster UI). When present the runner
|
|
10922
|
+
* dials THIS host for the owner's restream, in preference to the
|
|
10923
|
+
* `CAMSTACK_HUB_URL`-derived default. Absent → auto-detect fallback.
|
|
10924
|
+
*/
|
|
10925
|
+
ownerReachableHost: string().optional(),
|
|
11068
10926
|
/** Operator override for the owner host the runner dials. */
|
|
11069
10927
|
hubHostnameOverride: string().optional()
|
|
11070
10928
|
})]).describe("Per-camera frame-source mode for the runner (P2c)");
|
|
@@ -11073,13 +10931,11 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
|
|
|
11073
10931
|
* specific runner instance via `attachCamera`. Carries everything the
|
|
11074
10932
|
* runner needs to subscribe to the local broker and execute inference.
|
|
11075
10933
|
*
|
|
11076
|
-
* Stateless-pipeline model: the
|
|
11077
|
-
*
|
|
11078
|
-
*
|
|
11079
|
-
*
|
|
11080
|
-
*
|
|
11081
|
-
* `engine`/`steps`/`audio` are optional during the additive migration
|
|
11082
|
-
* window; once orchestrator + UI are migrated they become required.
|
|
10934
|
+
* Stateless-pipeline model: the pipeline content (`steps`, optional
|
|
10935
|
+
* `audio`) travels with the attach payload. The runner keeps it in RAM
|
|
10936
|
+
* for the lifetime of the attach — on rebalance, edit, or restart the
|
|
10937
|
+
* orchestrator re-sends the latest snapshot. Engine is NOT carried: it is
|
|
10938
|
+
* node-local, resolved by the executing runner at dispatch time.
|
|
11083
10939
|
*/
|
|
11084
10940
|
var RunnerCameraConfigSchema = object({
|
|
11085
10941
|
deviceId: number(),
|
|
@@ -11130,14 +10986,11 @@ var RunnerCameraConfigSchema = object({
|
|
|
11130
10986
|
*/
|
|
11131
10987
|
motionSources: MotionSourcesSchema.default(["analyzer"]),
|
|
11132
10988
|
pipelineEnabled: boolean().default(true),
|
|
11133
|
-
/** Engine choice for video steps (runtime+backend+format). */
|
|
11134
|
-
engine: PipelineEngineChoiceSchema.optional(),
|
|
11135
10989
|
/** Ordered tree of video steps. Absent → runner skips video detection. */
|
|
11136
10990
|
steps: array(PipelineStepInputSchema).readonly().optional(),
|
|
11137
10991
|
/** Audio classification branch. `enabled:false` disables, null skips. */
|
|
11138
10992
|
audio: object({
|
|
11139
|
-
|
|
11140
|
-
modelId: string(),
|
|
10993
|
+
modelId: string().optional(),
|
|
11141
10994
|
enabled: boolean()
|
|
11142
10995
|
}).nullable().optional(),
|
|
11143
10996
|
/**
|
|
@@ -11224,7 +11077,11 @@ var RunnerLocalMetricsSchema = object({
|
|
|
11224
11077
|
avgInferenceTimeMs: number(),
|
|
11225
11078
|
queueDepth: number()
|
|
11226
11079
|
});
|
|
11227
|
-
method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly())
|
|
11080
|
+
method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly()), method(object({
|
|
11081
|
+
handle: FrameHandleSchema,
|
|
11082
|
+
bbox: NativeCropBboxSchema,
|
|
11083
|
+
maxWidth: number().int().positive().optional()
|
|
11084
|
+
}), NativeCropResultSchema.nullable());
|
|
11228
11085
|
object({
|
|
11229
11086
|
detected: boolean(),
|
|
11230
11087
|
/** Ms epoch of the last detected-true observation. Null if never detected. */
|
|
@@ -12518,7 +12375,9 @@ var AddonPageDeclarationSchema$1 = object({
|
|
|
12518
12375
|
icon: string(),
|
|
12519
12376
|
path: string(),
|
|
12520
12377
|
remoteName: string(),
|
|
12521
|
-
bundle: string()
|
|
12378
|
+
bundle: string(),
|
|
12379
|
+
section: string().optional(),
|
|
12380
|
+
sectionLabel: string().optional()
|
|
12522
12381
|
});
|
|
12523
12382
|
var AddonPageInfoSchema = object({
|
|
12524
12383
|
addonId: string(),
|
|
@@ -12558,7 +12417,18 @@ var AddonPageDeclarationSchema = object({
|
|
|
12558
12417
|
* the static-file route can compute an mtime-based cache-buster URL
|
|
12559
12418
|
* without a separate filesystem stat.
|
|
12560
12419
|
*/
|
|
12561
|
-
bundle: string()
|
|
12420
|
+
bundle: string(),
|
|
12421
|
+
/**
|
|
12422
|
+
* Sidebar section this page docks into. Well-known ids: `'detection'`,
|
|
12423
|
+
* `'cluster'`, `'administration'` — the page renders inside that group.
|
|
12424
|
+
* Any OTHER string creates (or joins) a custom section rendered after
|
|
12425
|
+
* the built-in groups; its label comes from `sectionLabel` (first
|
|
12426
|
+
* declaration wins), falling back to the id. Absent → the legacy
|
|
12427
|
+
* "Addon Pages" group.
|
|
12428
|
+
*/
|
|
12429
|
+
section: string().optional(),
|
|
12430
|
+
/** Display label for a CUSTOM `section` id (ignored for well-known ids). */
|
|
12431
|
+
sectionLabel: string().optional()
|
|
12562
12432
|
});
|
|
12563
12433
|
method(_void(), array(AddonPageDeclarationSchema).readonly());
|
|
12564
12434
|
var AddonHttpRouteSchema = object({
|
|
@@ -12774,6 +12644,17 @@ var WidgetMetadataSchema = object({
|
|
|
12774
12644
|
deviceContext: boolean().default(false),
|
|
12775
12645
|
integrationContext: boolean().default(false)
|
|
12776
12646
|
}),
|
|
12647
|
+
/**
|
|
12648
|
+
* Loadable BEFORE authentication. The normal widget registry listing
|
|
12649
|
+
* (`addon-widgets.listWidgets`) is auth-gated, so a pre-auth surface
|
|
12650
|
+
* (the login page) cannot discover a widget through it. A widget that
|
|
12651
|
+
* declares `preAuth: true` marks itself as safe to mount on a pre-auth
|
|
12652
|
+
* screen — it is surfaced through the PUBLIC `auth.listLoginMethods`
|
|
12653
|
+
* login-method contribution channel (see `login-method.cap.ts`) rather
|
|
12654
|
+
* than the authenticated registry, and its bundle is served by the
|
|
12655
|
+
* public `/api/addon-widgets/:addonId/*` static route. Defaults false.
|
|
12656
|
+
*/
|
|
12657
|
+
preAuth: boolean().optional().default(false),
|
|
12777
12658
|
/** Dashboard placement HINTS (operator can override per instance). */
|
|
12778
12659
|
defaultSize: WidgetSizeEnum.default("md"),
|
|
12779
12660
|
allowedSizes: array(WidgetSizeEnum).readonly().default([
|
|
@@ -13112,6 +12993,66 @@ method(object({
|
|
|
13112
12993
|
password: string()
|
|
13113
12994
|
}), AuthResultSchema.nullable(), { kind: "mutation" }), method(object({ state: string() }), string()), method(record(string(), string()), AuthResultSchema, { kind: "mutation" }), method(object({ token: string() }), AuthResultSchema.nullable());
|
|
13114
12995
|
/**
|
|
12996
|
+
* `login-method` — collection cap through which auth addons contribute
|
|
12997
|
+
* their pre-auth login surfaces to the login page. This is the SINGLE,
|
|
12998
|
+
* generic mechanism that supersedes the dead `auth.listProviders` reader:
|
|
12999
|
+
* every auth addon (OIDC, magic-link, WebAuthn/passkey) registers a
|
|
13000
|
+
* `login-method` provider and the PUBLIC `auth.listLoginMethods`
|
|
13001
|
+
* procedure aggregates them for the unauthenticated login page.
|
|
13002
|
+
*
|
|
13003
|
+
* A contribution is a discriminated union on `kind`:
|
|
13004
|
+
*
|
|
13005
|
+
* - `redirect` — a declarative button. The login page renders a generic
|
|
13006
|
+
* button that navigates to `startUrl` (an addon-owned HTTP route).
|
|
13007
|
+
* Covers OIDC (`/addon/auth-oidc/<id>/start`) and magic-link with
|
|
13008
|
+
* ZERO shell-side JS. A future SSO addon plugs in the same way — the
|
|
13009
|
+
* login page needs NO change.
|
|
13010
|
+
*
|
|
13011
|
+
* - `widget` — a Module-Federation widget the login page mounts (via
|
|
13012
|
+
* `loadRemoteBundle`) for an in-page ceremony. Covers the passkey
|
|
13013
|
+
* login ceremony, which must run `@simplewebauthn/browser` INSIDE the
|
|
13014
|
+
* addon bundle. The referenced widget also declares `preAuth: true` in
|
|
13015
|
+
* its `addon-widgets-source` catalog entry. `auth.listLoginMethods`
|
|
13016
|
+
* stamps a public `bundleUrl` from `addonId` + `bundle`.
|
|
13017
|
+
*
|
|
13018
|
+
* Every contribution carries a `stage`:
|
|
13019
|
+
* - `primary` — shown on the first credentials screen (OIDC /
|
|
13020
|
+
* magic-link buttons; a future usernameless passkey).
|
|
13021
|
+
* - `second-factor` — shown AFTER the password leg, gated on the
|
|
13022
|
+
* returned `factors` (passkey-as-2FA today).
|
|
13023
|
+
*
|
|
13024
|
+
* `mount: skip` — the cap is read server-side by the core auth router
|
|
13025
|
+
* (`registry.getCollection('login-method')`), never mounted as its own
|
|
13026
|
+
* tRPC router.
|
|
13027
|
+
*/
|
|
13028
|
+
/** When a login method renders in the two-phase login flow. */
|
|
13029
|
+
var LoginStageEnum = _enum(["primary", "second-factor"]);
|
|
13030
|
+
/** One login-method contribution — redirect button OR pre-auth widget. */
|
|
13031
|
+
var LoginMethodContributionSchema = discriminatedUnion("kind", [object({
|
|
13032
|
+
kind: literal("redirect"),
|
|
13033
|
+
/** Stable id within the login-method set (e.g. `auth-oidc/google`). */
|
|
13034
|
+
id: string(),
|
|
13035
|
+
/** Operator-facing button label. */
|
|
13036
|
+
label: string(),
|
|
13037
|
+
/** lucide-react icon name. */
|
|
13038
|
+
icon: string().optional(),
|
|
13039
|
+
/** Addon-owned HTTP route the button navigates to (GET). */
|
|
13040
|
+
startUrl: string(),
|
|
13041
|
+
stage: LoginStageEnum
|
|
13042
|
+
}), object({
|
|
13043
|
+
kind: literal("widget"),
|
|
13044
|
+
/** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
|
|
13045
|
+
id: string(),
|
|
13046
|
+
/** Owning addon id — drives the public bundle URL + the MF namespace. */
|
|
13047
|
+
addonId: string(),
|
|
13048
|
+
/** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
|
|
13049
|
+
bundle: string(),
|
|
13050
|
+
/** MF remote descriptor — `{ remoteName, exposedModule, componentKey }`. */
|
|
13051
|
+
remote: WidgetRemoteSchema,
|
|
13052
|
+
stage: LoginStageEnum
|
|
13053
|
+
})]);
|
|
13054
|
+
method(_void(), array(LoginMethodContributionSchema).readonly());
|
|
13055
|
+
/**
|
|
13115
13056
|
* Orchestrator-side destination metadata. The orchestrator computes
|
|
13116
13057
|
* `id = <addonId>:<subId>` from its provider lookup so consumers
|
|
13117
13058
|
* (admin UI, restore flow) see one canonical key.
|
|
@@ -15308,7 +15249,17 @@ var TrackSchema = object({
|
|
|
15308
15249
|
/** Cumulative normalized distance travelled (0..1 units = full frame width). */
|
|
15309
15250
|
totalDistance: number(),
|
|
15310
15251
|
state: TrackStateSchema,
|
|
15311
|
-
active: boolean()
|
|
15252
|
+
active: boolean(),
|
|
15253
|
+
/** Deterministic key-event importance score in [0,1] (server-computed at
|
|
15254
|
+
* track expiry, recomputed on late label). Absent on legacy rows written
|
|
15255
|
+
* before scoring shipped — consumers degrade to absence / compute-on-read. */
|
|
15256
|
+
importance: number().optional(),
|
|
15257
|
+
/** Id of the track's highest-confidence ObjectEvent (its representative
|
|
15258
|
+
* "best" frame). Absent when the track produced no object events. */
|
|
15259
|
+
bestEventId: string().optional(),
|
|
15260
|
+
/** Tag of the importance sub-signal that dominated the score
|
|
15261
|
+
* (identity|dwell|proximity|class|confidence|travel|zone). */
|
|
15262
|
+
importanceReason: string().optional()
|
|
15312
15263
|
});
|
|
15313
15264
|
var BaseEventFields = {
|
|
15314
15265
|
id: string(),
|
|
@@ -15373,8 +15324,18 @@ var ObjectEventSchema = object({
|
|
|
15373
15324
|
frameHeight: number().optional(),
|
|
15374
15325
|
/** MediaStore key for the crop attached to this event (if any). */
|
|
15375
15326
|
mediaKey: string().optional(),
|
|
15327
|
+
/** Design B: MediaStore key of the track's native-resolution key frame (the
|
|
15328
|
+
* best-detection full frame). Resolve via the event-media data-plane
|
|
15329
|
+
* (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
|
|
15330
|
+
* draws `bbox` over the native frame. Absent on legacy rows / non-decoded
|
|
15331
|
+
* sources — consumers fall back to `mediaKey` (the tight crop). */
|
|
15332
|
+
keyFrameMediaKey: string().optional(),
|
|
15376
15333
|
/** Populated by B5 (recording playback URL for this event). */
|
|
15377
|
-
mediaUrl: string().optional()
|
|
15334
|
+
mediaUrl: string().optional(),
|
|
15335
|
+
/** The parent track's key-event importance [0,1], propagated to every object
|
|
15336
|
+
* event of the track (so an event row can be sorted by importance without a
|
|
15337
|
+
* track join). Absent on legacy rows / before the track was scored. */
|
|
15338
|
+
importance: number().optional()
|
|
15378
15339
|
});
|
|
15379
15340
|
var AudioEventSchema = object({
|
|
15380
15341
|
...BaseEventFields,
|
|
@@ -15398,7 +15359,8 @@ var MediaFileKindEnum = _enum([
|
|
|
15398
15359
|
"fullFrame",
|
|
15399
15360
|
"fullFrameBoxed",
|
|
15400
15361
|
"faceCrop",
|
|
15401
|
-
"plateCrop"
|
|
15362
|
+
"plateCrop",
|
|
15363
|
+
"keyFrame"
|
|
15402
15364
|
]);
|
|
15403
15365
|
var MediaFileSchema = object({
|
|
15404
15366
|
key: string(),
|
|
@@ -15419,6 +15381,32 @@ var DeviceEventQueryInput = object({
|
|
|
15419
15381
|
projection: _enum(["full", "slim"]).optional()
|
|
15420
15382
|
});
|
|
15421
15383
|
var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
|
|
15384
|
+
var KeyEventQueryInput = object({
|
|
15385
|
+
deviceId: number(),
|
|
15386
|
+
/** Window lower bound (track firstSeen ≥ since). */
|
|
15387
|
+
since: number(),
|
|
15388
|
+
/** Window upper bound (track firstSeen ≤ until). */
|
|
15389
|
+
until: number(),
|
|
15390
|
+
limit: number().int().min(1).max(200).default(50),
|
|
15391
|
+
/** Drop tracks scoring below this importance. */
|
|
15392
|
+
minImportance: number().min(0).max(1).optional(),
|
|
15393
|
+
/** Restrict to a single class (e.g. 'person'). */
|
|
15394
|
+
classFilter: string().optional()
|
|
15395
|
+
});
|
|
15396
|
+
var KeyEventSchema = object({
|
|
15397
|
+
/** The representative event id (the track's best ObjectEvent, else its trackId). */
|
|
15398
|
+
id: string(),
|
|
15399
|
+
trackId: string(),
|
|
15400
|
+
/** Track start time (firstSeen). */
|
|
15401
|
+
timestamp: number(),
|
|
15402
|
+
className: string(),
|
|
15403
|
+
label: string().optional(),
|
|
15404
|
+
importance: number(),
|
|
15405
|
+
/** Highest-confidence ObjectEvent id for the track (empty when none). */
|
|
15406
|
+
bestEventId: string(),
|
|
15407
|
+
/** Track lifetime in ms (lastSeen - firstSeen). */
|
|
15408
|
+
windowMs: number().optional()
|
|
15409
|
+
});
|
|
15422
15410
|
var TrackedDetectionSchema = object({
|
|
15423
15411
|
trackId: string(),
|
|
15424
15412
|
className: string(),
|
|
@@ -15448,7 +15436,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
15448
15436
|
}), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
|
|
15449
15437
|
kind: "mutation",
|
|
15450
15438
|
auth: "admin"
|
|
15451
|
-
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({
|
|
15439
|
+
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
|
|
15452
15440
|
deviceId: number(),
|
|
15453
15441
|
since: number(),
|
|
15454
15442
|
until: number(),
|
|
@@ -15493,11 +15481,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
15493
15481
|
timestamp: number()
|
|
15494
15482
|
});
|
|
15495
15483
|
var CameraPipelineConfigSchema = object({
|
|
15496
|
-
engine: PipelineEngineChoiceSchema,
|
|
15484
|
+
engine: PipelineEngineChoiceSchema.optional(),
|
|
15497
15485
|
steps: array(PipelineStepInputSchema).readonly(),
|
|
15498
15486
|
audio: object({
|
|
15499
|
-
engine: PipelineEngineChoiceSchema,
|
|
15500
|
-
modelId: string(),
|
|
15487
|
+
engine: PipelineEngineChoiceSchema.optional(),
|
|
15488
|
+
modelId: string().optional(),
|
|
15501
15489
|
enabled: boolean(),
|
|
15502
15490
|
settings: record(string(), unknown()).readonly().optional()
|
|
15503
15491
|
}).nullable().optional()
|
|
@@ -15512,7 +15500,7 @@ var PipelineTemplateSchema = object({
|
|
|
15512
15500
|
});
|
|
15513
15501
|
var AgentAddonConfigSchema = object({
|
|
15514
15502
|
enabled: boolean(),
|
|
15515
|
-
modelId: string(),
|
|
15503
|
+
modelId: string().optional(),
|
|
15516
15504
|
settings: record(string(), unknown()).readonly()
|
|
15517
15505
|
});
|
|
15518
15506
|
var AgentPipelineSettingsSchema = object({
|
|
@@ -15522,12 +15510,25 @@ var AgentPipelineSettingsSchema = object({
|
|
|
15522
15510
|
detectWeight: number().positive().optional(),
|
|
15523
15511
|
/** Node is eligible to run the detection pipeline (decode + inference). */
|
|
15524
15512
|
detect: boolean().optional(),
|
|
15525
|
-
/**
|
|
15513
|
+
/**
|
|
15514
|
+
* DEPRECATED AND IGNORED. Decode is always co-located with its frame
|
|
15515
|
+
* consumer, so decode eligibility IS detect eligibility. Kept optional in
|
|
15516
|
+
* the schema ONLY so persisted stores written before the removal still
|
|
15517
|
+
* parse — no code reads it and no write path emits it.
|
|
15518
|
+
*/
|
|
15526
15519
|
decode: boolean().optional(),
|
|
15527
15520
|
/** Node is eligible to run audio-analyzer sessions. */
|
|
15528
15521
|
audio: boolean().optional(),
|
|
15529
15522
|
/** Node is eligible to be the ingest / source-owner (serve the restream). */
|
|
15530
|
-
ingest: boolean().optional()
|
|
15523
|
+
ingest: boolean().optional(),
|
|
15524
|
+
/**
|
|
15525
|
+
* Operator override for the LAN host a cross-node decoder dials to reach
|
|
15526
|
+
* THIS node's restream (Cluster UI). Absent → auto-detect: a remote runner
|
|
15527
|
+
* falls back to its `CAMSTACK_HUB_URL`-derived host (the Moleculer address
|
|
15528
|
+
* it already uses to reach the hub). Set this only when the auto-detected
|
|
15529
|
+
* address is wrong (multi-homed host, NAT, custom interface).
|
|
15530
|
+
*/
|
|
15531
|
+
reachableHost: string().optional()
|
|
15531
15532
|
});
|
|
15532
15533
|
var CameraPipelineForAgentSchema = object({
|
|
15533
15534
|
steps: array(PipelineStepInputSchema).readonly(),
|
|
@@ -15575,25 +15576,6 @@ var PipelineAssignmentSchema = object({
|
|
|
15575
15576
|
assignedAt: number()
|
|
15576
15577
|
});
|
|
15577
15578
|
/**
|
|
15578
|
-
* Decoder placement record. Symmetric to `PipelineAssignmentSchema` but for
|
|
15579
|
-
* the decoder-node placement domain (`balanceDecoder` decision: manual pin
|
|
15580
|
-
* → co-located with pipeline → capacity).
|
|
15581
|
-
*/
|
|
15582
|
-
var DecoderAssignmentSchema = object({
|
|
15583
|
-
deviceId: number(),
|
|
15584
|
-
/** Moleculer node id of the decoder provider currently responsible for this camera. */
|
|
15585
|
-
decoderNodeId: string(),
|
|
15586
|
-
/** True when the assignment was set manually via `assignDecoder`, false when chosen by the balancer. */
|
|
15587
|
-
pinned: boolean(),
|
|
15588
|
-
/** Why this assignment was made — useful for debugging the decoder balancer. */
|
|
15589
|
-
reason: _enum([
|
|
15590
|
-
"manual",
|
|
15591
|
-
"co-located",
|
|
15592
|
-
"capacity",
|
|
15593
|
-
"hardware-affinity"
|
|
15594
|
-
])
|
|
15595
|
-
});
|
|
15596
|
-
/**
|
|
15597
15579
|
* Per-agent load summary surfaced to the load balancer + dashboards.
|
|
15598
15580
|
* Aggregated from each runner's `getLocalLoad` cap call.
|
|
15599
15581
|
*/
|
|
@@ -15633,6 +15615,15 @@ var GlobalMetricsSchema = object({
|
|
|
15633
15615
|
* capability providers.
|
|
15634
15616
|
*/
|
|
15635
15617
|
var CapabilityBindingsSchema = record(string(), string());
|
|
15618
|
+
/**
|
|
15619
|
+
* The cluster's single camera-source owner (`clusterRoles.ingestNode`) plus
|
|
15620
|
+
* its LAN-reachable host, if one is registered. See `getIngestOwner`.
|
|
15621
|
+
*/
|
|
15622
|
+
var IngestOwnerSchema = object({
|
|
15623
|
+
ownerNodeId: string(),
|
|
15624
|
+
reachableHost: string().optional(),
|
|
15625
|
+
configIssue: string().optional()
|
|
15626
|
+
});
|
|
15636
15627
|
/** Source block — always present; derives from the stream catalog. */
|
|
15637
15628
|
var CameraSourceStatusSchema = object({ streams: array(object({
|
|
15638
15629
|
camStreamId: string(),
|
|
@@ -15647,6 +15638,14 @@ var CameraAssignmentStatusSchema = object({
|
|
|
15647
15638
|
detectionNodeId: string().nullable(),
|
|
15648
15639
|
decoderNodeId: string().nullable(),
|
|
15649
15640
|
audioNodeId: string().nullable(),
|
|
15641
|
+
/**
|
|
15642
|
+
* The node that OWNS this camera's physical source pull (dials the RTSP and
|
|
15643
|
+
* hosts the broker/restream) — the cluster ingest owner today
|
|
15644
|
+
* (`clusterRoles.ingestNode`), per-camera once source assignment lands. Lets
|
|
15645
|
+
* the UI show WHERE a camera is sourced without SSH/logs, and is the node the
|
|
15646
|
+
* broker block below was read from (pinned). Nullable only pre-wiring.
|
|
15647
|
+
*/
|
|
15648
|
+
sourceNodeId: string().nullable(),
|
|
15650
15649
|
pinned: object({
|
|
15651
15650
|
detection: boolean(),
|
|
15652
15651
|
decoder: boolean(),
|
|
@@ -15779,16 +15778,7 @@ method(object({
|
|
|
15779
15778
|
}), object({ success: literal(true) }), {
|
|
15780
15779
|
kind: "mutation",
|
|
15781
15780
|
auth: "admin"
|
|
15782
|
-
}), method(object({
|
|
15783
|
-
deviceId: number(),
|
|
15784
|
-
nodeId: string()
|
|
15785
|
-
}), _void(), {
|
|
15786
|
-
kind: "mutation",
|
|
15787
|
-
auth: "admin"
|
|
15788
|
-
}), method(object({ deviceId: number() }), _void(), {
|
|
15789
|
-
kind: "mutation",
|
|
15790
|
-
auth: "admin"
|
|
15791
|
-
}), method(_void(), array(DecoderAssignmentSchema).readonly()), method(object({
|
|
15781
|
+
}), method(_void(), IngestOwnerSchema), method(object({
|
|
15792
15782
|
deviceId: number(),
|
|
15793
15783
|
nodeId: string()
|
|
15794
15784
|
}), object({ success: literal(true) }), {
|
|
@@ -15809,10 +15799,7 @@ method(object({
|
|
|
15809
15799
|
nodeId: string(),
|
|
15810
15800
|
pinned: boolean(),
|
|
15811
15801
|
assignedAt: number()
|
|
15812
|
-
}))), method(object({
|
|
15813
|
-
deviceId: number(),
|
|
15814
|
-
pipelineNodeId: string().optional()
|
|
15815
|
-
}), DecoderAssignmentSchema), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
|
|
15802
|
+
}))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
|
|
15816
15803
|
nodeId: string(),
|
|
15817
15804
|
settings: AgentPipelineSettingsSchema
|
|
15818
15805
|
})).readonly()), method(object({
|
|
@@ -15842,12 +15829,26 @@ method(object({
|
|
|
15842
15829
|
}), method(object({
|
|
15843
15830
|
agentNodeId: string(),
|
|
15844
15831
|
detect: boolean().nullable().optional(),
|
|
15845
|
-
decode: boolean().nullable().optional(),
|
|
15846
15832
|
audio: boolean().nullable().optional(),
|
|
15847
15833
|
ingest: boolean().nullable().optional()
|
|
15848
15834
|
}), object({ success: literal(true) }), {
|
|
15849
15835
|
kind: "mutation",
|
|
15850
15836
|
auth: "admin"
|
|
15837
|
+
}), method(object({
|
|
15838
|
+
agentNodeId: string(),
|
|
15839
|
+
reachableHost: string().nullable()
|
|
15840
|
+
}), object({ success: literal(true) }), {
|
|
15841
|
+
kind: "mutation",
|
|
15842
|
+
auth: "admin"
|
|
15843
|
+
}), method(object({ agentNodeId: string() }), object({
|
|
15844
|
+
success: literal(true),
|
|
15845
|
+
/** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
|
|
15846
|
+
effectiveModelId: string().nullable(),
|
|
15847
|
+
/** Number of cameras whose node-scoped overrides were cleared. */
|
|
15848
|
+
clearedCameraOverrides: number()
|
|
15849
|
+
}), {
|
|
15850
|
+
kind: "mutation",
|
|
15851
|
+
auth: "admin"
|
|
15851
15852
|
}), method(object({ deviceId: number() }), CameraPipelineSettingsSchema.nullable()), method(object({
|
|
15852
15853
|
deviceId: number(),
|
|
15853
15854
|
addonId: string(),
|
|
@@ -15892,22 +15893,131 @@ method(object({
|
|
|
15892
15893
|
kind: "mutation",
|
|
15893
15894
|
auth: "admin"
|
|
15894
15895
|
});
|
|
15895
|
-
|
|
15896
|
-
|
|
15897
|
-
|
|
15898
|
-
|
|
15899
|
-
|
|
15900
|
-
|
|
15896
|
+
/**
|
|
15897
|
+
* server-management — per-NODE singleton capability for a node's ROOT
|
|
15898
|
+
* package lifecycle (runtime-updatable node packages, phase 2: hub + docker
|
|
15899
|
+
* agents).
|
|
15900
|
+
*
|
|
15901
|
+
* Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
|
|
15902
|
+
* on agents) carries the whole software stack in its npm dep tree, so ONE
|
|
15903
|
+
* version describes the node. Updates install into
|
|
15904
|
+
* `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
|
|
15905
|
+
* starter (probation boot + auto-rollback to N-1).
|
|
15906
|
+
*
|
|
15907
|
+
* Providers:
|
|
15908
|
+
* - HUB: `ServerUpdateService` behind the `server-provided` mount
|
|
15909
|
+
* (`buildServerProviders` in trpc.router.ts) — the default target for
|
|
15910
|
+
* unpinned calls.
|
|
15911
|
+
* - AGENT: `AgentUpdateService` registered by the agent bootstrap under
|
|
15912
|
+
* the synthetic `agent-runtime` addonId and declared in the agent's
|
|
15913
|
+
* `$hub.registerNode` manifest.
|
|
15914
|
+
*
|
|
15915
|
+
* Node routing: singleton caps get the codegen/runtime-builder `nodeId`
|
|
15916
|
+
* injection on every method — `input.nodeId` (or `nodePin(nodeId)` from the
|
|
15917
|
+
* SDK) routes the call to that node's provider via the standard remote
|
|
15918
|
+
* proxy (`createCapabilityProxy` → `$agent-cap-fwd` → the agent's
|
|
15919
|
+
* in-process provider lookup). No `nodeId` → the hub's own provider.
|
|
15920
|
+
*
|
|
15921
|
+
* Spec: docs/superpowers/specs/2026-07-12-runtime-updatable-node-packages-design.md
|
|
15922
|
+
*/
|
|
15923
|
+
/**
|
|
15924
|
+
* Where the running hub's code was loaded from:
|
|
15925
|
+
* - `workspace` — dev checkout (tsx / workspace dist); the starter defers to
|
|
15926
|
+
* plain resolution and runtime updates are refused.
|
|
15927
|
+
* - `baked` — the immutable image seed closure (no data-dir root active).
|
|
15928
|
+
* - `data-root` — the runtime-updatable `<dataDir>/server-root` closure.
|
|
15929
|
+
*/
|
|
15930
|
+
var ServerBootModeSchema = _enum([
|
|
15931
|
+
"workspace",
|
|
15932
|
+
"baked",
|
|
15933
|
+
"data-root"
|
|
15934
|
+
]);
|
|
15935
|
+
/**
|
|
15936
|
+
* Update lifecycle state:
|
|
15937
|
+
* - `idle` / `checking` / `staging` — steady / in-flight registry work.
|
|
15938
|
+
* - `pending-restart` — a version is staged and the node has NOT yet
|
|
15939
|
+
* restarted onto it (still running the OLD version).
|
|
15940
|
+
* - `awaiting-confirmation` — the node HAS restarted onto the staged version
|
|
15941
|
+
* (it is the active probation boot) and is waiting to confirm boot-health.
|
|
15942
|
+
* Apply/rollback are refused in this state and the node must NOT be
|
|
15943
|
+
* manually restarted, or the probation boot auto-rolls-back.
|
|
15944
|
+
*/
|
|
15945
|
+
var ServerUpdateStateSchema = _enum([
|
|
15946
|
+
"idle",
|
|
15947
|
+
"checking",
|
|
15948
|
+
"staging",
|
|
15949
|
+
"pending-restart",
|
|
15950
|
+
"awaiting-confirmation"
|
|
15951
|
+
]);
|
|
15952
|
+
var ServerRollbackInfoSchema = object({
|
|
15953
|
+
/** The version that failed (or was manually rolled back). */
|
|
15954
|
+
fromVersion: string(),
|
|
15955
|
+
/** The version rolled back to; null = the baked seed. */
|
|
15956
|
+
toVersion: string().nullable(),
|
|
15957
|
+
atMs: number(),
|
|
15958
|
+
reason: string()
|
|
15901
15959
|
});
|
|
15902
|
-
var
|
|
15903
|
-
|
|
15904
|
-
|
|
15905
|
-
|
|
15960
|
+
var ServerPackageStatusSchema = object({
|
|
15961
|
+
/** Root package name (`@camstack/server` on the hub). */
|
|
15962
|
+
packageName: string(),
|
|
15963
|
+
/** Version of the code the running process ACTUALLY loaded. */
|
|
15964
|
+
runningVersion: string().nullable(),
|
|
15965
|
+
/** Node.js runtime version the node's process runs on (`process.versions.node`). */
|
|
15966
|
+
nodeRuntimeVersion: string().nullable(),
|
|
15967
|
+
/** Active data-dir root version; null when booted from seed/workspace. */
|
|
15968
|
+
activeVersion: string().nullable(),
|
|
15969
|
+
/** N-1 version kept for rollback; null when no previous version exists. */
|
|
15970
|
+
previousVersion: string().nullable(),
|
|
15971
|
+
/** Version of the immutable baked seed closure (image fallback). */
|
|
15972
|
+
seedVersion: string().nullable(),
|
|
15973
|
+
/** Latest registry version from the most recent check (null = never checked). */
|
|
15974
|
+
latestVersion: string().nullable(),
|
|
15975
|
+
updateAvailable: boolean(),
|
|
15976
|
+
bootMode: ServerBootModeSchema,
|
|
15977
|
+
updateState: ServerUpdateStateSchema,
|
|
15978
|
+
/** Version staged + awaiting its probation boot, when one is pending. */
|
|
15979
|
+
pendingVersion: string().nullable(),
|
|
15980
|
+
/** Set when the last freshly-activated version failed its boot health-check. */
|
|
15981
|
+
rolledBack: ServerRollbackInfoSchema.nullable(),
|
|
15982
|
+
/**
|
|
15983
|
+
* True when `server-root/state.json` EXISTS but is unreadable/corrupt — the
|
|
15984
|
+
* hub is running from the baked seed (or workspace) while installed data-dir
|
|
15985
|
+
* versions are being IGNORED. Surfaced as a warning in the UI.
|
|
15986
|
+
*/
|
|
15987
|
+
stateFileCorrupt: boolean(),
|
|
15988
|
+
lastCheckedAtMs: number().nullable()
|
|
15989
|
+
});
|
|
15990
|
+
var ServerUpdateCheckResultSchema = object({
|
|
15991
|
+
packageName: string(),
|
|
15992
|
+
runningVersion: string().nullable(),
|
|
15993
|
+
latestVersion: string().nullable(),
|
|
15994
|
+
updateAvailable: boolean(),
|
|
15995
|
+
checkedAtMs: number(),
|
|
15996
|
+
/** Non-null when the registry lookup failed (offline, bad registry, …). */
|
|
15997
|
+
error: string().nullable()
|
|
15998
|
+
});
|
|
15999
|
+
var ServerUpdateActionResultSchema = object({
|
|
16000
|
+
accepted: boolean(),
|
|
16001
|
+
targetVersion: string().nullable(),
|
|
16002
|
+
/** True when a graceful restart was scheduled to apply the change. */
|
|
16003
|
+
restarting: boolean(),
|
|
16004
|
+
message: string()
|
|
16005
|
+
});
|
|
16006
|
+
method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
|
|
16007
|
+
kind: "mutation",
|
|
16008
|
+
auth: "admin"
|
|
16009
|
+
}), method(object({
|
|
16010
|
+
/** Explicit target version; omitted = latest from the registry. */
|
|
16011
|
+
version: string().optional() }), ServerUpdateActionResultSchema, {
|
|
16012
|
+
kind: "mutation",
|
|
16013
|
+
auth: "admin"
|
|
16014
|
+
}), method(_void(), ServerUpdateActionResultSchema, {
|
|
16015
|
+
kind: "mutation",
|
|
16016
|
+
auth: "admin"
|
|
16017
|
+
}), method(_void(), ServerUpdateActionResultSchema, {
|
|
16018
|
+
kind: "mutation",
|
|
16019
|
+
auth: "admin"
|
|
15906
16020
|
});
|
|
15907
|
-
method(object({
|
|
15908
|
-
deviceId: number(),
|
|
15909
|
-
streams: array(RegisteredStreamSchema).readonly()
|
|
15910
|
-
}), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), array(ExposedResourceSchema).readonly());
|
|
15911
16021
|
/**
|
|
15912
16022
|
* Query filter for settings-store collections.
|
|
15913
16023
|
*/
|
|
@@ -16060,9 +16170,9 @@ method(SendEmailInputSchema, SendEmailResultSchema, {
|
|
|
16060
16170
|
/**
|
|
16061
16171
|
* A single device snapshot returned as base64 JPEG/PNG.
|
|
16062
16172
|
*
|
|
16063
|
-
*
|
|
16064
|
-
*
|
|
16065
|
-
*
|
|
16173
|
+
* The `SnapshotAddon` wrapper returns this shape whether the frame came from
|
|
16174
|
+
* the device-native provider (onboard capture) or from the stream-broker
|
|
16175
|
+
* prebuffer fallback.
|
|
16066
16176
|
*/
|
|
16067
16177
|
var SnapshotImageSchema = object({
|
|
16068
16178
|
base64: string(),
|
|
@@ -16093,11 +16203,12 @@ DeviceType.Camera, method(object({
|
|
|
16093
16203
|
}), SnapshotImageSchema.nullable()), method(object({ deviceId: number() }), _void(), {
|
|
16094
16204
|
kind: "mutation",
|
|
16095
16205
|
auth: "admin"
|
|
16096
|
-
})
|
|
16097
|
-
method(object({ deviceId: number() }), boolean()), method(object({
|
|
16206
|
+
}), systemMethod(object({ deviceIds: array(number()).min(1).max(200) }), array(object({
|
|
16098
16207
|
deviceId: number(),
|
|
16099
|
-
|
|
16100
|
-
|
|
16208
|
+
lastCapturedAt: number().nullable(),
|
|
16209
|
+
cacheAgeMs: number().nullable(),
|
|
16210
|
+
etag: string().nullable()
|
|
16211
|
+
})));
|
|
16101
16212
|
/**
|
|
16102
16213
|
* `sso-bridge` — internal hub-only cap that lets SSO-style auth
|
|
16103
16214
|
* providers (OIDC, SAML, magic-link, …) mint an HMAC-signed token
|
|
@@ -16348,10 +16459,32 @@ method(_void(), array(TurnServerSchema).readonly());
|
|
|
16348
16459
|
* b. `finishAuthentication({userId, response})` → server verifies
|
|
16349
16460
|
* the assertion, bumps the credential counter, returns ok.
|
|
16350
16461
|
*
|
|
16462
|
+
* 2b. Usernameless (discoverable-credential) authentication — the
|
|
16463
|
+
* passkey IS the primary factor, no password leg:
|
|
16464
|
+
* a. `beginDiscoverableAuthentication({})` → assertion options with
|
|
16465
|
+
* EMPTY `allowCredentials` (the browser offers every resident
|
|
16466
|
+
* passkey it holds for this RP) + `userVerification: 'required'`
|
|
16467
|
+
* (the passkey replaces both factors, so UV is mandatory).
|
|
16468
|
+
* The challenge is stored server-side, NOT bound to any user.
|
|
16469
|
+
* b. `finishDiscoverableAuthentication({response})` → the provider
|
|
16470
|
+
* resolves the credential by the response's credential id,
|
|
16471
|
+
* verifies the assertion against the stored challenge + that
|
|
16472
|
+
* credential's public key/counter, and returns the OWNING
|
|
16473
|
+
* `userId` — the caller (core auth router) mints the session.
|
|
16474
|
+
*
|
|
16351
16475
|
* 3. Management:
|
|
16352
16476
|
* - `listPasskeys({userId})` — enumerate user's enrolled credentials.
|
|
16353
16477
|
* - `removePasskey({userId, credentialId})` — revoke one credential.
|
|
16354
16478
|
*
|
|
16479
|
+
* 4. Second-factor preference (opt-in, default OFF):
|
|
16480
|
+
* Enrolling a passkey only enables passkey-FIRST sign-in. It is
|
|
16481
|
+
* demanded as a second factor after a password login ONLY when the
|
|
16482
|
+
* user explicitly opts in via `setSecondFactorPreference`.
|
|
16483
|
+
* - `getSecondFactorPreference({userId})` → `{ enabled }` (missing
|
|
16484
|
+
* row ⇒ `enabled: false`).
|
|
16485
|
+
* - `setSecondFactorPreference({userId, enabled})` — persisted by
|
|
16486
|
+
* the providing addon beside its credentials.
|
|
16487
|
+
*
|
|
16355
16488
|
* Challenges are short-lived (5 min, in-memory). The cap is internal —
|
|
16356
16489
|
* the admin-ui composes the begin/finish round-trip and never exposes
|
|
16357
16490
|
* the cap to non-admins.
|
|
@@ -16394,6 +16527,17 @@ method(object({
|
|
|
16394
16527
|
}), object({ verified: boolean() }), {
|
|
16395
16528
|
kind: "mutation",
|
|
16396
16529
|
access: "view"
|
|
16530
|
+
}), method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
|
|
16531
|
+
kind: "mutation",
|
|
16532
|
+
access: "view"
|
|
16533
|
+
}), method(object({
|
|
16534
|
+
/** AuthenticationResponseJSON from the browser. */
|
|
16535
|
+
response: record(string(), unknown()) }), object({
|
|
16536
|
+
verified: boolean(),
|
|
16537
|
+
userId: string().nullable()
|
|
16538
|
+
}), {
|
|
16539
|
+
kind: "mutation",
|
|
16540
|
+
access: "view"
|
|
16397
16541
|
}), method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }), method(object({
|
|
16398
16542
|
userId: string(),
|
|
16399
16543
|
credentialId: string()
|
|
@@ -16401,6 +16545,13 @@ method(object({
|
|
|
16401
16545
|
kind: "mutation",
|
|
16402
16546
|
auth: "admin",
|
|
16403
16547
|
access: "delete"
|
|
16548
|
+
}), method(object({ userId: string() }), object({ enabled: boolean() }), { auth: "admin" }), method(object({
|
|
16549
|
+
userId: string(),
|
|
16550
|
+
enabled: boolean()
|
|
16551
|
+
}), object({ success: literal(true) }), {
|
|
16552
|
+
kind: "mutation",
|
|
16553
|
+
auth: "admin",
|
|
16554
|
+
access: "create"
|
|
16404
16555
|
});
|
|
16405
16556
|
/**
|
|
16406
16557
|
* `videoclips` — the unified, navigable-clip surface for a camera.
|
|
@@ -16458,9 +16609,10 @@ method(object({
|
|
|
16458
16609
|
auth: "admin"
|
|
16459
16610
|
});
|
|
16460
16611
|
/**
|
|
16461
|
-
* Optional client-side hints sent at session creation to help the
|
|
16462
|
-
*
|
|
16463
|
-
*
|
|
16612
|
+
* Optional client-side hints sent at session creation to help the provider
|
|
16613
|
+
* pick the best native source. All fields optional — a viewer that knows
|
|
16614
|
+
* nothing still gets a sane default. (Relocated from the retired `webrtc`
|
|
16615
|
+
* collection cap; this `webrtc-session` cap is the live signaling surface.)
|
|
16464
16616
|
*/
|
|
16465
16617
|
var webrtcClientHintsSchema = object({
|
|
16466
16618
|
viewportWidth: number().int().positive().optional(),
|
|
@@ -16471,22 +16623,6 @@ var webrtcClientHintsSchema = object({
|
|
|
16471
16623
|
/** Hard tier override; takes precedence over scoring when registered. */
|
|
16472
16624
|
prefersTier: string().optional()
|
|
16473
16625
|
}).partial();
|
|
16474
|
-
method(object({
|
|
16475
|
-
streamId: string(),
|
|
16476
|
-
sdpOffer: string()
|
|
16477
|
-
}), string(), { kind: "mutation" }), method(object({ streamId: string() }), boolean()), method(object({
|
|
16478
|
-
streamId: string(),
|
|
16479
|
-
codec: string()
|
|
16480
|
-
}), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
|
|
16481
|
-
streamId: string(),
|
|
16482
|
-
hints: webrtcClientHintsSchema.optional()
|
|
16483
|
-
}), object({
|
|
16484
|
-
sessionId: string(),
|
|
16485
|
-
sdpOffer: string()
|
|
16486
|
-
}), { kind: "mutation" }), method(object({
|
|
16487
|
-
sessionId: string(),
|
|
16488
|
-
sdpAnswer: string()
|
|
16489
|
-
}), _void(), { kind: "mutation" }), method(object({ sessionId: string() }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), boolean());
|
|
16490
16626
|
/**
|
|
16491
16627
|
* Discriminated target for a WebRTC session. The client sends this
|
|
16492
16628
|
* structured object instead of building / parsing brokerId strings;
|
|
@@ -17217,7 +17353,17 @@ var FaceInfoSchema = object({
|
|
|
17217
17353
|
recognizedIdentityId: string().optional(),
|
|
17218
17354
|
identityName: string().optional(),
|
|
17219
17355
|
assigned: boolean(),
|
|
17220
|
-
base64: string().optional()
|
|
17356
|
+
base64: string().optional(),
|
|
17357
|
+
/** Design B: the face bbox (pixel space) on the key frame — lets a detail
|
|
17358
|
+
* view draw the box over the native `keyFrameMediaKey` frame. Absent on
|
|
17359
|
+
* legacy rows written before design B. */
|
|
17360
|
+
faceBbox: BoundingBoxSchema.optional(),
|
|
17361
|
+
/** Design B: MediaStore key of the track's native-resolution key frame.
|
|
17362
|
+
* Fetch the native JPEG via the event-media data-plane
|
|
17363
|
+
* (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
|
|
17364
|
+
* track produced no key frame (e.g. native/onboard source) — the UI falls
|
|
17365
|
+
* back to the inline `base64` face crop. */
|
|
17366
|
+
keyFrameMediaKey: string().optional()
|
|
17221
17367
|
});
|
|
17222
17368
|
var FaceFilterEnum = _enum([
|
|
17223
17369
|
"unassigned",
|
|
@@ -17914,6 +18060,16 @@ var TopologyCategorySchema = object({
|
|
|
17914
18060
|
healthy: number(),
|
|
17915
18061
|
addons: array(TopologyCategoryAddonSchema).readonly()
|
|
17916
18062
|
});
|
|
18063
|
+
/**
|
|
18064
|
+
* The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
|
|
18065
|
+
* `@camstack/agent` on agents) as reported by its `registerNode` manifest —
|
|
18066
|
+
* version visibility for the Server management surface. Nullable: offline
|
|
18067
|
+
* rows and pre-phase-2 nodes report none.
|
|
18068
|
+
*/
|
|
18069
|
+
var TopologyRootPackageSchema = object({
|
|
18070
|
+
name: string(),
|
|
18071
|
+
version: string()
|
|
18072
|
+
});
|
|
17917
18073
|
var TopologyNodeSchema = object({
|
|
17918
18074
|
id: string(),
|
|
17919
18075
|
name: string(),
|
|
@@ -17937,7 +18093,8 @@ var TopologyNodeSchema = object({
|
|
|
17937
18093
|
status: string()
|
|
17938
18094
|
})).readonly(),
|
|
17939
18095
|
processes: array(TopologyProcessSchema).readonly(),
|
|
17940
|
-
categories: array(TopologyCategorySchema).readonly()
|
|
18096
|
+
categories: array(TopologyCategorySchema).readonly(),
|
|
18097
|
+
rootPackage: TopologyRootPackageSchema.nullable()
|
|
17941
18098
|
});
|
|
17942
18099
|
var CapUsageEdgeSchema = object({
|
|
17943
18100
|
callerAddonId: string(),
|
|
@@ -20737,6 +20894,12 @@ Object.freeze({
|
|
|
20737
20894
|
addonId: null,
|
|
20738
20895
|
access: "create"
|
|
20739
20896
|
},
|
|
20897
|
+
"loginMethod.getLoginMethods": {
|
|
20898
|
+
capName: "login-method",
|
|
20899
|
+
capScope: "system",
|
|
20900
|
+
addonId: null,
|
|
20901
|
+
access: "view"
|
|
20902
|
+
},
|
|
20740
20903
|
"mediaPlayer.next": {
|
|
20741
20904
|
capName: "media-player",
|
|
20742
20905
|
capScope: "device",
|
|
@@ -21319,6 +21482,12 @@ Object.freeze({
|
|
|
21319
21482
|
addonId: null,
|
|
21320
21483
|
access: "view"
|
|
21321
21484
|
},
|
|
21485
|
+
"pipelineAnalytics.getKeyEvents": {
|
|
21486
|
+
capName: "pipeline-analytics",
|
|
21487
|
+
capScope: "device",
|
|
21488
|
+
addonId: null,
|
|
21489
|
+
access: "view"
|
|
21490
|
+
},
|
|
21322
21491
|
"pipelineAnalytics.getMotionEvents": {
|
|
21323
21492
|
capName: "pipeline-analytics",
|
|
21324
21493
|
capScope: "device",
|
|
@@ -21367,23 +21536,23 @@ Object.freeze({
|
|
|
21367
21536
|
addonId: null,
|
|
21368
21537
|
access: "create"
|
|
21369
21538
|
},
|
|
21370
|
-
"pipelineExecutor.
|
|
21539
|
+
"pipelineExecutor.clearDeviceOverrides": {
|
|
21371
21540
|
capName: "pipeline-executor",
|
|
21372
21541
|
capScope: "system",
|
|
21373
21542
|
addonId: null,
|
|
21374
21543
|
access: "delete"
|
|
21375
21544
|
},
|
|
21376
|
-
"pipelineExecutor.
|
|
21545
|
+
"pipelineExecutor.deleteModel": {
|
|
21377
21546
|
capName: "pipeline-executor",
|
|
21378
21547
|
capScope: "system",
|
|
21379
21548
|
addonId: null,
|
|
21380
21549
|
access: "delete"
|
|
21381
21550
|
},
|
|
21382
|
-
"pipelineExecutor.
|
|
21551
|
+
"pipelineExecutor.deleteTemplate": {
|
|
21383
21552
|
capName: "pipeline-executor",
|
|
21384
21553
|
capScope: "system",
|
|
21385
21554
|
addonId: null,
|
|
21386
|
-
access: "
|
|
21555
|
+
access: "delete"
|
|
21387
21556
|
},
|
|
21388
21557
|
"pipelineExecutor.downloadModel": {
|
|
21389
21558
|
capName: "pipeline-executor",
|
|
@@ -21577,13 +21746,13 @@ Object.freeze({
|
|
|
21577
21746
|
addonId: null,
|
|
21578
21747
|
access: "create"
|
|
21579
21748
|
},
|
|
21580
|
-
"
|
|
21581
|
-
capName: "pipeline-
|
|
21749
|
+
"pipelineExecutor.validatePipeline": {
|
|
21750
|
+
capName: "pipeline-executor",
|
|
21582
21751
|
capScope: "system",
|
|
21583
21752
|
addonId: null,
|
|
21584
|
-
access: "
|
|
21753
|
+
access: "view"
|
|
21585
21754
|
},
|
|
21586
|
-
"pipelineOrchestrator.
|
|
21755
|
+
"pipelineOrchestrator.assignAudio": {
|
|
21587
21756
|
capName: "pipeline-orchestrator",
|
|
21588
21757
|
capScope: "system",
|
|
21589
21758
|
addonId: null,
|
|
@@ -21667,19 +21836,13 @@ Object.freeze({
|
|
|
21667
21836
|
addonId: null,
|
|
21668
21837
|
access: "view"
|
|
21669
21838
|
},
|
|
21670
|
-
"pipelineOrchestrator.
|
|
21671
|
-
capName: "pipeline-orchestrator",
|
|
21672
|
-
capScope: "system",
|
|
21673
|
-
addonId: null,
|
|
21674
|
-
access: "view"
|
|
21675
|
-
},
|
|
21676
|
-
"pipelineOrchestrator.getDecoderAssignments": {
|
|
21839
|
+
"pipelineOrchestrator.getGlobalMetrics": {
|
|
21677
21840
|
capName: "pipeline-orchestrator",
|
|
21678
21841
|
capScope: "system",
|
|
21679
21842
|
addonId: null,
|
|
21680
21843
|
access: "view"
|
|
21681
21844
|
},
|
|
21682
|
-
"pipelineOrchestrator.
|
|
21845
|
+
"pipelineOrchestrator.getIngestOwner": {
|
|
21683
21846
|
capName: "pipeline-orchestrator",
|
|
21684
21847
|
capScope: "system",
|
|
21685
21848
|
addonId: null,
|
|
@@ -21721,6 +21884,12 @@ Object.freeze({
|
|
|
21721
21884
|
addonId: null,
|
|
21722
21885
|
access: "delete"
|
|
21723
21886
|
},
|
|
21887
|
+
"pipelineOrchestrator.resetNodePipelineDefaults": {
|
|
21888
|
+
capName: "pipeline-orchestrator",
|
|
21889
|
+
capScope: "system",
|
|
21890
|
+
addonId: null,
|
|
21891
|
+
access: "delete"
|
|
21892
|
+
},
|
|
21724
21893
|
"pipelineOrchestrator.resolvePipeline": {
|
|
21725
21894
|
capName: "pipeline-orchestrator",
|
|
21726
21895
|
capScope: "system",
|
|
@@ -21757,37 +21926,37 @@ Object.freeze({
|
|
|
21757
21926
|
addonId: null,
|
|
21758
21927
|
access: "create"
|
|
21759
21928
|
},
|
|
21760
|
-
"pipelineOrchestrator.
|
|
21929
|
+
"pipelineOrchestrator.setAgentReachableHost": {
|
|
21761
21930
|
capName: "pipeline-orchestrator",
|
|
21762
21931
|
capScope: "system",
|
|
21763
21932
|
addonId: null,
|
|
21764
21933
|
access: "create"
|
|
21765
21934
|
},
|
|
21766
|
-
"pipelineOrchestrator.
|
|
21935
|
+
"pipelineOrchestrator.setCameraPipelineForAgent": {
|
|
21767
21936
|
capName: "pipeline-orchestrator",
|
|
21768
21937
|
capScope: "system",
|
|
21769
21938
|
addonId: null,
|
|
21770
21939
|
access: "create"
|
|
21771
21940
|
},
|
|
21772
|
-
"pipelineOrchestrator.
|
|
21941
|
+
"pipelineOrchestrator.setCameraStepOverride": {
|
|
21773
21942
|
capName: "pipeline-orchestrator",
|
|
21774
21943
|
capScope: "system",
|
|
21775
21944
|
addonId: null,
|
|
21776
21945
|
access: "create"
|
|
21777
21946
|
},
|
|
21778
|
-
"pipelineOrchestrator.
|
|
21947
|
+
"pipelineOrchestrator.setCameraStepToggle": {
|
|
21779
21948
|
capName: "pipeline-orchestrator",
|
|
21780
21949
|
capScope: "system",
|
|
21781
21950
|
addonId: null,
|
|
21782
21951
|
access: "create"
|
|
21783
21952
|
},
|
|
21784
|
-
"pipelineOrchestrator.
|
|
21953
|
+
"pipelineOrchestrator.setCapabilityBinding": {
|
|
21785
21954
|
capName: "pipeline-orchestrator",
|
|
21786
21955
|
capScope: "system",
|
|
21787
21956
|
addonId: null,
|
|
21788
21957
|
access: "create"
|
|
21789
21958
|
},
|
|
21790
|
-
"pipelineOrchestrator.
|
|
21959
|
+
"pipelineOrchestrator.unassignAudio": {
|
|
21791
21960
|
capName: "pipeline-orchestrator",
|
|
21792
21961
|
capScope: "system",
|
|
21793
21962
|
addonId: null,
|
|
@@ -21847,6 +22016,12 @@ Object.freeze({
|
|
|
21847
22016
|
addonId: null,
|
|
21848
22017
|
access: "view"
|
|
21849
22018
|
},
|
|
22019
|
+
"pipelineRunner.getNativeCrop": {
|
|
22020
|
+
capName: "pipeline-runner",
|
|
22021
|
+
capScope: "system",
|
|
22022
|
+
addonId: null,
|
|
22023
|
+
access: "view"
|
|
22024
|
+
},
|
|
21850
22025
|
"pipelineRunner.reportMotion": {
|
|
21851
22026
|
capName: "pipeline-runner",
|
|
21852
22027
|
capScope: "system",
|
|
@@ -22087,33 +22262,45 @@ Object.freeze({
|
|
|
22087
22262
|
addonId: null,
|
|
22088
22263
|
access: "create"
|
|
22089
22264
|
},
|
|
22090
|
-
"
|
|
22091
|
-
capName: "
|
|
22265
|
+
"scriptRunner.run": {
|
|
22266
|
+
capName: "script-runner",
|
|
22267
|
+
capScope: "device",
|
|
22268
|
+
addonId: null,
|
|
22269
|
+
access: "create"
|
|
22270
|
+
},
|
|
22271
|
+
"scriptRunner.stop": {
|
|
22272
|
+
capName: "script-runner",
|
|
22273
|
+
capScope: "device",
|
|
22274
|
+
addonId: null,
|
|
22275
|
+
access: "create"
|
|
22276
|
+
},
|
|
22277
|
+
"serverManagement.applyServerUpdate": {
|
|
22278
|
+
capName: "server-management",
|
|
22092
22279
|
capScope: "system",
|
|
22093
22280
|
addonId: null,
|
|
22094
|
-
access: "
|
|
22281
|
+
access: "create"
|
|
22095
22282
|
},
|
|
22096
|
-
"
|
|
22097
|
-
capName: "
|
|
22283
|
+
"serverManagement.checkServerUpdate": {
|
|
22284
|
+
capName: "server-management",
|
|
22098
22285
|
capScope: "system",
|
|
22099
22286
|
addonId: null,
|
|
22100
22287
|
access: "create"
|
|
22101
22288
|
},
|
|
22102
|
-
"
|
|
22103
|
-
capName: "
|
|
22289
|
+
"serverManagement.getServerPackageStatus": {
|
|
22290
|
+
capName: "server-management",
|
|
22104
22291
|
capScope: "system",
|
|
22105
22292
|
addonId: null,
|
|
22106
|
-
access: "
|
|
22293
|
+
access: "view"
|
|
22107
22294
|
},
|
|
22108
|
-
"
|
|
22109
|
-
capName: "
|
|
22110
|
-
capScope: "
|
|
22295
|
+
"serverManagement.restartServer": {
|
|
22296
|
+
capName: "server-management",
|
|
22297
|
+
capScope: "system",
|
|
22111
22298
|
addonId: null,
|
|
22112
22299
|
access: "create"
|
|
22113
22300
|
},
|
|
22114
|
-
"
|
|
22115
|
-
capName: "
|
|
22116
|
-
capScope: "
|
|
22301
|
+
"serverManagement.rollbackServerUpdate": {
|
|
22302
|
+
capName: "server-management",
|
|
22303
|
+
capScope: "system",
|
|
22117
22304
|
addonId: null,
|
|
22118
22305
|
access: "create"
|
|
22119
22306
|
},
|
|
@@ -22201,23 +22388,17 @@ Object.freeze({
|
|
|
22201
22388
|
addonId: null,
|
|
22202
22389
|
access: "view"
|
|
22203
22390
|
},
|
|
22204
|
-
"snapshot.
|
|
22391
|
+
"snapshot.getSnapshotOverview": {
|
|
22205
22392
|
capName: "snapshot",
|
|
22206
22393
|
capScope: "device",
|
|
22207
22394
|
addonId: null,
|
|
22208
|
-
access: "create"
|
|
22209
|
-
},
|
|
22210
|
-
"snapshotProvider.getSnapshot": {
|
|
22211
|
-
capName: "snapshot-provider",
|
|
22212
|
-
capScope: "system",
|
|
22213
|
-
addonId: null,
|
|
22214
22395
|
access: "view"
|
|
22215
22396
|
},
|
|
22216
|
-
"
|
|
22217
|
-
capName: "snapshot
|
|
22218
|
-
capScope: "
|
|
22397
|
+
"snapshot.invalidateCache": {
|
|
22398
|
+
capName: "snapshot",
|
|
22399
|
+
capScope: "device",
|
|
22219
22400
|
addonId: null,
|
|
22220
|
-
access: "
|
|
22401
|
+
access: "create"
|
|
22221
22402
|
},
|
|
22222
22403
|
"ssoBridge.signBridgeToken": {
|
|
22223
22404
|
capName: "sso-bridge",
|
|
@@ -22645,30 +22826,6 @@ Object.freeze({
|
|
|
22645
22826
|
addonId: null,
|
|
22646
22827
|
access: "view"
|
|
22647
22828
|
},
|
|
22648
|
-
"streamingEngine.getStreamUrl": {
|
|
22649
|
-
capName: "streaming-engine",
|
|
22650
|
-
capScope: "system",
|
|
22651
|
-
addonId: null,
|
|
22652
|
-
access: "view"
|
|
22653
|
-
},
|
|
22654
|
-
"streamingEngine.listStreams": {
|
|
22655
|
-
capName: "streaming-engine",
|
|
22656
|
-
capScope: "system",
|
|
22657
|
-
addonId: null,
|
|
22658
|
-
access: "view"
|
|
22659
|
-
},
|
|
22660
|
-
"streamingEngine.registerStream": {
|
|
22661
|
-
capName: "streaming-engine",
|
|
22662
|
-
capScope: "system",
|
|
22663
|
-
addonId: null,
|
|
22664
|
-
access: "create"
|
|
22665
|
-
},
|
|
22666
|
-
"streamingEngine.unregisterStream": {
|
|
22667
|
-
capName: "streaming-engine",
|
|
22668
|
-
capScope: "system",
|
|
22669
|
-
addonId: null,
|
|
22670
|
-
access: "delete"
|
|
22671
|
-
},
|
|
22672
22829
|
"streamParams.getConfigSchema": {
|
|
22673
22830
|
capName: "stream-params",
|
|
22674
22831
|
capScope: "device",
|
|
@@ -22915,6 +23072,12 @@ Object.freeze({
|
|
|
22915
23072
|
addonId: null,
|
|
22916
23073
|
access: "view"
|
|
22917
23074
|
},
|
|
23075
|
+
"userPasskeys.beginDiscoverableAuthentication": {
|
|
23076
|
+
capName: "user-passkeys",
|
|
23077
|
+
capScope: "system",
|
|
23078
|
+
addonId: null,
|
|
23079
|
+
access: "view"
|
|
23080
|
+
},
|
|
22918
23081
|
"userPasskeys.beginRegistration": {
|
|
22919
23082
|
capName: "user-passkeys",
|
|
22920
23083
|
capScope: "system",
|
|
@@ -22927,12 +23090,24 @@ Object.freeze({
|
|
|
22927
23090
|
addonId: null,
|
|
22928
23091
|
access: "view"
|
|
22929
23092
|
},
|
|
23093
|
+
"userPasskeys.finishDiscoverableAuthentication": {
|
|
23094
|
+
capName: "user-passkeys",
|
|
23095
|
+
capScope: "system",
|
|
23096
|
+
addonId: null,
|
|
23097
|
+
access: "view"
|
|
23098
|
+
},
|
|
22930
23099
|
"userPasskeys.finishRegistration": {
|
|
22931
23100
|
capName: "user-passkeys",
|
|
22932
23101
|
capScope: "system",
|
|
22933
23102
|
addonId: null,
|
|
22934
23103
|
access: "create"
|
|
22935
23104
|
},
|
|
23105
|
+
"userPasskeys.getSecondFactorPreference": {
|
|
23106
|
+
capName: "user-passkeys",
|
|
23107
|
+
capScope: "system",
|
|
23108
|
+
addonId: null,
|
|
23109
|
+
access: "view"
|
|
23110
|
+
},
|
|
22936
23111
|
"userPasskeys.listPasskeys": {
|
|
22937
23112
|
capName: "user-passkeys",
|
|
22938
23113
|
capScope: "system",
|
|
@@ -22945,6 +23120,12 @@ Object.freeze({
|
|
|
22945
23120
|
addonId: null,
|
|
22946
23121
|
access: "delete"
|
|
22947
23122
|
},
|
|
23123
|
+
"userPasskeys.setSecondFactorPreference": {
|
|
23124
|
+
capName: "user-passkeys",
|
|
23125
|
+
capScope: "system",
|
|
23126
|
+
addonId: null,
|
|
23127
|
+
access: "create"
|
|
23128
|
+
},
|
|
22948
23129
|
"vacuumControl.locate": {
|
|
22949
23130
|
capName: "vacuum-control",
|
|
22950
23131
|
capScope: "device",
|
|
@@ -23017,6 +23198,18 @@ Object.freeze({
|
|
|
23017
23198
|
addonId: null,
|
|
23018
23199
|
access: "view"
|
|
23019
23200
|
},
|
|
23201
|
+
"viewerUi.getStaticDir": {
|
|
23202
|
+
capName: "viewer-ui",
|
|
23203
|
+
capScope: "system",
|
|
23204
|
+
addonId: null,
|
|
23205
|
+
access: "view"
|
|
23206
|
+
},
|
|
23207
|
+
"viewerUi.getVersion": {
|
|
23208
|
+
capName: "viewer-ui",
|
|
23209
|
+
capScope: "system",
|
|
23210
|
+
addonId: null,
|
|
23211
|
+
access: "view"
|
|
23212
|
+
},
|
|
23020
23213
|
"waterHeater.setAway": {
|
|
23021
23214
|
capName: "water-heater",
|
|
23022
23215
|
capScope: "device",
|
|
@@ -23035,54 +23228,6 @@ Object.freeze({
|
|
|
23035
23228
|
addonId: null,
|
|
23036
23229
|
access: "create"
|
|
23037
23230
|
},
|
|
23038
|
-
"webrtc.closeSession": {
|
|
23039
|
-
capName: "webrtc",
|
|
23040
|
-
capScope: "system",
|
|
23041
|
-
addonId: null,
|
|
23042
|
-
access: "create"
|
|
23043
|
-
},
|
|
23044
|
-
"webrtc.createSession": {
|
|
23045
|
-
capName: "webrtc",
|
|
23046
|
-
capScope: "system",
|
|
23047
|
-
addonId: null,
|
|
23048
|
-
access: "create"
|
|
23049
|
-
},
|
|
23050
|
-
"webrtc.handleAnswer": {
|
|
23051
|
-
capName: "webrtc",
|
|
23052
|
-
capScope: "system",
|
|
23053
|
-
addonId: null,
|
|
23054
|
-
access: "create"
|
|
23055
|
-
},
|
|
23056
|
-
"webrtc.handleOffer": {
|
|
23057
|
-
capName: "webrtc",
|
|
23058
|
-
capScope: "system",
|
|
23059
|
-
addonId: null,
|
|
23060
|
-
access: "create"
|
|
23061
|
-
},
|
|
23062
|
-
"webrtc.hasAdaptiveBitrate": {
|
|
23063
|
-
capName: "webrtc",
|
|
23064
|
-
capScope: "system",
|
|
23065
|
-
addonId: null,
|
|
23066
|
-
access: "view"
|
|
23067
|
-
},
|
|
23068
|
-
"webrtc.registerStream": {
|
|
23069
|
-
capName: "webrtc",
|
|
23070
|
-
capScope: "system",
|
|
23071
|
-
addonId: null,
|
|
23072
|
-
access: "create"
|
|
23073
|
-
},
|
|
23074
|
-
"webrtc.supportsStream": {
|
|
23075
|
-
capName: "webrtc",
|
|
23076
|
-
capScope: "system",
|
|
23077
|
-
addonId: null,
|
|
23078
|
-
access: "view"
|
|
23079
|
-
},
|
|
23080
|
-
"webrtc.unregisterStream": {
|
|
23081
|
-
capName: "webrtc",
|
|
23082
|
-
capScope: "system",
|
|
23083
|
-
addonId: null,
|
|
23084
|
-
access: "delete"
|
|
23085
|
-
},
|
|
23086
23231
|
"webrtcSession.addIceCandidate": {
|
|
23087
23232
|
capName: "webrtc-session",
|
|
23088
23233
|
capScope: "device",
|