@camstack/addon-pipeline 0.1.13 → 0.1.15
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/audio-analyzer/index.js +2 -4
- package/dist/audio-analyzer/index.js.map +1 -1
- package/dist/audio-analyzer/index.mjs +2 -4
- package/dist/audio-analyzer/index.mjs.map +1 -1
- package/dist/audio-codec-nodeav/index.js +1 -1
- package/dist/audio-codec-nodeav/index.mjs +1 -1
- package/dist/decoder-nodeav/index.js +552 -18
- package/dist/decoder-nodeav/index.js.map +1 -1
- package/dist/decoder-nodeav/index.mjs +553 -19
- package/dist/decoder-nodeav/index.mjs.map +1 -1
- package/dist/detection-pipeline/index.js +2 -4
- package/dist/detection-pipeline/index.js.map +1 -1
- package/dist/detection-pipeline/index.mjs +2 -4
- package/dist/detection-pipeline/index.mjs.map +1 -1
- package/dist/{index-BwLnHesq.mjs → index-CVzLrojg.mjs} +567 -327
- package/dist/index-CVzLrojg.mjs.map +1 -0
- package/dist/{index-BBpVDiWL.js → index-p-6GfKOg.js} +567 -327
- package/dist/index-p-6GfKOg.js.map +1 -0
- package/dist/motion-wasm/index.js +2 -4
- package/dist/motion-wasm/index.js.map +1 -1
- package/dist/motion-wasm/index.mjs +2 -4
- package/dist/motion-wasm/index.mjs.map +1 -1
- package/dist/pipeline-runner/index.js +133 -54
- package/dist/pipeline-runner/index.js.map +1 -1
- package/dist/pipeline-runner/index.mjs +133 -54
- package/dist/pipeline-runner/index.mjs.map +1 -1
- package/dist/stream-broker/@mf-types.zip +0 -0
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.mjs-d8PmLbO2.mjs +19 -0
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.mjs-B4l8Nb2y.mjs +20 -0
- package/dist/stream-broker/{__mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.mjs-DePVYdid.mjs → __mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.mjs-DAssX3h0.mjs} +4 -2
- package/dist/stream-broker/{__mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.mjs_commonjs-proxy-CBlCGyx5.mjs → __mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.mjs_commonjs-proxy-DFoJJhpt.mjs} +1 -1
- package/dist/stream-broker/{__mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_2_dom__loadShare__.mjs_commonjs-proxy-DZchZKbW.mjs → __mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_2_dom__loadShare__.mjs_commonjs-proxy-x7XMEeuJ.mjs} +1 -1
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B8d_i3jf.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CWHjxwIc.mjs} +6 -6
- package/dist/stream-broker/{client-BK73l2KT.mjs → client-CZXrddDR.mjs} +2990 -3217
- package/dist/stream-broker/{hostInit-oba_vMZE.mjs → hostInit-B86vUcFC.mjs} +12 -12
- package/dist/stream-broker/{index-COebxMhm.mjs → index-BCEx31Mh.mjs} +4032 -3582
- package/dist/stream-broker/{index-BAZcm437.mjs → index-BvV3RVTZ.mjs} +1 -1
- package/dist/stream-broker/{index-IUYKHbxX.mjs → index-C0BzaWmB.mjs} +1 -1
- package/dist/stream-broker/index-CWkKuNLr.mjs +232 -0
- package/dist/stream-broker/{index-ns1fRD30.mjs → index-CZNxa0ad.mjs} +1 -1
- package/dist/stream-broker/index-Kb4xa8FX.mjs +36403 -0
- package/dist/stream-broker/{index-BxHaCH3N.mjs → index-KtR7Pp0O.mjs} +1 -1
- package/dist/stream-broker/{index-Ss9m7Jum.mjs → index-cYW01SNH.mjs} +1 -1
- package/dist/stream-broker/index.js +805 -544
- package/dist/stream-broker/index.js.map +1 -1
- package/dist/stream-broker/index.mjs +805 -522
- package/dist/stream-broker/index.mjs.map +1 -1
- package/dist/stream-broker/{jsx-runtime-ZdY5pIZz.mjs → jsx-runtime-B_evVsXl.mjs} +1 -1
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +23 -31
- package/dist/index-BBpVDiWL.js.map +0 -1
- package/dist/index-BwLnHesq.mjs.map +0 -1
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.mjs-p-Z3JTk9.mjs +0 -19
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.mjs-MYpjp-jd.mjs +0 -20
- package/dist/stream-broker/index-BkB3U-Tc.mjs +0 -20852
- package/python/__pycache__/inference_pool.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/__init__.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/__init__.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/_safety.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/arcface.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/arcface.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/ctc.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/ctc.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/saliency.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/saliency.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/scrfd.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/scrfd.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/softmax.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/softmax.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/yamnet.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/yamnet.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo_seg.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo_seg.cpython-313.pyc +0 -0
|
@@ -5067,6 +5067,7 @@ const WELL_KNOWN_TABS = [
|
|
|
5067
5067
|
{ id: "osd", label: "OSD", icon: "type", order: 18 },
|
|
5068
5068
|
{ id: "stream-broker", label: "Stream Broker", icon: "radio", order: 20 },
|
|
5069
5069
|
{ id: "streaming", label: "Streaming", icon: "video", order: 35 },
|
|
5070
|
+
{ id: "ptz", label: "PTZ", icon: "move", order: 40 },
|
|
5070
5071
|
{ id: "pipeline", label: "Detection Pipeline", icon: "cpu", order: 39 },
|
|
5071
5072
|
{ id: "zones", label: "Detection", icon: "shapes", order: 38 },
|
|
5072
5073
|
{ id: "live-stats", label: "Live Stats", icon: "activity", order: 39 },
|
|
@@ -5134,6 +5135,9 @@ function hydrateField(field, values) {
|
|
|
5134
5135
|
return { ...field, value: items };
|
|
5135
5136
|
}
|
|
5136
5137
|
const rawValue = storedValue !== void 0 ? storedValue : defaultValue !== void 0 ? defaultValue : null;
|
|
5138
|
+
if (field.type === "password") {
|
|
5139
|
+
return { ...field, value: "" };
|
|
5140
|
+
}
|
|
5137
5141
|
const value = field.type === "textarea" && field.isJson && rawValue !== null && typeof rawValue === "object" ? JSON.stringify(rawValue, null, 2) : rawValue;
|
|
5138
5142
|
const hydrated = { ...field, value };
|
|
5139
5143
|
return hydrated;
|
|
@@ -5220,7 +5224,19 @@ const DecoderSessionConfigSchema = object({
|
|
|
5220
5224
|
* on every line so `grep tag=broker:5/high` filters one camera
|
|
5221
5225
|
* profile cleanly.
|
|
5222
5226
|
*/
|
|
5223
|
-
tag: string().optional()
|
|
5227
|
+
tag: string().optional(),
|
|
5228
|
+
/**
|
|
5229
|
+
* Where the session delivers decoded frames (Phase 5 / D9):
|
|
5230
|
+
*
|
|
5231
|
+
* - `'callback'` (default) — the legacy pixel path: decoded frames are
|
|
5232
|
+
* buffered as `DecodedFrame`s and drained via `pullFrames`.
|
|
5233
|
+
* - `'shm'` — the shared-memory frame plane: decoded frames are written
|
|
5234
|
+
* into an OS shared-memory ring and drained as zero-pixel
|
|
5235
|
+
* `FrameHandle`s via `pullHandles`. A session is one mode or the
|
|
5236
|
+
* other — `pullFrames` returns nothing for an `'shm'` session and
|
|
5237
|
+
* `pullHandles` returns nothing for a `'callback'` session.
|
|
5238
|
+
*/
|
|
5239
|
+
frameSink: _enum(["callback", "shm"]).default("callback")
|
|
5224
5240
|
});
|
|
5225
5241
|
function errMsg(err) {
|
|
5226
5242
|
if (err instanceof Error) return err.message;
|
|
@@ -5986,6 +6002,53 @@ const DecodedFrameSchema = object({
|
|
|
5986
6002
|
format: _enum(["jpeg", "rgb", "bgr", "yuv420", "gray"]),
|
|
5987
6003
|
timestamp: number()
|
|
5988
6004
|
});
|
|
6005
|
+
const FrameHandleSchema = object({
|
|
6006
|
+
shmId: string(),
|
|
6007
|
+
slot: number().int().nonnegative(),
|
|
6008
|
+
seq: number().int().nonnegative(),
|
|
6009
|
+
width: number().int().positive(),
|
|
6010
|
+
height: number().int().positive(),
|
|
6011
|
+
format: _enum(["jpeg", "rgb", "bgr", "yuv420", "gray"]),
|
|
6012
|
+
pts: number(),
|
|
6013
|
+
byteLength: number().int().nonnegative(),
|
|
6014
|
+
nodeId: string(),
|
|
6015
|
+
slotCount: number().int().positive()
|
|
6016
|
+
});
|
|
6017
|
+
const FrameHandleFormatSchema = _enum(["rgb", "bgr", "yuv420", "gray"]);
|
|
6018
|
+
const SubscribeFramesInputSchema = object({
|
|
6019
|
+
brokerId: string(),
|
|
6020
|
+
format: FrameHandleFormatSchema,
|
|
6021
|
+
/**
|
|
6022
|
+
* Optional reader-side cadence hint in frames per second. The broker does
|
|
6023
|
+
* NOT throttle — latest-wins ring reads drop frames implicitly for a slow
|
|
6024
|
+
* consumer. The value is echoed back in `SubscribeFramesResult.maxFps` so
|
|
6025
|
+
* the consumer can pace its own `pullFrameHandles` polling.
|
|
6026
|
+
*/
|
|
6027
|
+
maxFps: number().positive().optional(),
|
|
6028
|
+
/** Short caller-identity tag (`motion`, `detection`, …) for diagnostics. */
|
|
6029
|
+
tag: string().optional()
|
|
6030
|
+
});
|
|
6031
|
+
const SubscribeFramesResultSchema = object({
|
|
6032
|
+
/** Opaque id the consumer passes to `pullFrameHandles` / `unsubscribeFrames`. */
|
|
6033
|
+
subscriptionId: string(),
|
|
6034
|
+
/** Reader-side cadence hint (frames/s) — echoes `SubscribeFramesInput.maxFps`. */
|
|
6035
|
+
maxFps: number().nonnegative()
|
|
6036
|
+
});
|
|
6037
|
+
const DecodedAudioChunkSchema = object({
|
|
6038
|
+
data: _instanceof(Uint8Array),
|
|
6039
|
+
sampleRate: number().int().positive(),
|
|
6040
|
+
channels: number().int().positive(),
|
|
6041
|
+
timestamp: number()
|
|
6042
|
+
});
|
|
6043
|
+
const SubscribeAudioChunksInputSchema = object({
|
|
6044
|
+
brokerId: string(),
|
|
6045
|
+
/** Short caller-identity tag (`audio-analyzer`, …) for `listClients`. */
|
|
6046
|
+
tag: string().optional()
|
|
6047
|
+
});
|
|
6048
|
+
const SubscribeAudioChunksResultSchema = object({
|
|
6049
|
+
/** Opaque id passed to `pullAudioChunks` / `unsubscribeAudioChunks`. */
|
|
6050
|
+
subscriptionId: string()
|
|
6051
|
+
});
|
|
5989
6052
|
const BrokerStatusSchema$1 = _enum(["idle", "connecting", "streaming", "error", "stopped"]);
|
|
5990
6053
|
const BrokerStatsSchema = object({
|
|
5991
6054
|
status: BrokerStatusSchema$1,
|
|
@@ -6211,9 +6274,76 @@ const streamBrokerCapability = {
|
|
|
6211
6274
|
object({ released: boolean(), refcount: number().int().nonnegative() }),
|
|
6212
6275
|
{ kind: "mutation", auth: "admin" }
|
|
6213
6276
|
),
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6277
|
+
/**
|
|
6278
|
+
* ── Decoded audio-chunk plane (Phase 5 / D9) ──────────────────────
|
|
6279
|
+
*
|
|
6280
|
+
* The serialisable replacement for the live-object `IStreamBroker.
|
|
6281
|
+
* onDecodedAudioChunk` callback path. Unlike the video frame plane,
|
|
6282
|
+
* audio chunks are tiny (a ~500ms PCM window is a few KB) so they ship
|
|
6283
|
+
* their bytes INLINE over tRPC — no shared-memory ring. A consumer:
|
|
6284
|
+
*
|
|
6285
|
+
* 1. `subscribeAudioChunks({ brokerId, tag })` — the broker registers
|
|
6286
|
+
* a per-subscription bounded FIFO queue and returns a
|
|
6287
|
+
* `subscriptionId`.
|
|
6288
|
+
* 2. polls `pullAudioChunks({ subscriptionId, maxCount })` — drains
|
|
6289
|
+
* `DecodedAudioChunk[]` in arrival order (FIFO, no latest-wins
|
|
6290
|
+
* drop: an audio gap is audible / breaks an analysis window). The
|
|
6291
|
+
* queue is generously sized; it only drops its oldest chunk if a
|
|
6292
|
+
* truly stalled consumer lets it overflow.
|
|
6293
|
+
* 3. `unsubscribeAudioChunks({ subscriptionId })` on teardown.
|
|
6294
|
+
*/
|
|
6295
|
+
subscribeAudioChunks: method(
|
|
6296
|
+
SubscribeAudioChunksInputSchema,
|
|
6297
|
+
SubscribeAudioChunksResultSchema,
|
|
6298
|
+
{ kind: "mutation" }
|
|
6299
|
+
),
|
|
6300
|
+
pullAudioChunks: method(
|
|
6301
|
+
object({
|
|
6302
|
+
subscriptionId: string(),
|
|
6303
|
+
maxCount: number().int().positive().default(8)
|
|
6304
|
+
}),
|
|
6305
|
+
array(DecodedAudioChunkSchema).readonly()
|
|
6306
|
+
),
|
|
6307
|
+
unsubscribeAudioChunks: method(
|
|
6308
|
+
object({ subscriptionId: string() }),
|
|
6309
|
+
object({ released: boolean() }),
|
|
6310
|
+
{ kind: "mutation" }
|
|
6311
|
+
),
|
|
6312
|
+
/**
|
|
6313
|
+
* ── Shared-memory frame plane (Phase 5 / D9) ──────────────────────
|
|
6314
|
+
*
|
|
6315
|
+
* The handle-based replacement for the live-object `IStreamBroker.
|
|
6316
|
+
* onDecodedFrame` callback path. A consumer:
|
|
6317
|
+
*
|
|
6318
|
+
* 1. `subscribeFrames({ brokerId, format })` — the broker spins up
|
|
6319
|
+
* (or reuses) a `frameSink: 'shm'` decoder session producing that
|
|
6320
|
+
* `format` and returns a `subscriptionId`.
|
|
6321
|
+
* 2. polls `pullFrameHandles({ subscriptionId, maxCount })` — drains
|
|
6322
|
+
* zero-pixel `FrameHandle[]`; each handle is fed to a
|
|
6323
|
+
* `FrameRingReader` that opens the named shm segment and reads the
|
|
6324
|
+
* pixels back zero-copy.
|
|
6325
|
+
* 3. `unsubscribeFrames({ subscriptionId })` on teardown.
|
|
6326
|
+
*
|
|
6327
|
+
* The broker keeps one shm ring per `(brokerId, format)` actually
|
|
6328
|
+
* requested — no broker-side `sharp` conversion. fps throttling is
|
|
6329
|
+
* implicit (latest-wins ring reads drop frames for a slow consumer).
|
|
6330
|
+
*/
|
|
6331
|
+
subscribeFrames: method(
|
|
6332
|
+
SubscribeFramesInputSchema,
|
|
6333
|
+
SubscribeFramesResultSchema,
|
|
6334
|
+
{ kind: "mutation" }
|
|
6335
|
+
),
|
|
6336
|
+
pullFrameHandles: method(
|
|
6337
|
+
object({
|
|
6338
|
+
subscriptionId: string(),
|
|
6339
|
+
maxCount: number().int().positive().default(4)
|
|
6340
|
+
}),
|
|
6341
|
+
array(FrameHandleSchema).readonly()
|
|
6342
|
+
),
|
|
6343
|
+
unsubscribeFrames: method(
|
|
6344
|
+
object({ subscriptionId: string() }),
|
|
6345
|
+
object({ released: boolean() }),
|
|
6346
|
+
{ kind: "mutation" }
|
|
6217
6347
|
),
|
|
6218
6348
|
setPreBufferDuration: method(
|
|
6219
6349
|
object({ brokerId: string(), seconds: number().min(0).max(30) }),
|
|
@@ -6269,6 +6399,8 @@ const cameraStreamsCapability = {
|
|
|
6269
6399
|
name: "camera-streams",
|
|
6270
6400
|
scope: "device",
|
|
6271
6401
|
mode: "singleton",
|
|
6402
|
+
kind: "wrapper",
|
|
6403
|
+
defaultActive: true,
|
|
6272
6404
|
deviceTypes: [DeviceType.Camera],
|
|
6273
6405
|
methods: {
|
|
6274
6406
|
getCameraStreams: method(
|
|
@@ -6534,6 +6666,8 @@ const motionDetectionCapability = {
|
|
|
6534
6666
|
name: "motion-detection",
|
|
6535
6667
|
scope: "device",
|
|
6536
6668
|
mode: "singleton",
|
|
6669
|
+
kind: "wrapper",
|
|
6670
|
+
defaultActive: true,
|
|
6537
6671
|
exposesDeviceSettings: true,
|
|
6538
6672
|
methods: {
|
|
6539
6673
|
analyze: method(
|
|
@@ -7304,6 +7438,34 @@ MotionTriggerStatusSchema.extend({
|
|
|
7304
7438
|
}) }
|
|
7305
7439
|
}
|
|
7306
7440
|
});
|
|
7441
|
+
object({
|
|
7442
|
+
enabled: boolean(),
|
|
7443
|
+
sensitivity: number(),
|
|
7444
|
+
/** Row-major active-cell grid. Length = gridWidth*gridHeight (see getOptions). */
|
|
7445
|
+
cells: array(boolean()),
|
|
7446
|
+
lastFetchedAt: number()
|
|
7447
|
+
});
|
|
7448
|
+
const MotionZoneOptionsSchema = object({
|
|
7449
|
+
gridWidth: number(),
|
|
7450
|
+
gridHeight: number(),
|
|
7451
|
+
sensitivity: object({ min: number(), max: number(), step: number() })
|
|
7452
|
+
});
|
|
7453
|
+
const MotionZonePatchSchema = object({
|
|
7454
|
+
enabled: boolean().optional(),
|
|
7455
|
+
sensitivity: number().optional(),
|
|
7456
|
+
cells: array(boolean()).optional()
|
|
7457
|
+
});
|
|
7458
|
+
({
|
|
7459
|
+
deviceTypes: [DeviceType.Camera],
|
|
7460
|
+
methods: {
|
|
7461
|
+
getOptions: method(object({ deviceId: number() }), MotionZoneOptionsSchema),
|
|
7462
|
+
setZone: method(
|
|
7463
|
+
object({ deviceId: number(), patch: MotionZonePatchSchema }),
|
|
7464
|
+
_void(),
|
|
7465
|
+
{ kind: "mutation", auth: "admin" }
|
|
7466
|
+
)
|
|
7467
|
+
}
|
|
7468
|
+
});
|
|
7307
7469
|
const AutotrackTargetTypeSchema = string().describe("Vendor target string (people/vehicle/pet); empty = camera default");
|
|
7308
7470
|
const PtzAutotrackSettingsSchema = object({
|
|
7309
7471
|
targetType: AutotrackTargetTypeSchema,
|
|
@@ -7392,6 +7554,100 @@ PtzAutotrackStatusSchema.extend({
|
|
|
7392
7554
|
}) }
|
|
7393
7555
|
}
|
|
7394
7556
|
});
|
|
7557
|
+
const StreamProfileSchema = _enum(["main", "sub", "ext"]);
|
|
7558
|
+
const StreamProfileConfigSchema = object({
|
|
7559
|
+
width: number(),
|
|
7560
|
+
height: number(),
|
|
7561
|
+
codec: _enum(["h264", "h265"]),
|
|
7562
|
+
framerate: number(),
|
|
7563
|
+
bitrate: number(),
|
|
7564
|
+
// kbps
|
|
7565
|
+
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
7566
|
+
encoderProfile: _enum(["high", "main", "baseline"]).optional(),
|
|
7567
|
+
gop: number().optional(),
|
|
7568
|
+
audio: boolean().optional()
|
|
7569
|
+
});
|
|
7570
|
+
object({
|
|
7571
|
+
/** Per-profile current config. A profile absent = the camera doesn't have it. */
|
|
7572
|
+
main: StreamProfileConfigSchema.optional(),
|
|
7573
|
+
sub: StreamProfileConfigSchema.optional(),
|
|
7574
|
+
ext: StreamProfileConfigSchema.optional(),
|
|
7575
|
+
lastFetchedAt: number()
|
|
7576
|
+
});
|
|
7577
|
+
const StreamProfileOptionsSchema = object({
|
|
7578
|
+
resolutions: array(object({ width: number(), height: number() })),
|
|
7579
|
+
codecs: array(_enum(["h264", "h265"])),
|
|
7580
|
+
framerates: array(number()),
|
|
7581
|
+
/** Allowed bitrate values (kbps). Empty if the camera takes a free range. */
|
|
7582
|
+
bitrates: array(number()),
|
|
7583
|
+
/** Optional [min,max] kbps when the camera accepts a continuous range. */
|
|
7584
|
+
bitrateRange: tuple([number(), number()]).optional(),
|
|
7585
|
+
supportsBitrateMode: boolean(),
|
|
7586
|
+
supportsEncoderProfile: boolean(),
|
|
7587
|
+
supportsGop: boolean(),
|
|
7588
|
+
/** Allowed GOP / keyframe-interval range, in seconds — drives the
|
|
7589
|
+
* I-frame-interval selector. Absent when the camera advertises GOP
|
|
7590
|
+
* support but no concrete range (callers then fall back to a free
|
|
7591
|
+
* numeric input). `{ min, max, step }` per the getOptions convention. */
|
|
7592
|
+
gop: object({ min: number(), max: number(), step: number() }).optional()
|
|
7593
|
+
});
|
|
7594
|
+
const StreamParamsOptionsSchema = object({
|
|
7595
|
+
main: StreamProfileOptionsSchema.optional(),
|
|
7596
|
+
sub: StreamProfileOptionsSchema.optional(),
|
|
7597
|
+
ext: StreamProfileOptionsSchema.optional()
|
|
7598
|
+
});
|
|
7599
|
+
const StreamProfilePatchSchema = object({
|
|
7600
|
+
width: number().optional(),
|
|
7601
|
+
height: number().optional(),
|
|
7602
|
+
codec: _enum(["h264", "h265"]).optional(),
|
|
7603
|
+
framerate: number().optional(),
|
|
7604
|
+
bitrate: number().optional(),
|
|
7605
|
+
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
7606
|
+
encoderProfile: _enum(["high", "main", "baseline"]).optional(),
|
|
7607
|
+
gop: number().optional(),
|
|
7608
|
+
audio: boolean().optional()
|
|
7609
|
+
});
|
|
7610
|
+
({
|
|
7611
|
+
deviceTypes: [DeviceType.Camera],
|
|
7612
|
+
methods: {
|
|
7613
|
+
getOptions: method(
|
|
7614
|
+
object({ deviceId: number() }),
|
|
7615
|
+
StreamParamsOptionsSchema
|
|
7616
|
+
),
|
|
7617
|
+
setProfile: method(
|
|
7618
|
+
object({
|
|
7619
|
+
deviceId: number(),
|
|
7620
|
+
profile: StreamProfileSchema,
|
|
7621
|
+
patch: StreamProfilePatchSchema
|
|
7622
|
+
}),
|
|
7623
|
+
_void(),
|
|
7624
|
+
{ kind: "mutation", auth: "admin" }
|
|
7625
|
+
),
|
|
7626
|
+
/**
|
|
7627
|
+
* Build the `ConfigUISchema` (admin-ui `ConfigFormBuilder` input
|
|
7628
|
+
* shape) for this camera's stream-encoder settings — one section per
|
|
7629
|
+
* profile (main / sub / ext) with the resolution / codec / framerate
|
|
7630
|
+
* / bitrate / bitrate-mode / encoder-profile / GOP controls the
|
|
7631
|
+
* firmware actually exposes.
|
|
7632
|
+
*
|
|
7633
|
+
* Driven by `getOptions` (camera-probed availability) + `getStatus`
|
|
7634
|
+
* (current per-profile config); each field's `default` is seeded
|
|
7635
|
+
* from the live config so the form renders the camera state in one
|
|
7636
|
+
* pass. Returns `null` when the camera exposes no configurable
|
|
7637
|
+
* stream property — the renderer then shows the unsupported message.
|
|
7638
|
+
*
|
|
7639
|
+
* Output is `z.unknown().nullable()` — the same convention every
|
|
7640
|
+
* other `ConfigUISchema`-returning cap method uses (`device-ops`,
|
|
7641
|
+
* `device-manager`); `ConfigUISchema` is a TS-only type with no
|
|
7642
|
+
* companion Zod schema, and a concrete object would collapse
|
|
7643
|
+
* unrelated AppRouter branches to `unknown` during codegen.
|
|
7644
|
+
*/
|
|
7645
|
+
getConfigSchema: method(
|
|
7646
|
+
object({ deviceId: number() }),
|
|
7647
|
+
unknown().nullable()
|
|
7648
|
+
)
|
|
7649
|
+
}
|
|
7650
|
+
});
|
|
7395
7651
|
object({
|
|
7396
7652
|
on: boolean(),
|
|
7397
7653
|
/** Ms epoch of the last state change. Useful for UI "X minutes ago". */
|
|
@@ -8329,6 +8585,83 @@ const VersionOutputSchema = object({ version: string() });
|
|
|
8329
8585
|
getVersion: method(_void(), VersionOutputSchema)
|
|
8330
8586
|
}
|
|
8331
8587
|
});
|
|
8588
|
+
const MethodAccessSchema = _enum(["view", "create", "delete"]);
|
|
8589
|
+
const AllowedProviderSchema = union([literal("*"), array(string())]);
|
|
8590
|
+
const AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
|
|
8591
|
+
const CapScopeSchema = _enum(["device", "system"]);
|
|
8592
|
+
const TokenScopeSchema = discriminatedUnion("type", [
|
|
8593
|
+
object({
|
|
8594
|
+
type: literal("category"),
|
|
8595
|
+
target: CapScopeSchema,
|
|
8596
|
+
access: array(MethodAccessSchema).min(1)
|
|
8597
|
+
}),
|
|
8598
|
+
object({
|
|
8599
|
+
type: literal("capability"),
|
|
8600
|
+
target: string(),
|
|
8601
|
+
access: array(MethodAccessSchema).min(1)
|
|
8602
|
+
}),
|
|
8603
|
+
object({
|
|
8604
|
+
type: literal("addon"),
|
|
8605
|
+
target: string(),
|
|
8606
|
+
access: array(MethodAccessSchema).min(1)
|
|
8607
|
+
}),
|
|
8608
|
+
object({
|
|
8609
|
+
type: literal("device"),
|
|
8610
|
+
/**
|
|
8611
|
+
* One or more deviceIds (serialised as strings for wire-format
|
|
8612
|
+
* consistency with the rest of the union). Matcher accepts if
|
|
8613
|
+
* `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
|
|
8614
|
+
* of one scope-per-device when granting access to a set of cameras.
|
|
8615
|
+
*/
|
|
8616
|
+
targets: array(string()).min(1),
|
|
8617
|
+
access: array(MethodAccessSchema).min(1)
|
|
8618
|
+
})
|
|
8619
|
+
]);
|
|
8620
|
+
object({
|
|
8621
|
+
id: string(),
|
|
8622
|
+
username: string(),
|
|
8623
|
+
passwordHash: string(),
|
|
8624
|
+
/**
|
|
8625
|
+
* Admin bypass. When true, the middleware skips the scope-access
|
|
8626
|
+
* check entirely. There is no other axis of privilege; the legacy
|
|
8627
|
+
* role enum collapsed onto this boolean in v2.
|
|
8628
|
+
*/
|
|
8629
|
+
isAdmin: boolean().default(false),
|
|
8630
|
+
allowedProviders: AllowedProviderSchema,
|
|
8631
|
+
allowedDevices: AllowedDevicesSchema,
|
|
8632
|
+
/**
|
|
8633
|
+
* Scopes granted to this user. Admins bypass; their `scopes` is
|
|
8634
|
+
* ignored. Non-admins without scopes are locked out of every
|
|
8635
|
+
* protected call.
|
|
8636
|
+
*/
|
|
8637
|
+
scopes: array(TokenScopeSchema).default([]),
|
|
8638
|
+
createdAt: number(),
|
|
8639
|
+
updatedAt: number()
|
|
8640
|
+
});
|
|
8641
|
+
object({
|
|
8642
|
+
id: string(),
|
|
8643
|
+
label: string(),
|
|
8644
|
+
isAdmin: boolean().default(false),
|
|
8645
|
+
allowedProviders: AllowedProviderSchema,
|
|
8646
|
+
allowedDevices: AllowedDevicesSchema,
|
|
8647
|
+
tokenHash: string(),
|
|
8648
|
+
tokenPrefix: string(),
|
|
8649
|
+
createdAt: number(),
|
|
8650
|
+
lastUsedAt: number().optional()
|
|
8651
|
+
});
|
|
8652
|
+
object({
|
|
8653
|
+
id: string(),
|
|
8654
|
+
userId: string(),
|
|
8655
|
+
name: string(),
|
|
8656
|
+
tokenHash: string(),
|
|
8657
|
+
tokenPrefix: string(),
|
|
8658
|
+
scopes: array(TokenScopeSchema),
|
|
8659
|
+
// SQLite/JSON storage round-trips undefined → null. Use `nullish` so the
|
|
8660
|
+
// schema accepts both `null` (read from disk) and `undefined` (in-memory).
|
|
8661
|
+
expiresAt: number().nullish(),
|
|
8662
|
+
lastUsedAt: number().nullish(),
|
|
8663
|
+
createdAt: number()
|
|
8664
|
+
});
|
|
8332
8665
|
const SsoBridgeClaimsSchema = object({
|
|
8333
8666
|
userId: string(),
|
|
8334
8667
|
username: string(),
|
|
@@ -8344,7 +8677,18 @@ const SsoBridgeClaimsSchema = object({
|
|
|
8344
8677
|
* JWT WITHOUT verifying the signature — the hub re-verifies on every
|
|
8345
8678
|
* inbound call so trust still rests with the signing hub.
|
|
8346
8679
|
*/
|
|
8347
|
-
hubUrl: string().optional()
|
|
8680
|
+
hubUrl: string().optional(),
|
|
8681
|
+
/** Permission scopes baked into the token. Set by the OAuth
|
|
8682
|
+
* account-linking grant; absent on ordinary SSO-login tokens. */
|
|
8683
|
+
scopes: array(TokenScopeSchema).optional(),
|
|
8684
|
+
/** OAuth authorization-code binding — set only on `oauth-code` tokens. */
|
|
8685
|
+
redirectUri: string().optional(),
|
|
8686
|
+
integrationId: string().optional(),
|
|
8687
|
+
/** JWT ID — unique per issued code; consumed-set enforces single-use. */
|
|
8688
|
+
jti: string().optional(),
|
|
8689
|
+
/** OAuth session registry id — set on `oauth-access`/`oauth-refresh`
|
|
8690
|
+
* tokens so the verify path can check the session is not revoked. */
|
|
8691
|
+
sessionId: string().optional()
|
|
8348
8692
|
});
|
|
8349
8693
|
({
|
|
8350
8694
|
methods: {
|
|
@@ -8361,6 +8705,23 @@ const SsoBridgeClaimsSchema = object({
|
|
|
8361
8705
|
)
|
|
8362
8706
|
}
|
|
8363
8707
|
});
|
|
8708
|
+
const OauthIntegrationDescriptorSchema = object({
|
|
8709
|
+
/** Stable id used as the `integration=` query param, e.g. 'export-alexa'. */
|
|
8710
|
+
integrationId: string(),
|
|
8711
|
+
/** Human label rendered on the consent page. */
|
|
8712
|
+
displayName: string(),
|
|
8713
|
+
/** Scopes baked into every token issued for this integration. */
|
|
8714
|
+
requestedScopes: array(TokenScopeSchema),
|
|
8715
|
+
/** Allowed redirect_uri prefixes. /api/oauth2/authorize rejects any
|
|
8716
|
+
* redirect_uri that does not start with one of these. Required —
|
|
8717
|
+
* an empty list means the integration can never complete linking. */
|
|
8718
|
+
allowedRedirectPrefixes: array(string()).min(1)
|
|
8719
|
+
});
|
|
8720
|
+
({
|
|
8721
|
+
methods: {
|
|
8722
|
+
getDescriptor: method(_void(), OauthIntegrationDescriptorSchema)
|
|
8723
|
+
}
|
|
8724
|
+
});
|
|
8364
8725
|
const PasskeySummarySchema = object({
|
|
8365
8726
|
credentialId: string(),
|
|
8366
8727
|
label: string(),
|
|
@@ -8641,21 +9002,30 @@ const AddonPageDeclarationSchema = object({
|
|
|
8641
9002
|
});
|
|
8642
9003
|
const WidgetHostEnum = _enum(["device-tab", "dashboard", "integration-detail"]);
|
|
8643
9004
|
const WidgetSizeEnum = _enum(["xs", "sm", "md", "lg", "xl"]);
|
|
9005
|
+
const WidgetRemoteSchema = object({
|
|
9006
|
+
remoteName: string(),
|
|
9007
|
+
exposedModule: string(),
|
|
9008
|
+
componentKey: string().optional()
|
|
9009
|
+
});
|
|
8644
9010
|
const WidgetMetadataSchema = object({
|
|
8645
|
-
|
|
8646
|
-
|
|
9011
|
+
// ── UiContribution core (kind:'remote') ──────────────────────────
|
|
9012
|
+
/** Primary host tab — `'dashboard'`, `'device-tab'`, or a device-detail tab id. */
|
|
9013
|
+
tab: string(),
|
|
9014
|
+
/** Optional sub-tab within `tab`. */
|
|
9015
|
+
subTab: string().optional(),
|
|
8647
9016
|
/** Operator-facing label. */
|
|
8648
9017
|
label: string(),
|
|
9018
|
+
/** Ordering within `(tab, subTab)`, ascending. */
|
|
9019
|
+
order: number().optional(),
|
|
9020
|
+
/** Always `'remote'` — a widget is a Module Federation remote. */
|
|
9021
|
+
kind: literal("remote"),
|
|
9022
|
+
/** MF remote descriptor. */
|
|
9023
|
+
remote: WidgetRemoteSchema,
|
|
9024
|
+
// ── Widget-only metadata ─────────────────────────────────────────
|
|
9025
|
+
/** Stable id within the addon — kebab-case. Equals `remote.componentKey`. */
|
|
9026
|
+
stableId: string(),
|
|
8649
9027
|
description: string().optional(),
|
|
8650
9028
|
icon: string().optional(),
|
|
8651
|
-
/**
|
|
8652
|
-
* Module Federation remote name — must match the `name` field on the
|
|
8653
|
-
* widget addon's `federation()` plugin config. Used by the host's
|
|
8654
|
-
* `<WidgetRegistryProvider>` to call `loadRemote('<remoteName>/widgets')`.
|
|
8655
|
-
* Conventionally `addon_<addonid>_widgets` (snake_case; MF names
|
|
8656
|
-
* cannot contain hyphens).
|
|
8657
|
-
*/
|
|
8658
|
-
remoteName: string(),
|
|
8659
9029
|
/**
|
|
8660
9030
|
* Bundle filename inside the addon's `dist/` dir served at
|
|
8661
9031
|
* `/api/addon-widgets/<addonId>/<bundle>`. With Module Federation
|
|
@@ -8664,9 +9034,9 @@ const WidgetMetadataSchema = object({
|
|
|
8664
9034
|
* cache-buster URL without a separate filesystem stat.
|
|
8665
9035
|
*/
|
|
8666
9036
|
bundle: string(),
|
|
8667
|
-
/**
|
|
9037
|
+
/** Every host the widget supports. The picker filters on this set. */
|
|
8668
9038
|
hosts: array(WidgetHostEnum).readonly(),
|
|
8669
|
-
/** Required props the host must supply. Validated at
|
|
9039
|
+
/** Required props the host must supply. Validated at `<WidgetSlot>` mount. */
|
|
8670
9040
|
requires: object({
|
|
8671
9041
|
deviceContext: boolean().default(false),
|
|
8672
9042
|
integrationContext: boolean().default(false)
|
|
@@ -8759,6 +9129,16 @@ const DEFAULT_DECODER_HWACCEL_CONFIG = {
|
|
|
8759
9129
|
hwaccel: "auto",
|
|
8760
9130
|
probedBestHwaccel: ""
|
|
8761
9131
|
};
|
|
9132
|
+
const ShmRingStatsSchema = object({
|
|
9133
|
+
sessionId: string(),
|
|
9134
|
+
slotCount: number().int(),
|
|
9135
|
+
slotByteLength: number().int(),
|
|
9136
|
+
segmentBytes: number().int(),
|
|
9137
|
+
budgetMb: number().int(),
|
|
9138
|
+
framesWritten: number().int(),
|
|
9139
|
+
getFrameHits: number().int(),
|
|
9140
|
+
getFrameMisses: number().int()
|
|
9141
|
+
});
|
|
8762
9142
|
const decoderCapability = {
|
|
8763
9143
|
name: "decoder",
|
|
8764
9144
|
scope: "system",
|
|
@@ -8795,10 +9175,27 @@ const decoderCapability = {
|
|
|
8795
9175
|
url: string()
|
|
8796
9176
|
}), _void()),
|
|
8797
9177
|
// ── Output — polling-based frame retrieval ────────────────────
|
|
9178
|
+
// `pullFrames` drains the pixel `DecodedFrame[]` of a `frameSink:
|
|
9179
|
+
// 'callback'` session. `pullHandles` (Phase 5 / D9) drains the
|
|
9180
|
+
// zero-pixel `FrameHandle[]` of a `frameSink: 'shm'` session — the
|
|
9181
|
+
// broker hands each handle to a `FrameRingReader` that opens the
|
|
9182
|
+
// named segment and reads the pixels back zero-copy. A session is
|
|
9183
|
+
// one mode or the other; the unmatched method returns an empty
|
|
9184
|
+
// array.
|
|
8798
9185
|
pullFrames: method(object({
|
|
8799
9186
|
sessionId: string(),
|
|
8800
9187
|
maxCount: number().default(1)
|
|
8801
9188
|
}), array(DecodedFrameSchema)),
|
|
9189
|
+
pullHandles: method(object({
|
|
9190
|
+
sessionId: string(),
|
|
9191
|
+
maxCount: number().default(1)
|
|
9192
|
+
}), array(FrameHandleSchema)),
|
|
9193
|
+
// ── Frame fetch (Phase 5 / D9 downstream access) ──────────────
|
|
9194
|
+
// Read the pixels a FrameHandle refers to from this node's shm ring.
|
|
9195
|
+
// Returns null when the slot was already recycled (latest-wins).
|
|
9196
|
+
getFrame: method(object({ handle: FrameHandleSchema }), DecodedFrameSchema.nullable()),
|
|
9197
|
+
// shm ring usage stats for a session.
|
|
9198
|
+
getShmStats: method(object({ sessionId: string() }), ShmRingStatsSchema.nullable()),
|
|
8802
9199
|
// ── Control ───────────────────────────────────────────────────
|
|
8803
9200
|
updateConfig: method(object({
|
|
8804
9201
|
sessionId: string(),
|
|
@@ -8943,6 +9340,8 @@ const webrtcSessionCapability = {
|
|
|
8943
9340
|
name: "webrtc-session",
|
|
8944
9341
|
scope: "device",
|
|
8945
9342
|
mode: "singleton",
|
|
9343
|
+
kind: "wrapper",
|
|
9344
|
+
defaultActive: true,
|
|
8946
9345
|
deviceTypes: [DeviceType.Camera],
|
|
8947
9346
|
methods: {
|
|
8948
9347
|
/**
|
|
@@ -9457,6 +9856,8 @@ const audioAnalysisCapability = {
|
|
|
9457
9856
|
name: "audio-analysis",
|
|
9458
9857
|
scope: "device",
|
|
9459
9858
|
mode: "singleton",
|
|
9859
|
+
kind: "wrapper",
|
|
9860
|
+
defaultActive: true,
|
|
9460
9861
|
deviceTypes: [DeviceType.Camera],
|
|
9461
9862
|
exposesDeviceSettings: true,
|
|
9462
9863
|
methods: {
|
|
@@ -9989,8 +10390,8 @@ const DevicePersistConfigPayloadSchema = object({
|
|
|
9989
10390
|
/**
|
|
9990
10391
|
* Return the addon ids that declared a wrapper provider for `capName`.
|
|
9991
10392
|
* Backs the device-bindings UI's wrapper-picker dropdown. Entries are
|
|
9992
|
-
* sourced from `CapabilityRegistry.wrapperProviders`, populated
|
|
9993
|
-
* `
|
|
10393
|
+
* sourced from `CapabilityRegistry.wrapperProviders`, populated when
|
|
10394
|
+
* the cap definition declares `kind: 'wrapper'`.
|
|
9994
10395
|
*/
|
|
9995
10396
|
listWrappersForCap: method(
|
|
9996
10397
|
object({ capName: string() }),
|
|
@@ -10296,51 +10697,6 @@ const AuthResultSchema = object({
|
|
|
10296
10697
|
validateToken: method(object({ token: string() }), AuthResultSchema.nullable())
|
|
10297
10698
|
}
|
|
10298
10699
|
});
|
|
10299
|
-
const AuthProviderInfoSchema = object({
|
|
10300
|
-
/** Stable id matching the addon id (used for `getLoginUrl({addonId,…})`). */
|
|
10301
|
-
addonId: string(),
|
|
10302
|
-
/**
|
|
10303
|
-
* Per-instance id when one addon registers multiple "logical"
|
|
10304
|
-
* providers (e.g. OIDC with Google + Microsoft + custom). The login
|
|
10305
|
-
* URL becomes `/addon/${addonId}/${instanceId}/start` — handler reads
|
|
10306
|
-
* `:instanceId` from the route. Empty/unset means the addon is a
|
|
10307
|
-
* single-instance provider; the URL is `/addon/${addonId}/start`.
|
|
10308
|
-
*/
|
|
10309
|
-
instanceId: string().optional(),
|
|
10310
|
-
/** Display label shown on the login button + admin row. */
|
|
10311
|
-
displayName: string(),
|
|
10312
|
-
/** Optional iconography hint (lucide-react icon name OR emoji). */
|
|
10313
|
-
icon: string().optional(),
|
|
10314
|
-
/** When true, the provider exposes a redirect-based login flow
|
|
10315
|
-
* (`getLoginUrl` returns a URL the browser navigates to). */
|
|
10316
|
-
hasRedirectFlow: boolean(),
|
|
10317
|
-
/** When true, the provider exposes a credential-form login flow
|
|
10318
|
-
* (`validateCredentials` accepts username + password). */
|
|
10319
|
-
hasCredentialFlow: boolean(),
|
|
10320
|
-
/** Provider kind, drives admin-UI hint dispatch (oidc / saml / totp / …). */
|
|
10321
|
-
kind: string().optional(),
|
|
10322
|
-
/** Operator-facing status string (e.g. "Connected to https://login.acme.com"). */
|
|
10323
|
-
status: string().optional(),
|
|
10324
|
-
/** When false, the provider is registered but disabled by config; the
|
|
10325
|
-
* UI surfaces it as inactive without enumerating it for login. */
|
|
10326
|
-
enabled: boolean()
|
|
10327
|
-
});
|
|
10328
|
-
({
|
|
10329
|
-
methods: {
|
|
10330
|
-
/** All registered auth providers, both enabled and disabled. */
|
|
10331
|
-
listProviders: method(_void(), array(AuthProviderInfoSchema).readonly()),
|
|
10332
|
-
/**
|
|
10333
|
-
* Toggle a provider's enabled flag. Disabled providers stay
|
|
10334
|
-
* registered but aren't surfaced on the login page. The orchestrator
|
|
10335
|
-
* persists the state in `addon-settings` so it survives restarts.
|
|
10336
|
-
*/
|
|
10337
|
-
setProviderEnabled: method(
|
|
10338
|
-
object({ addonId: string(), enabled: boolean() }),
|
|
10339
|
-
object({ success: literal(true) }),
|
|
10340
|
-
{ kind: "mutation", auth: "admin" }
|
|
10341
|
-
)
|
|
10342
|
-
}
|
|
10343
|
-
});
|
|
10344
10700
|
const NetworkEndpointSchema = object({
|
|
10345
10701
|
url: string(),
|
|
10346
10702
|
hostname: string(),
|
|
@@ -10372,55 +10728,13 @@ const NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
|
|
|
10372
10728
|
getEndpoint: method(_void(), NetworkEndpointSchema.nullable()),
|
|
10373
10729
|
getStatus: method(_void(), NetworkAccessStatusSchema),
|
|
10374
10730
|
/**
|
|
10375
|
-
* Enumerate every active ingress entry.
|
|
10376
|
-
*
|
|
10377
|
-
*
|
|
10731
|
+
* Enumerate every active ingress entry. Providers that expose only a
|
|
10732
|
+
* single endpoint may omit this method; callers fall back to
|
|
10733
|
+
* `getEndpoint()` in that case.
|
|
10378
10734
|
*/
|
|
10379
10735
|
listEndpoints: method(_void(), array(NetworkEndpointEntrySchema).readonly())
|
|
10380
10736
|
}
|
|
10381
10737
|
});
|
|
10382
|
-
const RemoteAccessEndpointSchema = object({
|
|
10383
|
-
url: string(),
|
|
10384
|
-
hostname: string(),
|
|
10385
|
-
port: number(),
|
|
10386
|
-
protocol: _enum(["http", "https"])
|
|
10387
|
-
});
|
|
10388
|
-
const RemoteAccessProviderInfoSchema = object({
|
|
10389
|
-
/** Stable id matching the addon id. */
|
|
10390
|
-
addonId: string(),
|
|
10391
|
-
/** Display label shown on the admin row — sourced from the addon manifest. */
|
|
10392
|
-
displayName: string(),
|
|
10393
|
-
/** When false, the provider is registered but disabled. */
|
|
10394
|
-
enabled: boolean(),
|
|
10395
|
-
/** True when the underlying tunnel/connection is up. */
|
|
10396
|
-
connected: boolean(),
|
|
10397
|
-
/** Public-facing endpoint, when connected. Null otherwise. */
|
|
10398
|
-
endpoint: RemoteAccessEndpointSchema.nullable(),
|
|
10399
|
-
/** Last error message (when connected=false), if available. */
|
|
10400
|
-
error: string().optional()
|
|
10401
|
-
});
|
|
10402
|
-
({
|
|
10403
|
-
methods: {
|
|
10404
|
-
/** All registered remote-access providers + their live status. */
|
|
10405
|
-
listProviders: method(_void(), array(RemoteAccessProviderInfoSchema).readonly()),
|
|
10406
|
-
/**
|
|
10407
|
-
* Start a specific provider's tunnel. Per-provider config still
|
|
10408
|
-
* lives on the addon's settings panel; this is just the on/off
|
|
10409
|
-
* trigger so the admin UI can manage the lifecycle from one place.
|
|
10410
|
-
*/
|
|
10411
|
-
startProvider: method(
|
|
10412
|
-
object({ addonId: string() }),
|
|
10413
|
-
RemoteAccessEndpointSchema,
|
|
10414
|
-
{ kind: "mutation", auth: "admin" }
|
|
10415
|
-
),
|
|
10416
|
-
/** Stop a specific provider's tunnel (idempotent on already-stopped). */
|
|
10417
|
-
stopProvider: method(
|
|
10418
|
-
object({ addonId: string() }),
|
|
10419
|
-
object({ success: literal(true) }),
|
|
10420
|
-
{ kind: "mutation", auth: "admin" }
|
|
10421
|
-
)
|
|
10422
|
-
}
|
|
10423
|
-
});
|
|
10424
10738
|
const TurnServerSchema = object({
|
|
10425
10739
|
/** Single URL or list of URLs (e.g. "turn:turn.example.com:3478?transport=udp"). */
|
|
10426
10740
|
urls: union([string(), array(string())]),
|
|
@@ -10440,45 +10754,6 @@ const TurnServerSchema = object({
|
|
|
10440
10754
|
)
|
|
10441
10755
|
}
|
|
10442
10756
|
});
|
|
10443
|
-
const TurnProviderInfoSchema = object({
|
|
10444
|
-
/** Stable id matching the addon id. */
|
|
10445
|
-
addonId: string(),
|
|
10446
|
-
/** Display label shown on the admin row — sourced from the addon manifest. */
|
|
10447
|
-
displayName: string(),
|
|
10448
|
-
/** When false, the provider is registered but disabled. */
|
|
10449
|
-
enabled: boolean(),
|
|
10450
|
-
/** Number of servers this provider is currently exposing. */
|
|
10451
|
-
serverCount: number(),
|
|
10452
|
-
/**
|
|
10453
|
-
* Flat list of every TURN/STUN URL this provider currently exposes.
|
|
10454
|
-
* One row per URL (multi-URL ICE server entries are flattened). The
|
|
10455
|
-
* admin UI shows this in a compact per-provider list so operators
|
|
10456
|
-
* can verify what's actually being negotiated without having to dig
|
|
10457
|
-
* into the combined `getAllServers` output.
|
|
10458
|
-
*/
|
|
10459
|
-
urls: array(string()).readonly(),
|
|
10460
|
-
/** Last fetch error (when serverCount=0 due to API failure), if any. */
|
|
10461
|
-
error: string().optional()
|
|
10462
|
-
});
|
|
10463
|
-
({
|
|
10464
|
-
methods: {
|
|
10465
|
-
/** All registered TURN providers + per-provider stats. */
|
|
10466
|
-
listProviders: method(_void(), array(TurnProviderInfoSchema).readonly()),
|
|
10467
|
-
/**
|
|
10468
|
-
* Combined list of TURN/STUN servers from all ENABLED providers.
|
|
10469
|
-
* Consumed by the WebRTC layer at session-creation time —
|
|
10470
|
-
* implementations may fetch fresh short-lived credentials each
|
|
10471
|
-
* call (e.g. Cloudflare API), so consumers SHOULD call per-session.
|
|
10472
|
-
*/
|
|
10473
|
-
getAllServers: method(_void(), array(TurnServerSchema).readonly()),
|
|
10474
|
-
/** Toggle a provider's enabled flag. */
|
|
10475
|
-
setProviderEnabled: method(
|
|
10476
|
-
object({ addonId: string(), enabled: boolean() }),
|
|
10477
|
-
object({ success: literal(true) }),
|
|
10478
|
-
{ kind: "mutation", auth: "admin" }
|
|
10479
|
-
)
|
|
10480
|
-
}
|
|
10481
|
-
});
|
|
10482
10757
|
const SnapshotImageSchema = object({
|
|
10483
10758
|
base64: string(),
|
|
10484
10759
|
contentType: string()
|
|
@@ -10821,6 +11096,8 @@ const detectionPipelineCapability = {
|
|
|
10821
11096
|
name: "detection-pipeline",
|
|
10822
11097
|
scope: "device",
|
|
10823
11098
|
mode: "singleton",
|
|
11099
|
+
kind: "wrapper",
|
|
11100
|
+
defaultActive: true,
|
|
10824
11101
|
deviceTypes: [DeviceType.Camera],
|
|
10825
11102
|
exposesDeviceSettings: true,
|
|
10826
11103
|
methods: {}
|
|
@@ -11211,6 +11488,18 @@ const PtzMoveCommandSchema = object({
|
|
|
11211
11488
|
zoom: number().optional(),
|
|
11212
11489
|
speed: number().optional()
|
|
11213
11490
|
});
|
|
11491
|
+
PtzPositionSchema.extend({ autofocus: boolean() });
|
|
11492
|
+
const PtzOptionsSchema = object({
|
|
11493
|
+
hasPan: boolean(),
|
|
11494
|
+
hasTilt: boolean(),
|
|
11495
|
+
hasZoom: boolean(),
|
|
11496
|
+
supportsPresets: boolean(),
|
|
11497
|
+
/** Max number of named presets the camera supports, when known. */
|
|
11498
|
+
maxPresets: number().optional(),
|
|
11499
|
+
/** Whether the camera exposes a controllable autofocus toggle
|
|
11500
|
+
* (boolean `hasX` per the getOptions availability convention). */
|
|
11501
|
+
hasAutofocus: boolean()
|
|
11502
|
+
});
|
|
11214
11503
|
({
|
|
11215
11504
|
deviceTypes: [DeviceType.Camera],
|
|
11216
11505
|
methods: {
|
|
@@ -11238,6 +11527,20 @@ const PtzMoveCommandSchema = object({
|
|
|
11238
11527
|
_void(),
|
|
11239
11528
|
{ kind: "mutation" }
|
|
11240
11529
|
),
|
|
11530
|
+
savePreset: method(
|
|
11531
|
+
object({ deviceId: number(), presetId: string(), name: string() }),
|
|
11532
|
+
_void(),
|
|
11533
|
+
{ kind: "mutation", auth: "admin" }
|
|
11534
|
+
),
|
|
11535
|
+
deletePreset: method(
|
|
11536
|
+
object({ deviceId: number(), presetId: string() }),
|
|
11537
|
+
_void(),
|
|
11538
|
+
{ kind: "mutation", auth: "admin" }
|
|
11539
|
+
),
|
|
11540
|
+
getOptions: method(
|
|
11541
|
+
object({ deviceId: number() }),
|
|
11542
|
+
PtzOptionsSchema
|
|
11543
|
+
),
|
|
11241
11544
|
goHome: method(
|
|
11242
11545
|
object({ deviceId: number() }),
|
|
11243
11546
|
_void(),
|
|
@@ -11252,6 +11555,13 @@ const PtzMoveCommandSchema = object({
|
|
|
11252
11555
|
getPosition: method(
|
|
11253
11556
|
object({ deviceId: number() }),
|
|
11254
11557
|
PtzPositionSchema
|
|
11558
|
+
),
|
|
11559
|
+
/** Toggle the camera's autofocus. Only meaningful when
|
|
11560
|
+
* `getOptions().hasAutofocus` is true. */
|
|
11561
|
+
setAutofocus: method(
|
|
11562
|
+
object({ deviceId: number(), enabled: boolean() }),
|
|
11563
|
+
_void(),
|
|
11564
|
+
{ kind: "mutation" }
|
|
11255
11565
|
)
|
|
11256
11566
|
}
|
|
11257
11567
|
});
|
|
@@ -11951,7 +12261,7 @@ const AllowedAddressesSchema = object({
|
|
|
11951
12261
|
)
|
|
11952
12262
|
}
|
|
11953
12263
|
});
|
|
11954
|
-
const MeshEndpointSchema
|
|
12264
|
+
const MeshEndpointSchema = object({
|
|
11955
12265
|
/** Stable identifier within the provider (e.g. `mesh-ipv4`, `magicdns`, `funnel`). */
|
|
11956
12266
|
id: string(),
|
|
11957
12267
|
/** Operator-facing label (e.g. "Mesh IPv4", "MagicDNS"). */
|
|
@@ -12024,7 +12334,7 @@ const MeshStatusSchema = object({
|
|
|
12024
12334
|
/** Number of peers visible to this host (excluding self). */
|
|
12025
12335
|
peerCount: number(),
|
|
12026
12336
|
/** Every endpoint this provider exposes for the current host. */
|
|
12027
|
-
endpoints: array(MeshEndpointSchema
|
|
12337
|
+
endpoints: array(MeshEndpointSchema).readonly(),
|
|
12028
12338
|
/** Last error from the daemon, when not joined. */
|
|
12029
12339
|
error: string().optional(),
|
|
12030
12340
|
// ── Account / tenant identity (generic across providers) ────────
|
|
@@ -12187,182 +12497,6 @@ const MeshStatusSchema = object({
|
|
|
12187
12497
|
// tabs driven by this cap.
|
|
12188
12498
|
}
|
|
12189
12499
|
});
|
|
12190
|
-
const MeshEndpointSchema = object({
|
|
12191
|
-
id: string(),
|
|
12192
|
-
label: string(),
|
|
12193
|
-
scope: _enum(["mesh", "public"]),
|
|
12194
|
-
url: string(),
|
|
12195
|
-
hostname: string(),
|
|
12196
|
-
port: number(),
|
|
12197
|
-
protocol: _enum(["http", "https"])
|
|
12198
|
-
});
|
|
12199
|
-
const MeshProviderInfoSchema = object({
|
|
12200
|
-
/** Stable id matching the addon id. */
|
|
12201
|
-
addonId: string(),
|
|
12202
|
-
/** Display label shown on the admin row — sourced from the addon manifest. */
|
|
12203
|
-
displayName: string(),
|
|
12204
|
-
/** True when the host is joined to this provider's mesh. */
|
|
12205
|
-
joined: boolean(),
|
|
12206
|
-
/** Local mesh IP (empty when not joined). */
|
|
12207
|
-
meshIp: string(),
|
|
12208
|
-
/** MagicDNS / mesh hostname (empty when not configured). */
|
|
12209
|
-
magicDnsHostname: string(),
|
|
12210
|
-
/** Peer count (excluding self). */
|
|
12211
|
-
peerCount: number(),
|
|
12212
|
-
/** Active endpoints (mesh IP + MagicDNS + optional public Funnel). */
|
|
12213
|
-
endpoints: array(MeshEndpointSchema).readonly(),
|
|
12214
|
-
/** Last error reported by the provider. */
|
|
12215
|
-
error: string().optional(),
|
|
12216
|
-
// ── Generic identity fields mirrored from MeshStatus ─────────────
|
|
12217
|
-
/** Tenant / tailnet / network display name. Empty pre-join. */
|
|
12218
|
-
tenantName: string(),
|
|
12219
|
-
/** Mesh DNS suffix (e.g. tailXXXX.ts.net). Empty when not configured. */
|
|
12220
|
-
magicDnsSuffix: string(),
|
|
12221
|
-
/** Authenticated user / account login. Null for token-only providers. */
|
|
12222
|
-
userLogin: string().nullable(),
|
|
12223
|
-
/** Provider control-plane URL. */
|
|
12224
|
-
controlPlaneUrl: string(),
|
|
12225
|
-
/** Machine-key expiry (epoch ms). Null when keys don't rotate. */
|
|
12226
|
-
keyExpiry: number().nullable()
|
|
12227
|
-
});
|
|
12228
|
-
({
|
|
12229
|
-
methods: {
|
|
12230
|
-
/** All registered mesh-network providers + live status. */
|
|
12231
|
-
listProviders: method(_void(), array(MeshProviderInfoSchema).readonly()),
|
|
12232
|
-
/**
|
|
12233
|
-
* Join the mesh of a specific provider. Per-provider config still
|
|
12234
|
-
* lives on its settings panel; the orchestrator forwards.
|
|
12235
|
-
*/
|
|
12236
|
-
joinProvider: method(
|
|
12237
|
-
object({
|
|
12238
|
-
addonId: string(),
|
|
12239
|
-
authKey: string().min(8),
|
|
12240
|
-
hostname: string().optional()
|
|
12241
|
-
}),
|
|
12242
|
-
object({ joined: literal(true) }),
|
|
12243
|
-
{ kind: "mutation" }
|
|
12244
|
-
),
|
|
12245
|
-
leaveProvider: method(
|
|
12246
|
-
object({ addonId: string() }),
|
|
12247
|
-
object({ success: literal(true) }),
|
|
12248
|
-
{ kind: "mutation" }
|
|
12249
|
-
),
|
|
12250
|
-
/**
|
|
12251
|
-
* Browser-redirect login flow. Forwards to the named provider's
|
|
12252
|
-
* `mesh-network.startLogin` and returns the URL the daemon
|
|
12253
|
-
* prints. UI opens it in a new tab, then polls `listProviders`
|
|
12254
|
-
* for `joined: true`.
|
|
12255
|
-
*/
|
|
12256
|
-
startLoginProvider: method(
|
|
12257
|
-
object({
|
|
12258
|
-
addonId: string(),
|
|
12259
|
-
hostname: string().optional()
|
|
12260
|
-
}),
|
|
12261
|
-
object({ loginUrl: string() }),
|
|
12262
|
-
{ kind: "mutation" }
|
|
12263
|
-
),
|
|
12264
|
-
/**
|
|
12265
|
-
* Sign out of the provider's account entirely (`mesh-network.logout`).
|
|
12266
|
-
* Distinct from `leaveProvider` which only takes the host off-mesh;
|
|
12267
|
-
* `logoutProvider` wipes credentials so the next start requires a
|
|
12268
|
-
* fresh login.
|
|
12269
|
-
*/
|
|
12270
|
-
logoutProvider: method(
|
|
12271
|
-
object({ addonId: string() }),
|
|
12272
|
-
object({ loggedOut: literal(true) }),
|
|
12273
|
-
{ kind: "mutation" }
|
|
12274
|
-
),
|
|
12275
|
-
/**
|
|
12276
|
-
* Per-provider peer list. Forwards to `mesh-network.listPeers` on
|
|
12277
|
-
* the addressed provider. Separate from `listProviders` because
|
|
12278
|
-
* peer payloads can be large on a heavily-populated tailnet —
|
|
12279
|
-
* fetch only when the operator opens the Peers tab.
|
|
12280
|
-
*/
|
|
12281
|
-
listProviderPeers: method(
|
|
12282
|
-
object({ addonId: string() }),
|
|
12283
|
-
object({
|
|
12284
|
-
peers: array(MeshPeerSchema).readonly()
|
|
12285
|
-
})
|
|
12286
|
-
)
|
|
12287
|
-
}
|
|
12288
|
-
});
|
|
12289
|
-
const MethodAccessSchema = _enum(["view", "create", "delete"]);
|
|
12290
|
-
const AllowedProviderSchema = union([literal("*"), array(string())]);
|
|
12291
|
-
const AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
|
|
12292
|
-
const CapScopeSchema = _enum(["device", "system"]);
|
|
12293
|
-
const TokenScopeSchema = discriminatedUnion("type", [
|
|
12294
|
-
object({
|
|
12295
|
-
type: literal("category"),
|
|
12296
|
-
target: CapScopeSchema,
|
|
12297
|
-
access: array(MethodAccessSchema).min(1)
|
|
12298
|
-
}),
|
|
12299
|
-
object({
|
|
12300
|
-
type: literal("capability"),
|
|
12301
|
-
target: string(),
|
|
12302
|
-
access: array(MethodAccessSchema).min(1)
|
|
12303
|
-
}),
|
|
12304
|
-
object({
|
|
12305
|
-
type: literal("addon"),
|
|
12306
|
-
target: string(),
|
|
12307
|
-
access: array(MethodAccessSchema).min(1)
|
|
12308
|
-
}),
|
|
12309
|
-
object({
|
|
12310
|
-
type: literal("device"),
|
|
12311
|
-
/**
|
|
12312
|
-
* One or more deviceIds (serialised as strings for wire-format
|
|
12313
|
-
* consistency with the rest of the union). Matcher accepts if
|
|
12314
|
-
* `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
|
|
12315
|
-
* of one scope-per-device when granting access to a set of cameras.
|
|
12316
|
-
*/
|
|
12317
|
-
targets: array(string()).min(1),
|
|
12318
|
-
access: array(MethodAccessSchema).min(1)
|
|
12319
|
-
})
|
|
12320
|
-
]);
|
|
12321
|
-
object({
|
|
12322
|
-
id: string(),
|
|
12323
|
-
username: string(),
|
|
12324
|
-
passwordHash: string(),
|
|
12325
|
-
/**
|
|
12326
|
-
* Admin bypass. When true, the middleware skips the scope-access
|
|
12327
|
-
* check entirely. There is no other axis of privilege; the legacy
|
|
12328
|
-
* role enum collapsed onto this boolean in v2.
|
|
12329
|
-
*/
|
|
12330
|
-
isAdmin: boolean().default(false),
|
|
12331
|
-
allowedProviders: AllowedProviderSchema,
|
|
12332
|
-
allowedDevices: AllowedDevicesSchema,
|
|
12333
|
-
/**
|
|
12334
|
-
* Scopes granted to this user. Admins bypass; their `scopes` is
|
|
12335
|
-
* ignored. Non-admins without scopes are locked out of every
|
|
12336
|
-
* protected call.
|
|
12337
|
-
*/
|
|
12338
|
-
scopes: array(TokenScopeSchema).default([]),
|
|
12339
|
-
createdAt: number(),
|
|
12340
|
-
updatedAt: number()
|
|
12341
|
-
});
|
|
12342
|
-
object({
|
|
12343
|
-
id: string(),
|
|
12344
|
-
label: string(),
|
|
12345
|
-
isAdmin: boolean().default(false),
|
|
12346
|
-
allowedProviders: AllowedProviderSchema,
|
|
12347
|
-
allowedDevices: AllowedDevicesSchema,
|
|
12348
|
-
tokenHash: string(),
|
|
12349
|
-
tokenPrefix: string(),
|
|
12350
|
-
createdAt: number(),
|
|
12351
|
-
lastUsedAt: number().optional()
|
|
12352
|
-
});
|
|
12353
|
-
object({
|
|
12354
|
-
id: string(),
|
|
12355
|
-
userId: string(),
|
|
12356
|
-
name: string(),
|
|
12357
|
-
tokenHash: string(),
|
|
12358
|
-
tokenPrefix: string(),
|
|
12359
|
-
scopes: array(TokenScopeSchema),
|
|
12360
|
-
// SQLite/JSON storage round-trips undefined → null. Use `nullish` so the
|
|
12361
|
-
// schema accepts both `null` (read from disk) and `undefined` (in-memory).
|
|
12362
|
-
expiresAt: number().nullish(),
|
|
12363
|
-
lastUsedAt: number().nullish(),
|
|
12364
|
-
createdAt: number()
|
|
12365
|
-
});
|
|
12366
12500
|
const UserSummarySchema = object({
|
|
12367
12501
|
id: string(),
|
|
12368
12502
|
username: string(),
|
|
@@ -12435,6 +12569,16 @@ const CreateScopedTokenResultSchema = object({
|
|
|
12435
12569
|
token: string(),
|
|
12436
12570
|
record: ScopedTokenSummarySchema
|
|
12437
12571
|
});
|
|
12572
|
+
const OauthSessionSummarySchema = object({
|
|
12573
|
+
id: string(),
|
|
12574
|
+
userId: string(),
|
|
12575
|
+
username: string(),
|
|
12576
|
+
integrationId: string(),
|
|
12577
|
+
scopes: array(TokenScopeSchema),
|
|
12578
|
+
createdAt: number(),
|
|
12579
|
+
lastUsedAt: number(),
|
|
12580
|
+
revokedAt: number().nullable()
|
|
12581
|
+
});
|
|
12438
12582
|
const TotpSetupResultSchema = object({
|
|
12439
12583
|
secret: string(),
|
|
12440
12584
|
otpauthUrl: string()
|
|
@@ -12510,6 +12654,66 @@ const TotpStatusSchema = object({
|
|
|
12510
12654
|
object({ userId: string(), code: string() }),
|
|
12511
12655
|
object({ valid: boolean() }),
|
|
12512
12656
|
{ kind: "mutation", access: "view" }
|
|
12657
|
+
),
|
|
12658
|
+
// ── OAuth account-linking grant ────────────────────────────────
|
|
12659
|
+
//
|
|
12660
|
+
// Core's /oauth2/* endpoints delegate here. Tokens are sso-bridge
|
|
12661
|
+
// JWTs (kinds oauth-code / oauth-access / oauth-refresh) and ALWAYS
|
|
12662
|
+
// carry isAdmin:false — the operator login proves hub control; the
|
|
12663
|
+
// issued token is minimal (device scope only).
|
|
12664
|
+
oauthIssueCode: method(
|
|
12665
|
+
object({
|
|
12666
|
+
integrationId: string(),
|
|
12667
|
+
userId: string(),
|
|
12668
|
+
username: string(),
|
|
12669
|
+
scopes: array(TokenScopeSchema),
|
|
12670
|
+
redirectUri: string(),
|
|
12671
|
+
hubUrl: string()
|
|
12672
|
+
}),
|
|
12673
|
+
object({ code: string() }),
|
|
12674
|
+
{ kind: "mutation", access: "create" }
|
|
12675
|
+
),
|
|
12676
|
+
oauthExchangeCode: method(
|
|
12677
|
+
object({ code: string(), redirectUri: string() }),
|
|
12678
|
+
object({
|
|
12679
|
+
accessToken: string(),
|
|
12680
|
+
refreshToken: string(),
|
|
12681
|
+
expiresIn: number()
|
|
12682
|
+
}).nullable(),
|
|
12683
|
+
{ kind: "mutation", access: "view" }
|
|
12684
|
+
),
|
|
12685
|
+
oauthRefresh: method(
|
|
12686
|
+
object({ refreshToken: string() }),
|
|
12687
|
+
object({
|
|
12688
|
+
accessToken: string(),
|
|
12689
|
+
refreshToken: string(),
|
|
12690
|
+
expiresIn: number()
|
|
12691
|
+
}).nullable(),
|
|
12692
|
+
{ kind: "mutation", access: "view" }
|
|
12693
|
+
),
|
|
12694
|
+
oauthVerifyAccessToken: method(
|
|
12695
|
+
object({ token: string() }),
|
|
12696
|
+
object({
|
|
12697
|
+
userId: string(),
|
|
12698
|
+
username: string(),
|
|
12699
|
+
scopes: array(TokenScopeSchema)
|
|
12700
|
+
}).nullable(),
|
|
12701
|
+
{ access: "view" }
|
|
12702
|
+
),
|
|
12703
|
+
// ── OAuth linked-session management (Phase D) ──────────────────
|
|
12704
|
+
//
|
|
12705
|
+
// The admin UI lists active account-linking sessions and revokes
|
|
12706
|
+
// them; revocation makes the linked integration's tokens fail
|
|
12707
|
+
// verification immediately.
|
|
12708
|
+
listOauthSessions: method(
|
|
12709
|
+
_void(),
|
|
12710
|
+
array(OauthSessionSummarySchema),
|
|
12711
|
+
{ auth: "admin" }
|
|
12712
|
+
),
|
|
12713
|
+
revokeOauthSession: method(
|
|
12714
|
+
object({ id: string() }),
|
|
12715
|
+
object({ success: boolean() }),
|
|
12716
|
+
{ kind: "mutation", auth: "admin", access: "delete" }
|
|
12513
12717
|
)
|
|
12514
12718
|
}
|
|
12515
12719
|
});
|
|
@@ -12726,6 +12930,19 @@ const RenameNodeResultSchema = object({
|
|
|
12726
12930
|
record(string(), ClusterAddonStatusEntrySchema),
|
|
12727
12931
|
{ auth: "admin" }
|
|
12728
12932
|
),
|
|
12933
|
+
getCapUsageGraph: method(
|
|
12934
|
+
object({
|
|
12935
|
+
windowSeconds: number().int().positive().max(300).default(60)
|
|
12936
|
+
}),
|
|
12937
|
+
array(object({
|
|
12938
|
+
callerAddonId: string(),
|
|
12939
|
+
providerAddonId: string(),
|
|
12940
|
+
capName: string(),
|
|
12941
|
+
callsPerMin: number(),
|
|
12942
|
+
lastCallAtMs: number()
|
|
12943
|
+
})).readonly(),
|
|
12944
|
+
{ auth: "admin" }
|
|
12945
|
+
),
|
|
12729
12946
|
/**
|
|
12730
12947
|
* Direct per-node addon listing — calls `$agent.status` on the target
|
|
12731
12948
|
* node (or returns the hub registry for `nodeId === 'hub'`) and surfaces
|
|
@@ -13155,6 +13372,29 @@ const CustomActionInputSchema = object({
|
|
|
13155
13372
|
isActive: boolean()
|
|
13156
13373
|
})).readonly()
|
|
13157
13374
|
),
|
|
13375
|
+
/**
|
|
13376
|
+
* Toggle a single collection-cap provider on/off. Generic write-side
|
|
13377
|
+
* counterpart of `listCapabilityProviders` — drives the per-provider
|
|
13378
|
+
* Enable/Disable affordance in admin pages (TURN servers, etc.)
|
|
13379
|
+
* without needing a bespoke orchestrator cap.
|
|
13380
|
+
*
|
|
13381
|
+
* Reaches the hub's `CapabilityRegistry` directly:
|
|
13382
|
+
* `enableCollectionProvider` / `disableCollectionProvider` flip the
|
|
13383
|
+
* registry-level `disabledProviders` set. `getCollectionEntries`
|
|
13384
|
+
* already filters disabled providers out, so a disabled provider
|
|
13385
|
+
* drops out of every collection aggregate immediately. Only valid
|
|
13386
|
+
* for `mode: 'collection'` caps — the registry no-ops + warns for
|
|
13387
|
+
* singletons.
|
|
13388
|
+
*/
|
|
13389
|
+
setCapabilityProviderEnabled: method(
|
|
13390
|
+
object({
|
|
13391
|
+
capName: string().min(1),
|
|
13392
|
+
addonId: string().min(1),
|
|
13393
|
+
enabled: boolean()
|
|
13394
|
+
}),
|
|
13395
|
+
object({ success: literal(true) }),
|
|
13396
|
+
{ kind: "mutation", auth: "admin" }
|
|
13397
|
+
),
|
|
13158
13398
|
/**
|
|
13159
13399
|
* Live-update one of the framework packages marked
|
|
13160
13400
|
* `camstack.system: true` (`@camstack/types|kernel|core|sdk|ui-library`).
|
|
@@ -14028,4 +14268,4 @@ export {
|
|
|
14028
14268
|
DEFAULT_AUDIO_ANALYZER_CONFIG as y,
|
|
14029
14269
|
AUDIO_BACKEND_CHOICES as z
|
|
14030
14270
|
};
|
|
14031
|
-
//# sourceMappingURL=index-
|
|
14271
|
+
//# sourceMappingURL=index-CVzLrojg.mjs.map
|