@camstack/addon-admin-ui 0.1.35 → 0.1.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{__mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.mjs-CeLhKMin.js → __mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.mjs-Cv6qLu_-.js} +1 -1
- package/dist/assets/{__mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.mjs_commonjs-proxy-DXPQtD9p.js → __mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.mjs_commonjs-proxy-CChEG9e0.js} +1 -1
- package/dist/assets/{__mfe_internal__admin_ui_host__loadShare___mf_0_trpc_mf_1_react_mf_2_query__loadShare__.mjs-OcRSBqRe.js → __mfe_internal__admin_ui_host__loadShare___mf_0_trpc_mf_1_react_mf_2_query__loadShare__.mjs-C1BmHlGM.js} +1 -1
- package/dist/assets/{__mfe_internal__admin_ui_host__loadShare___mf_0_trpc_mf_1_react_mf_2_query__loadShare__.mjs_commonjs-proxy-GvBGLFI2.js → __mfe_internal__admin_ui_host__loadShare___mf_0_trpc_mf_1_react_mf_2_query__loadShare__.mjs_commonjs-proxy-Bxv_3KWz.js} +1 -1
- package/dist/assets/__mfe_internal__admin_ui_host__loadShare__konva__loadShare__.mjs-g85Oidc0.js +15 -0
- package/dist/assets/__mfe_internal__admin_ui_host__loadShare__konva__loadShare__.mjs_commonjs-proxy-Bqeq2Zwj.js +1 -0
- package/dist/assets/{__mfe_internal__admin_ui_host__loadShare__react__loadShare__.mjs-Dl4u0xYE.js → __mfe_internal__admin_ui_host__loadShare__react__loadShare__.mjs-DC63qZbr.js} +1 -1
- package/dist/assets/{__mfe_internal__admin_ui_host__loadShare__react__loadShare__.mjs_commonjs-proxy-CSyOECfO.js → __mfe_internal__admin_ui_host__loadShare__react__loadShare__.mjs_commonjs-proxy-D2C49VyZ.js} +1 -1
- package/dist/assets/{__mfe_internal__admin_ui_host__loadShare__react_mf_2_dom__loadShare__.mjs-CzO_Esxm.js → __mfe_internal__admin_ui_host__loadShare__react_mf_2_dom__loadShare__.mjs-D3WbXy3O.js} +1 -1
- package/dist/assets/{__mfe_internal__admin_ui_host__loadShare__react_mf_2_dom__loadShare__.mjs_commonjs-proxy-xw6c6JeS.js → __mfe_internal__admin_ui_host__loadShare__react_mf_2_dom__loadShare__.mjs_commonjs-proxy-Csg6EbPB.js} +1 -1
- package/dist/assets/{__mfe_internal__admin_ui_host__loadShare__react_mf_2_dom_mf_1_client__loadShare__.mjs-DNpf5SLz.js → __mfe_internal__admin_ui_host__loadShare__react_mf_2_dom_mf_1_client__loadShare__.mjs-CUc0oFNo.js} +2 -2
- package/dist/assets/{__mfe_internal__admin_ui_host__loadShare__react_mf_2_dom_mf_1_client__loadShare__.mjs_commonjs-proxy-BEKtiDI3.js → __mfe_internal__admin_ui_host__loadShare__react_mf_2_dom_mf_1_client__loadShare__.mjs_commonjs-proxy-vh0hhA5d.js} +1 -1
- package/dist/assets/__mfe_internal__admin_ui_host__loadShare__react_mf_2_konva__loadShare__.mjs-CpGtTxci.js +35 -0
- package/dist/assets/__mfe_internal__admin_ui_host__loadShare__react_mf_2_konva__loadShare__.mjs_commonjs-proxy-RUDPE9aM.js +1 -0
- package/dist/assets/_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-Cjyt3AKP.js +1 -0
- package/dist/assets/{devices-D4k34mru.js → devices-GLp7me2a.js} +1 -1
- package/dist/assets/hostInit-Cx72YpvD.js +1 -0
- package/dist/assets/index-ByRJWRUP.js +1 -0
- package/dist/assets/index-COoei6lw.css +1 -0
- package/dist/assets/index-CtHpLu0L.js +1 -0
- package/dist/assets/{index-D92THlot.js → index-CwF7Dxf8.js} +1 -1
- package/dist/assets/{index-BI3Z7SSi.js → index-D6khiudP.js} +1 -1
- package/dist/assets/index-DNY_otk4.js +151 -0
- package/dist/assets/index-DQFO9ZU6.js +1 -0
- package/dist/assets/index-G9qsQFtk.js +1150 -0
- package/dist/assets/{index-BGZqVJZt.js → index-JJ5NLHxC.js} +1 -1
- package/dist/assets/{index-Bwcmc2hW.js → index-TiJwCuZx.js} +1 -1
- package/dist/assets/index-WrAHDGEr.js +87 -0
- package/dist/assets/index-b0D9qthW.js +1 -0
- package/dist/assets/method-access-map-DLEhlu0x.js +1 -0
- package/dist/assets/{remoteEntry-BRybuw2S.js → remoteEntry-CbhqJYoN.js} +1 -1
- package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-DUkp33c_.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-I5siGnQL.js} +6 -6
- package/dist/index.html +6 -6
- package/dist/mf-entry-bootstrap-0.js +2 -2
- package/dist/server/addon.js +528 -96
- package/dist/server/addon.js.map +1 -1
- package/dist/sw.js +1 -1
- package/package.json +10 -2
- package/dist/assets/_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-DYU-qDnu.js +0 -1
- package/dist/assets/hostInit-CBKchP1G.js +0 -1
- package/dist/assets/index-BEewR-UX.js +0 -1
- package/dist/assets/index-BhNfKRLZ.js +0 -1156
- package/dist/assets/index-BqMPfvQx.js +0 -1
- package/dist/assets/index-CP1R6E2R.js +0 -88
- package/dist/assets/index-CwjnPcOV.js +0 -152
- package/dist/assets/index-Dmon2mdO.js +0 -1
- package/dist/assets/index-KlKJOjpC.js +0 -1
- package/dist/assets/index-bBEaoekV.css +0 -1
- package/dist/assets/method-access-map-BFPyQ2-G.js +0 -1
package/dist/server/addon.js
CHANGED
|
@@ -5069,6 +5069,7 @@ const WELL_KNOWN_TABS = [
|
|
|
5069
5069
|
{ id: "osd", label: "OSD", icon: "type", order: 18 },
|
|
5070
5070
|
{ id: "stream-broker", label: "Stream Broker", icon: "radio", order: 20 },
|
|
5071
5071
|
{ id: "streaming", label: "Streaming", icon: "video", order: 35 },
|
|
5072
|
+
{ id: "ptz", label: "PTZ", icon: "move", order: 40 },
|
|
5072
5073
|
{ id: "pipeline", label: "Detection Pipeline", icon: "cpu", order: 39 },
|
|
5073
5074
|
{ id: "zones", label: "Detection", icon: "shapes", order: 38 },
|
|
5074
5075
|
{ id: "live-stats", label: "Live Stats", icon: "activity", order: 39 },
|
|
@@ -5136,6 +5137,9 @@ function hydrateField(field, values) {
|
|
|
5136
5137
|
return { ...field, value: items };
|
|
5137
5138
|
}
|
|
5138
5139
|
const rawValue = storedValue !== void 0 ? storedValue : defaultValue !== void 0 ? defaultValue : null;
|
|
5140
|
+
if (field.type === "password") {
|
|
5141
|
+
return { ...field, value: "" };
|
|
5142
|
+
}
|
|
5139
5143
|
const value = field.type === "textarea" && field.isJson && rawValue !== null && typeof rawValue === "object" ? JSON.stringify(rawValue, null, 2) : rawValue;
|
|
5140
5144
|
const hydrated = { ...field, value };
|
|
5141
5145
|
return hydrated;
|
|
@@ -5222,7 +5226,19 @@ const DecoderSessionConfigSchema = object({
|
|
|
5222
5226
|
* on every line so `grep tag=broker:5/high` filters one camera
|
|
5223
5227
|
* profile cleanly.
|
|
5224
5228
|
*/
|
|
5225
|
-
tag: string().optional()
|
|
5229
|
+
tag: string().optional(),
|
|
5230
|
+
/**
|
|
5231
|
+
* Where the session delivers decoded frames (Phase 5 / D9):
|
|
5232
|
+
*
|
|
5233
|
+
* - `'callback'` (default) — the legacy pixel path: decoded frames are
|
|
5234
|
+
* buffered as `DecodedFrame`s and drained via `pullFrames`.
|
|
5235
|
+
* - `'shm'` — the shared-memory frame plane: decoded frames are written
|
|
5236
|
+
* into an OS shared-memory ring and drained as zero-pixel
|
|
5237
|
+
* `FrameHandle`s via `pullHandles`. A session is one mode or the
|
|
5238
|
+
* other — `pullFrames` returns nothing for an `'shm'` session and
|
|
5239
|
+
* `pullHandles` returns nothing for a `'callback'` session.
|
|
5240
|
+
*/
|
|
5241
|
+
frameSink: _enum(["callback", "shm"]).default("callback")
|
|
5226
5242
|
});
|
|
5227
5243
|
const YAMNET_TO_MACRO = {
|
|
5228
5244
|
mapping: {
|
|
@@ -5952,6 +5968,53 @@ const DecodedFrameSchema = object({
|
|
|
5952
5968
|
format: _enum(["jpeg", "rgb", "bgr", "yuv420", "gray"]),
|
|
5953
5969
|
timestamp: number()
|
|
5954
5970
|
});
|
|
5971
|
+
const FrameHandleSchema = object({
|
|
5972
|
+
shmId: string(),
|
|
5973
|
+
slot: number().int().nonnegative(),
|
|
5974
|
+
seq: number().int().nonnegative(),
|
|
5975
|
+
width: number().int().positive(),
|
|
5976
|
+
height: number().int().positive(),
|
|
5977
|
+
format: _enum(["jpeg", "rgb", "bgr", "yuv420", "gray"]),
|
|
5978
|
+
pts: number(),
|
|
5979
|
+
byteLength: number().int().nonnegative(),
|
|
5980
|
+
nodeId: string(),
|
|
5981
|
+
slotCount: number().int().positive()
|
|
5982
|
+
});
|
|
5983
|
+
const FrameHandleFormatSchema = _enum(["rgb", "bgr", "yuv420", "gray"]);
|
|
5984
|
+
const SubscribeFramesInputSchema = object({
|
|
5985
|
+
brokerId: string(),
|
|
5986
|
+
format: FrameHandleFormatSchema,
|
|
5987
|
+
/**
|
|
5988
|
+
* Optional reader-side cadence hint in frames per second. The broker does
|
|
5989
|
+
* NOT throttle — latest-wins ring reads drop frames implicitly for a slow
|
|
5990
|
+
* consumer. The value is echoed back in `SubscribeFramesResult.maxFps` so
|
|
5991
|
+
* the consumer can pace its own `pullFrameHandles` polling.
|
|
5992
|
+
*/
|
|
5993
|
+
maxFps: number().positive().optional(),
|
|
5994
|
+
/** Short caller-identity tag (`motion`, `detection`, …) for diagnostics. */
|
|
5995
|
+
tag: string().optional()
|
|
5996
|
+
});
|
|
5997
|
+
const SubscribeFramesResultSchema = object({
|
|
5998
|
+
/** Opaque id the consumer passes to `pullFrameHandles` / `unsubscribeFrames`. */
|
|
5999
|
+
subscriptionId: string(),
|
|
6000
|
+
/** Reader-side cadence hint (frames/s) — echoes `SubscribeFramesInput.maxFps`. */
|
|
6001
|
+
maxFps: number().nonnegative()
|
|
6002
|
+
});
|
|
6003
|
+
const DecodedAudioChunkSchema = object({
|
|
6004
|
+
data: _instanceof(Uint8Array),
|
|
6005
|
+
sampleRate: number().int().positive(),
|
|
6006
|
+
channels: number().int().positive(),
|
|
6007
|
+
timestamp: number()
|
|
6008
|
+
});
|
|
6009
|
+
const SubscribeAudioChunksInputSchema = object({
|
|
6010
|
+
brokerId: string(),
|
|
6011
|
+
/** Short caller-identity tag (`audio-analyzer`, …) for `listClients`. */
|
|
6012
|
+
tag: string().optional()
|
|
6013
|
+
});
|
|
6014
|
+
const SubscribeAudioChunksResultSchema = object({
|
|
6015
|
+
/** Opaque id passed to `pullAudioChunks` / `unsubscribeAudioChunks`. */
|
|
6016
|
+
subscriptionId: string()
|
|
6017
|
+
});
|
|
5955
6018
|
const BrokerStatusSchema$1 = _enum(["idle", "connecting", "streaming", "error", "stopped"]);
|
|
5956
6019
|
const BrokerStatsSchema = object({
|
|
5957
6020
|
status: BrokerStatusSchema$1,
|
|
@@ -6173,9 +6236,76 @@ const RtpSourceSchema = object({
|
|
|
6173
6236
|
object({ released: boolean(), refcount: number().int().nonnegative() }),
|
|
6174
6237
|
{ kind: "mutation", auth: "admin" }
|
|
6175
6238
|
),
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6239
|
+
/**
|
|
6240
|
+
* ── Decoded audio-chunk plane (Phase 5 / D9) ──────────────────────
|
|
6241
|
+
*
|
|
6242
|
+
* The serialisable replacement for the live-object `IStreamBroker.
|
|
6243
|
+
* onDecodedAudioChunk` callback path. Unlike the video frame plane,
|
|
6244
|
+
* audio chunks are tiny (a ~500ms PCM window is a few KB) so they ship
|
|
6245
|
+
* their bytes INLINE over tRPC — no shared-memory ring. A consumer:
|
|
6246
|
+
*
|
|
6247
|
+
* 1. `subscribeAudioChunks({ brokerId, tag })` — the broker registers
|
|
6248
|
+
* a per-subscription bounded FIFO queue and returns a
|
|
6249
|
+
* `subscriptionId`.
|
|
6250
|
+
* 2. polls `pullAudioChunks({ subscriptionId, maxCount })` — drains
|
|
6251
|
+
* `DecodedAudioChunk[]` in arrival order (FIFO, no latest-wins
|
|
6252
|
+
* drop: an audio gap is audible / breaks an analysis window). The
|
|
6253
|
+
* queue is generously sized; it only drops its oldest chunk if a
|
|
6254
|
+
* truly stalled consumer lets it overflow.
|
|
6255
|
+
* 3. `unsubscribeAudioChunks({ subscriptionId })` on teardown.
|
|
6256
|
+
*/
|
|
6257
|
+
subscribeAudioChunks: method(
|
|
6258
|
+
SubscribeAudioChunksInputSchema,
|
|
6259
|
+
SubscribeAudioChunksResultSchema,
|
|
6260
|
+
{ kind: "mutation" }
|
|
6261
|
+
),
|
|
6262
|
+
pullAudioChunks: method(
|
|
6263
|
+
object({
|
|
6264
|
+
subscriptionId: string(),
|
|
6265
|
+
maxCount: number().int().positive().default(8)
|
|
6266
|
+
}),
|
|
6267
|
+
array(DecodedAudioChunkSchema).readonly()
|
|
6268
|
+
),
|
|
6269
|
+
unsubscribeAudioChunks: method(
|
|
6270
|
+
object({ subscriptionId: string() }),
|
|
6271
|
+
object({ released: boolean() }),
|
|
6272
|
+
{ kind: "mutation" }
|
|
6273
|
+
),
|
|
6274
|
+
/**
|
|
6275
|
+
* ── Shared-memory frame plane (Phase 5 / D9) ──────────────────────
|
|
6276
|
+
*
|
|
6277
|
+
* The handle-based replacement for the live-object `IStreamBroker.
|
|
6278
|
+
* onDecodedFrame` callback path. A consumer:
|
|
6279
|
+
*
|
|
6280
|
+
* 1. `subscribeFrames({ brokerId, format })` — the broker spins up
|
|
6281
|
+
* (or reuses) a `frameSink: 'shm'` decoder session producing that
|
|
6282
|
+
* `format` and returns a `subscriptionId`.
|
|
6283
|
+
* 2. polls `pullFrameHandles({ subscriptionId, maxCount })` — drains
|
|
6284
|
+
* zero-pixel `FrameHandle[]`; each handle is fed to a
|
|
6285
|
+
* `FrameRingReader` that opens the named shm segment and reads the
|
|
6286
|
+
* pixels back zero-copy.
|
|
6287
|
+
* 3. `unsubscribeFrames({ subscriptionId })` on teardown.
|
|
6288
|
+
*
|
|
6289
|
+
* The broker keeps one shm ring per `(brokerId, format)` actually
|
|
6290
|
+
* requested — no broker-side `sharp` conversion. fps throttling is
|
|
6291
|
+
* implicit (latest-wins ring reads drop frames for a slow consumer).
|
|
6292
|
+
*/
|
|
6293
|
+
subscribeFrames: method(
|
|
6294
|
+
SubscribeFramesInputSchema,
|
|
6295
|
+
SubscribeFramesResultSchema,
|
|
6296
|
+
{ kind: "mutation" }
|
|
6297
|
+
),
|
|
6298
|
+
pullFrameHandles: method(
|
|
6299
|
+
object({
|
|
6300
|
+
subscriptionId: string(),
|
|
6301
|
+
maxCount: number().int().positive().default(4)
|
|
6302
|
+
}),
|
|
6303
|
+
array(FrameHandleSchema).readonly()
|
|
6304
|
+
),
|
|
6305
|
+
unsubscribeFrames: method(
|
|
6306
|
+
object({ subscriptionId: string() }),
|
|
6307
|
+
object({ released: boolean() }),
|
|
6308
|
+
{ kind: "mutation" }
|
|
6179
6309
|
),
|
|
6180
6310
|
setPreBufferDuration: method(
|
|
6181
6311
|
object({ brokerId: string(), seconds: number().min(0).max(30) }),
|
|
@@ -7253,6 +7383,34 @@ MotionTriggerStatusSchema.extend({
|
|
|
7253
7383
|
}) }
|
|
7254
7384
|
}
|
|
7255
7385
|
});
|
|
7386
|
+
object({
|
|
7387
|
+
enabled: boolean(),
|
|
7388
|
+
sensitivity: number(),
|
|
7389
|
+
/** Row-major active-cell grid. Length = gridWidth*gridHeight (see getOptions). */
|
|
7390
|
+
cells: array(boolean()),
|
|
7391
|
+
lastFetchedAt: number()
|
|
7392
|
+
});
|
|
7393
|
+
const MotionZoneOptionsSchema = object({
|
|
7394
|
+
gridWidth: number(),
|
|
7395
|
+
gridHeight: number(),
|
|
7396
|
+
sensitivity: object({ min: number(), max: number(), step: number() })
|
|
7397
|
+
});
|
|
7398
|
+
const MotionZonePatchSchema = object({
|
|
7399
|
+
enabled: boolean().optional(),
|
|
7400
|
+
sensitivity: number().optional(),
|
|
7401
|
+
cells: array(boolean()).optional()
|
|
7402
|
+
});
|
|
7403
|
+
({
|
|
7404
|
+
deviceTypes: [DeviceType.Camera],
|
|
7405
|
+
methods: {
|
|
7406
|
+
getOptions: method(object({ deviceId: number() }), MotionZoneOptionsSchema),
|
|
7407
|
+
setZone: method(
|
|
7408
|
+
object({ deviceId: number(), patch: MotionZonePatchSchema }),
|
|
7409
|
+
_void(),
|
|
7410
|
+
{ kind: "mutation", auth: "admin" }
|
|
7411
|
+
)
|
|
7412
|
+
}
|
|
7413
|
+
});
|
|
7256
7414
|
const AutotrackTargetTypeSchema = string().describe("Vendor target string (people/vehicle/pet); empty = camera default");
|
|
7257
7415
|
const PtzAutotrackSettingsSchema = object({
|
|
7258
7416
|
targetType: AutotrackTargetTypeSchema,
|
|
@@ -7341,6 +7499,100 @@ PtzAutotrackStatusSchema.extend({
|
|
|
7341
7499
|
}) }
|
|
7342
7500
|
}
|
|
7343
7501
|
});
|
|
7502
|
+
const StreamProfileSchema = _enum(["main", "sub", "ext"]);
|
|
7503
|
+
const StreamProfileConfigSchema = object({
|
|
7504
|
+
width: number(),
|
|
7505
|
+
height: number(),
|
|
7506
|
+
codec: _enum(["h264", "h265"]),
|
|
7507
|
+
framerate: number(),
|
|
7508
|
+
bitrate: number(),
|
|
7509
|
+
// kbps
|
|
7510
|
+
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
7511
|
+
encoderProfile: _enum(["high", "main", "baseline"]).optional(),
|
|
7512
|
+
gop: number().optional(),
|
|
7513
|
+
audio: boolean().optional()
|
|
7514
|
+
});
|
|
7515
|
+
object({
|
|
7516
|
+
/** Per-profile current config. A profile absent = the camera doesn't have it. */
|
|
7517
|
+
main: StreamProfileConfigSchema.optional(),
|
|
7518
|
+
sub: StreamProfileConfigSchema.optional(),
|
|
7519
|
+
ext: StreamProfileConfigSchema.optional(),
|
|
7520
|
+
lastFetchedAt: number()
|
|
7521
|
+
});
|
|
7522
|
+
const StreamProfileOptionsSchema = object({
|
|
7523
|
+
resolutions: array(object({ width: number(), height: number() })),
|
|
7524
|
+
codecs: array(_enum(["h264", "h265"])),
|
|
7525
|
+
framerates: array(number()),
|
|
7526
|
+
/** Allowed bitrate values (kbps). Empty if the camera takes a free range. */
|
|
7527
|
+
bitrates: array(number()),
|
|
7528
|
+
/** Optional [min,max] kbps when the camera accepts a continuous range. */
|
|
7529
|
+
bitrateRange: tuple([number(), number()]).optional(),
|
|
7530
|
+
supportsBitrateMode: boolean(),
|
|
7531
|
+
supportsEncoderProfile: boolean(),
|
|
7532
|
+
supportsGop: boolean(),
|
|
7533
|
+
/** Allowed GOP / keyframe-interval range, in seconds — drives the
|
|
7534
|
+
* I-frame-interval selector. Absent when the camera advertises GOP
|
|
7535
|
+
* support but no concrete range (callers then fall back to a free
|
|
7536
|
+
* numeric input). `{ min, max, step }` per the getOptions convention. */
|
|
7537
|
+
gop: object({ min: number(), max: number(), step: number() }).optional()
|
|
7538
|
+
});
|
|
7539
|
+
const StreamParamsOptionsSchema = object({
|
|
7540
|
+
main: StreamProfileOptionsSchema.optional(),
|
|
7541
|
+
sub: StreamProfileOptionsSchema.optional(),
|
|
7542
|
+
ext: StreamProfileOptionsSchema.optional()
|
|
7543
|
+
});
|
|
7544
|
+
const StreamProfilePatchSchema = object({
|
|
7545
|
+
width: number().optional(),
|
|
7546
|
+
height: number().optional(),
|
|
7547
|
+
codec: _enum(["h264", "h265"]).optional(),
|
|
7548
|
+
framerate: number().optional(),
|
|
7549
|
+
bitrate: number().optional(),
|
|
7550
|
+
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
7551
|
+
encoderProfile: _enum(["high", "main", "baseline"]).optional(),
|
|
7552
|
+
gop: number().optional(),
|
|
7553
|
+
audio: boolean().optional()
|
|
7554
|
+
});
|
|
7555
|
+
({
|
|
7556
|
+
deviceTypes: [DeviceType.Camera],
|
|
7557
|
+
methods: {
|
|
7558
|
+
getOptions: method(
|
|
7559
|
+
object({ deviceId: number() }),
|
|
7560
|
+
StreamParamsOptionsSchema
|
|
7561
|
+
),
|
|
7562
|
+
setProfile: method(
|
|
7563
|
+
object({
|
|
7564
|
+
deviceId: number(),
|
|
7565
|
+
profile: StreamProfileSchema,
|
|
7566
|
+
patch: StreamProfilePatchSchema
|
|
7567
|
+
}),
|
|
7568
|
+
_void(),
|
|
7569
|
+
{ kind: "mutation", auth: "admin" }
|
|
7570
|
+
),
|
|
7571
|
+
/**
|
|
7572
|
+
* Build the `ConfigUISchema` (admin-ui `ConfigFormBuilder` input
|
|
7573
|
+
* shape) for this camera's stream-encoder settings — one section per
|
|
7574
|
+
* profile (main / sub / ext) with the resolution / codec / framerate
|
|
7575
|
+
* / bitrate / bitrate-mode / encoder-profile / GOP controls the
|
|
7576
|
+
* firmware actually exposes.
|
|
7577
|
+
*
|
|
7578
|
+
* Driven by `getOptions` (camera-probed availability) + `getStatus`
|
|
7579
|
+
* (current per-profile config); each field's `default` is seeded
|
|
7580
|
+
* from the live config so the form renders the camera state in one
|
|
7581
|
+
* pass. Returns `null` when the camera exposes no configurable
|
|
7582
|
+
* stream property — the renderer then shows the unsupported message.
|
|
7583
|
+
*
|
|
7584
|
+
* Output is `z.unknown().nullable()` — the same convention every
|
|
7585
|
+
* other `ConfigUISchema`-returning cap method uses (`device-ops`,
|
|
7586
|
+
* `device-manager`); `ConfigUISchema` is a TS-only type with no
|
|
7587
|
+
* companion Zod schema, and a concrete object would collapse
|
|
7588
|
+
* unrelated AppRouter branches to `unknown` during codegen.
|
|
7589
|
+
*/
|
|
7590
|
+
getConfigSchema: method(
|
|
7591
|
+
object({ deviceId: number() }),
|
|
7592
|
+
unknown().nullable()
|
|
7593
|
+
)
|
|
7594
|
+
}
|
|
7595
|
+
});
|
|
7344
7596
|
object({
|
|
7345
7597
|
on: boolean(),
|
|
7346
7598
|
/** Ms epoch of the last state change. Useful for UI "X minutes ago". */
|
|
@@ -8282,6 +8534,83 @@ const adminUiCapability = {
|
|
|
8282
8534
|
getVersion: method(_void(), VersionOutputSchema)
|
|
8283
8535
|
}
|
|
8284
8536
|
};
|
|
8537
|
+
const MethodAccessSchema = _enum(["view", "create", "delete"]);
|
|
8538
|
+
const AllowedProviderSchema = union([literal("*"), array(string())]);
|
|
8539
|
+
const AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
|
|
8540
|
+
const CapScopeSchema = _enum(["device", "system"]);
|
|
8541
|
+
const TokenScopeSchema = discriminatedUnion("type", [
|
|
8542
|
+
object({
|
|
8543
|
+
type: literal("category"),
|
|
8544
|
+
target: CapScopeSchema,
|
|
8545
|
+
access: array(MethodAccessSchema).min(1)
|
|
8546
|
+
}),
|
|
8547
|
+
object({
|
|
8548
|
+
type: literal("capability"),
|
|
8549
|
+
target: string(),
|
|
8550
|
+
access: array(MethodAccessSchema).min(1)
|
|
8551
|
+
}),
|
|
8552
|
+
object({
|
|
8553
|
+
type: literal("addon"),
|
|
8554
|
+
target: string(),
|
|
8555
|
+
access: array(MethodAccessSchema).min(1)
|
|
8556
|
+
}),
|
|
8557
|
+
object({
|
|
8558
|
+
type: literal("device"),
|
|
8559
|
+
/**
|
|
8560
|
+
* One or more deviceIds (serialised as strings for wire-format
|
|
8561
|
+
* consistency with the rest of the union). Matcher accepts if
|
|
8562
|
+
* `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
|
|
8563
|
+
* of one scope-per-device when granting access to a set of cameras.
|
|
8564
|
+
*/
|
|
8565
|
+
targets: array(string()).min(1),
|
|
8566
|
+
access: array(MethodAccessSchema).min(1)
|
|
8567
|
+
})
|
|
8568
|
+
]);
|
|
8569
|
+
object({
|
|
8570
|
+
id: string(),
|
|
8571
|
+
username: string(),
|
|
8572
|
+
passwordHash: string(),
|
|
8573
|
+
/**
|
|
8574
|
+
* Admin bypass. When true, the middleware skips the scope-access
|
|
8575
|
+
* check entirely. There is no other axis of privilege; the legacy
|
|
8576
|
+
* role enum collapsed onto this boolean in v2.
|
|
8577
|
+
*/
|
|
8578
|
+
isAdmin: boolean().default(false),
|
|
8579
|
+
allowedProviders: AllowedProviderSchema,
|
|
8580
|
+
allowedDevices: AllowedDevicesSchema,
|
|
8581
|
+
/**
|
|
8582
|
+
* Scopes granted to this user. Admins bypass; their `scopes` is
|
|
8583
|
+
* ignored. Non-admins without scopes are locked out of every
|
|
8584
|
+
* protected call.
|
|
8585
|
+
*/
|
|
8586
|
+
scopes: array(TokenScopeSchema).default([]),
|
|
8587
|
+
createdAt: number(),
|
|
8588
|
+
updatedAt: number()
|
|
8589
|
+
});
|
|
8590
|
+
object({
|
|
8591
|
+
id: string(),
|
|
8592
|
+
label: string(),
|
|
8593
|
+
isAdmin: boolean().default(false),
|
|
8594
|
+
allowedProviders: AllowedProviderSchema,
|
|
8595
|
+
allowedDevices: AllowedDevicesSchema,
|
|
8596
|
+
tokenHash: string(),
|
|
8597
|
+
tokenPrefix: string(),
|
|
8598
|
+
createdAt: number(),
|
|
8599
|
+
lastUsedAt: number().optional()
|
|
8600
|
+
});
|
|
8601
|
+
object({
|
|
8602
|
+
id: string(),
|
|
8603
|
+
userId: string(),
|
|
8604
|
+
name: string(),
|
|
8605
|
+
tokenHash: string(),
|
|
8606
|
+
tokenPrefix: string(),
|
|
8607
|
+
scopes: array(TokenScopeSchema),
|
|
8608
|
+
// SQLite/JSON storage round-trips undefined → null. Use `nullish` so the
|
|
8609
|
+
// schema accepts both `null` (read from disk) and `undefined` (in-memory).
|
|
8610
|
+
expiresAt: number().nullish(),
|
|
8611
|
+
lastUsedAt: number().nullish(),
|
|
8612
|
+
createdAt: number()
|
|
8613
|
+
});
|
|
8285
8614
|
const SsoBridgeClaimsSchema = object({
|
|
8286
8615
|
userId: string(),
|
|
8287
8616
|
username: string(),
|
|
@@ -8297,7 +8626,18 @@ const SsoBridgeClaimsSchema = object({
|
|
|
8297
8626
|
* JWT WITHOUT verifying the signature — the hub re-verifies on every
|
|
8298
8627
|
* inbound call so trust still rests with the signing hub.
|
|
8299
8628
|
*/
|
|
8300
|
-
hubUrl: string().optional()
|
|
8629
|
+
hubUrl: string().optional(),
|
|
8630
|
+
/** Permission scopes baked into the token. Set by the OAuth
|
|
8631
|
+
* account-linking grant; absent on ordinary SSO-login tokens. */
|
|
8632
|
+
scopes: array(TokenScopeSchema).optional(),
|
|
8633
|
+
/** OAuth authorization-code binding — set only on `oauth-code` tokens. */
|
|
8634
|
+
redirectUri: string().optional(),
|
|
8635
|
+
integrationId: string().optional(),
|
|
8636
|
+
/** JWT ID — unique per issued code; consumed-set enforces single-use. */
|
|
8637
|
+
jti: string().optional(),
|
|
8638
|
+
/** OAuth session registry id — set on `oauth-access`/`oauth-refresh`
|
|
8639
|
+
* tokens so the verify path can check the session is not revoked. */
|
|
8640
|
+
sessionId: string().optional()
|
|
8301
8641
|
});
|
|
8302
8642
|
({
|
|
8303
8643
|
methods: {
|
|
@@ -8314,6 +8654,23 @@ const SsoBridgeClaimsSchema = object({
|
|
|
8314
8654
|
)
|
|
8315
8655
|
}
|
|
8316
8656
|
});
|
|
8657
|
+
const OauthIntegrationDescriptorSchema = object({
|
|
8658
|
+
/** Stable id used as the `integration=` query param, e.g. 'export-alexa'. */
|
|
8659
|
+
integrationId: string(),
|
|
8660
|
+
/** Human label rendered on the consent page. */
|
|
8661
|
+
displayName: string(),
|
|
8662
|
+
/** Scopes baked into every token issued for this integration. */
|
|
8663
|
+
requestedScopes: array(TokenScopeSchema),
|
|
8664
|
+
/** Allowed redirect_uri prefixes. /api/oauth2/authorize rejects any
|
|
8665
|
+
* redirect_uri that does not start with one of these. Required —
|
|
8666
|
+
* an empty list means the integration can never complete linking. */
|
|
8667
|
+
allowedRedirectPrefixes: array(string()).min(1)
|
|
8668
|
+
});
|
|
8669
|
+
({
|
|
8670
|
+
methods: {
|
|
8671
|
+
getDescriptor: method(_void(), OauthIntegrationDescriptorSchema)
|
|
8672
|
+
}
|
|
8673
|
+
});
|
|
8317
8674
|
const PasskeySummarySchema = object({
|
|
8318
8675
|
credentialId: string(),
|
|
8319
8676
|
label: string(),
|
|
@@ -8594,21 +8951,30 @@ const AddonPageDeclarationSchema = object({
|
|
|
8594
8951
|
});
|
|
8595
8952
|
const WidgetHostEnum = _enum(["device-tab", "dashboard", "integration-detail"]);
|
|
8596
8953
|
const WidgetSizeEnum = _enum(["xs", "sm", "md", "lg", "xl"]);
|
|
8954
|
+
const WidgetRemoteSchema = object({
|
|
8955
|
+
remoteName: string(),
|
|
8956
|
+
exposedModule: string(),
|
|
8957
|
+
componentKey: string().optional()
|
|
8958
|
+
});
|
|
8597
8959
|
const WidgetMetadataSchema = object({
|
|
8598
|
-
|
|
8599
|
-
|
|
8960
|
+
// ── UiContribution core (kind:'remote') ──────────────────────────
|
|
8961
|
+
/** Primary host tab — `'dashboard'`, `'device-tab'`, or a device-detail tab id. */
|
|
8962
|
+
tab: string(),
|
|
8963
|
+
/** Optional sub-tab within `tab`. */
|
|
8964
|
+
subTab: string().optional(),
|
|
8600
8965
|
/** Operator-facing label. */
|
|
8601
8966
|
label: string(),
|
|
8967
|
+
/** Ordering within `(tab, subTab)`, ascending. */
|
|
8968
|
+
order: number().optional(),
|
|
8969
|
+
/** Always `'remote'` — a widget is a Module Federation remote. */
|
|
8970
|
+
kind: literal("remote"),
|
|
8971
|
+
/** MF remote descriptor. */
|
|
8972
|
+
remote: WidgetRemoteSchema,
|
|
8973
|
+
// ── Widget-only metadata ─────────────────────────────────────────
|
|
8974
|
+
/** Stable id within the addon — kebab-case. Equals `remote.componentKey`. */
|
|
8975
|
+
stableId: string(),
|
|
8602
8976
|
description: string().optional(),
|
|
8603
8977
|
icon: string().optional(),
|
|
8604
|
-
/**
|
|
8605
|
-
* Module Federation remote name — must match the `name` field on the
|
|
8606
|
-
* widget addon's `federation()` plugin config. Used by the host's
|
|
8607
|
-
* `<WidgetRegistryProvider>` to call `loadRemote('<remoteName>/widgets')`.
|
|
8608
|
-
* Conventionally `addon_<addonid>_widgets` (snake_case; MF names
|
|
8609
|
-
* cannot contain hyphens).
|
|
8610
|
-
*/
|
|
8611
|
-
remoteName: string(),
|
|
8612
8978
|
/**
|
|
8613
8979
|
* Bundle filename inside the addon's `dist/` dir served at
|
|
8614
8980
|
* `/api/addon-widgets/<addonId>/<bundle>`. With Module Federation
|
|
@@ -8617,9 +8983,9 @@ const WidgetMetadataSchema = object({
|
|
|
8617
8983
|
* cache-buster URL without a separate filesystem stat.
|
|
8618
8984
|
*/
|
|
8619
8985
|
bundle: string(),
|
|
8620
|
-
/**
|
|
8986
|
+
/** Every host the widget supports. The picker filters on this set. */
|
|
8621
8987
|
hosts: array(WidgetHostEnum).readonly(),
|
|
8622
|
-
/** Required props the host must supply. Validated at
|
|
8988
|
+
/** Required props the host must supply. Validated at `<WidgetSlot>` mount. */
|
|
8623
8989
|
requires: object({
|
|
8624
8990
|
deviceContext: boolean().default(false),
|
|
8625
8991
|
integrationContext: boolean().default(false)
|
|
@@ -8690,6 +9056,16 @@ const InvokeReplyEnvelopeSchema = object({
|
|
|
8690
9056
|
invoke: method(InvokeRequestSchema, InvokeReplyEnvelopeSchema, { kind: "mutation" })
|
|
8691
9057
|
}
|
|
8692
9058
|
});
|
|
9059
|
+
const ShmRingStatsSchema = object({
|
|
9060
|
+
sessionId: string(),
|
|
9061
|
+
slotCount: number().int(),
|
|
9062
|
+
slotByteLength: number().int(),
|
|
9063
|
+
segmentBytes: number().int(),
|
|
9064
|
+
budgetMb: number().int(),
|
|
9065
|
+
framesWritten: number().int(),
|
|
9066
|
+
getFrameHits: number().int(),
|
|
9067
|
+
getFrameMisses: number().int()
|
|
9068
|
+
});
|
|
8693
9069
|
({
|
|
8694
9070
|
methods: {
|
|
8695
9071
|
// ── Discovery ─────────────────────────────────────────────────
|
|
@@ -8717,10 +9093,27 @@ const InvokeReplyEnvelopeSchema = object({
|
|
|
8717
9093
|
url: string()
|
|
8718
9094
|
}), _void()),
|
|
8719
9095
|
// ── Output — polling-based frame retrieval ────────────────────
|
|
9096
|
+
// `pullFrames` drains the pixel `DecodedFrame[]` of a `frameSink:
|
|
9097
|
+
// 'callback'` session. `pullHandles` (Phase 5 / D9) drains the
|
|
9098
|
+
// zero-pixel `FrameHandle[]` of a `frameSink: 'shm'` session — the
|
|
9099
|
+
// broker hands each handle to a `FrameRingReader` that opens the
|
|
9100
|
+
// named segment and reads the pixels back zero-copy. A session is
|
|
9101
|
+
// one mode or the other; the unmatched method returns an empty
|
|
9102
|
+
// array.
|
|
8720
9103
|
pullFrames: method(object({
|
|
8721
9104
|
sessionId: string(),
|
|
8722
9105
|
maxCount: number().default(1)
|
|
8723
9106
|
}), array(DecodedFrameSchema)),
|
|
9107
|
+
pullHandles: method(object({
|
|
9108
|
+
sessionId: string(),
|
|
9109
|
+
maxCount: number().default(1)
|
|
9110
|
+
}), array(FrameHandleSchema)),
|
|
9111
|
+
// ── Frame fetch (Phase 5 / D9 downstream access) ──────────────
|
|
9112
|
+
// Read the pixels a FrameHandle refers to from this node's shm ring.
|
|
9113
|
+
// Returns null when the slot was already recycled (latest-wins).
|
|
9114
|
+
getFrame: method(object({ handle: FrameHandleSchema }), DecodedFrameSchema.nullable()),
|
|
9115
|
+
// shm ring usage stats for a session.
|
|
9116
|
+
getShmStats: method(object({ sessionId: string() }), ShmRingStatsSchema.nullable()),
|
|
8724
9117
|
// ── Control ───────────────────────────────────────────────────
|
|
8725
9118
|
updateConfig: method(object({
|
|
8726
9119
|
sessionId: string(),
|
|
@@ -9888,8 +10281,8 @@ const DevicePersistConfigPayloadSchema = object({
|
|
|
9888
10281
|
/**
|
|
9889
10282
|
* Return the addon ids that declared a wrapper provider for `capName`.
|
|
9890
10283
|
* Backs the device-bindings UI's wrapper-picker dropdown. Entries are
|
|
9891
|
-
* sourced from `CapabilityRegistry.wrapperProviders`, populated
|
|
9892
|
-
* `
|
|
10284
|
+
* sourced from `CapabilityRegistry.wrapperProviders`, populated when
|
|
10285
|
+
* the cap definition declares `kind: 'wrapper'`.
|
|
9893
10286
|
*/
|
|
9894
10287
|
listWrappersForCap: method(
|
|
9895
10288
|
object({ capName: string() }),
|
|
@@ -10979,6 +11372,18 @@ const PtzMoveCommandSchema = object({
|
|
|
10979
11372
|
zoom: number().optional(),
|
|
10980
11373
|
speed: number().optional()
|
|
10981
11374
|
});
|
|
11375
|
+
PtzPositionSchema.extend({ autofocus: boolean() });
|
|
11376
|
+
const PtzOptionsSchema = object({
|
|
11377
|
+
hasPan: boolean(),
|
|
11378
|
+
hasTilt: boolean(),
|
|
11379
|
+
hasZoom: boolean(),
|
|
11380
|
+
supportsPresets: boolean(),
|
|
11381
|
+
/** Max number of named presets the camera supports, when known. */
|
|
11382
|
+
maxPresets: number().optional(),
|
|
11383
|
+
/** Whether the camera exposes a controllable autofocus toggle
|
|
11384
|
+
* (boolean `hasX` per the getOptions availability convention). */
|
|
11385
|
+
hasAutofocus: boolean()
|
|
11386
|
+
});
|
|
10982
11387
|
({
|
|
10983
11388
|
deviceTypes: [DeviceType.Camera],
|
|
10984
11389
|
methods: {
|
|
@@ -11006,6 +11411,20 @@ const PtzMoveCommandSchema = object({
|
|
|
11006
11411
|
_void(),
|
|
11007
11412
|
{ kind: "mutation" }
|
|
11008
11413
|
),
|
|
11414
|
+
savePreset: method(
|
|
11415
|
+
object({ deviceId: number(), presetId: string(), name: string() }),
|
|
11416
|
+
_void(),
|
|
11417
|
+
{ kind: "mutation", auth: "admin" }
|
|
11418
|
+
),
|
|
11419
|
+
deletePreset: method(
|
|
11420
|
+
object({ deviceId: number(), presetId: string() }),
|
|
11421
|
+
_void(),
|
|
11422
|
+
{ kind: "mutation", auth: "admin" }
|
|
11423
|
+
),
|
|
11424
|
+
getOptions: method(
|
|
11425
|
+
object({ deviceId: number() }),
|
|
11426
|
+
PtzOptionsSchema
|
|
11427
|
+
),
|
|
11009
11428
|
goHome: method(
|
|
11010
11429
|
object({ deviceId: number() }),
|
|
11011
11430
|
_void(),
|
|
@@ -11020,6 +11439,13 @@ const PtzMoveCommandSchema = object({
|
|
|
11020
11439
|
getPosition: method(
|
|
11021
11440
|
object({ deviceId: number() }),
|
|
11022
11441
|
PtzPositionSchema
|
|
11442
|
+
),
|
|
11443
|
+
/** Toggle the camera's autofocus. Only meaningful when
|
|
11444
|
+
* `getOptions().hasAutofocus` is true. */
|
|
11445
|
+
setAutofocus: method(
|
|
11446
|
+
object({ deviceId: number(), enabled: boolean() }),
|
|
11447
|
+
_void(),
|
|
11448
|
+
{ kind: "mutation" }
|
|
11023
11449
|
)
|
|
11024
11450
|
}
|
|
11025
11451
|
});
|
|
@@ -11955,83 +12381,6 @@ const MeshStatusSchema = object({
|
|
|
11955
12381
|
// tabs driven by this cap.
|
|
11956
12382
|
}
|
|
11957
12383
|
});
|
|
11958
|
-
const MethodAccessSchema = _enum(["view", "create", "delete"]);
|
|
11959
|
-
const AllowedProviderSchema = union([literal("*"), array(string())]);
|
|
11960
|
-
const AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
|
|
11961
|
-
const CapScopeSchema = _enum(["device", "system"]);
|
|
11962
|
-
const TokenScopeSchema = discriminatedUnion("type", [
|
|
11963
|
-
object({
|
|
11964
|
-
type: literal("category"),
|
|
11965
|
-
target: CapScopeSchema,
|
|
11966
|
-
access: array(MethodAccessSchema).min(1)
|
|
11967
|
-
}),
|
|
11968
|
-
object({
|
|
11969
|
-
type: literal("capability"),
|
|
11970
|
-
target: string(),
|
|
11971
|
-
access: array(MethodAccessSchema).min(1)
|
|
11972
|
-
}),
|
|
11973
|
-
object({
|
|
11974
|
-
type: literal("addon"),
|
|
11975
|
-
target: string(),
|
|
11976
|
-
access: array(MethodAccessSchema).min(1)
|
|
11977
|
-
}),
|
|
11978
|
-
object({
|
|
11979
|
-
type: literal("device"),
|
|
11980
|
-
/**
|
|
11981
|
-
* One or more deviceIds (serialised as strings for wire-format
|
|
11982
|
-
* consistency with the rest of the union). Matcher accepts if
|
|
11983
|
-
* `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
|
|
11984
|
-
* of one scope-per-device when granting access to a set of cameras.
|
|
11985
|
-
*/
|
|
11986
|
-
targets: array(string()).min(1),
|
|
11987
|
-
access: array(MethodAccessSchema).min(1)
|
|
11988
|
-
})
|
|
11989
|
-
]);
|
|
11990
|
-
object({
|
|
11991
|
-
id: string(),
|
|
11992
|
-
username: string(),
|
|
11993
|
-
passwordHash: string(),
|
|
11994
|
-
/**
|
|
11995
|
-
* Admin bypass. When true, the middleware skips the scope-access
|
|
11996
|
-
* check entirely. There is no other axis of privilege; the legacy
|
|
11997
|
-
* role enum collapsed onto this boolean in v2.
|
|
11998
|
-
*/
|
|
11999
|
-
isAdmin: boolean().default(false),
|
|
12000
|
-
allowedProviders: AllowedProviderSchema,
|
|
12001
|
-
allowedDevices: AllowedDevicesSchema,
|
|
12002
|
-
/**
|
|
12003
|
-
* Scopes granted to this user. Admins bypass; their `scopes` is
|
|
12004
|
-
* ignored. Non-admins without scopes are locked out of every
|
|
12005
|
-
* protected call.
|
|
12006
|
-
*/
|
|
12007
|
-
scopes: array(TokenScopeSchema).default([]),
|
|
12008
|
-
createdAt: number(),
|
|
12009
|
-
updatedAt: number()
|
|
12010
|
-
});
|
|
12011
|
-
object({
|
|
12012
|
-
id: string(),
|
|
12013
|
-
label: string(),
|
|
12014
|
-
isAdmin: boolean().default(false),
|
|
12015
|
-
allowedProviders: AllowedProviderSchema,
|
|
12016
|
-
allowedDevices: AllowedDevicesSchema,
|
|
12017
|
-
tokenHash: string(),
|
|
12018
|
-
tokenPrefix: string(),
|
|
12019
|
-
createdAt: number(),
|
|
12020
|
-
lastUsedAt: number().optional()
|
|
12021
|
-
});
|
|
12022
|
-
object({
|
|
12023
|
-
id: string(),
|
|
12024
|
-
userId: string(),
|
|
12025
|
-
name: string(),
|
|
12026
|
-
tokenHash: string(),
|
|
12027
|
-
tokenPrefix: string(),
|
|
12028
|
-
scopes: array(TokenScopeSchema),
|
|
12029
|
-
// SQLite/JSON storage round-trips undefined → null. Use `nullish` so the
|
|
12030
|
-
// schema accepts both `null` (read from disk) and `undefined` (in-memory).
|
|
12031
|
-
expiresAt: number().nullish(),
|
|
12032
|
-
lastUsedAt: number().nullish(),
|
|
12033
|
-
createdAt: number()
|
|
12034
|
-
});
|
|
12035
12384
|
const UserSummarySchema = object({
|
|
12036
12385
|
id: string(),
|
|
12037
12386
|
username: string(),
|
|
@@ -12104,6 +12453,16 @@ const CreateScopedTokenResultSchema = object({
|
|
|
12104
12453
|
token: string(),
|
|
12105
12454
|
record: ScopedTokenSummarySchema
|
|
12106
12455
|
});
|
|
12456
|
+
const OauthSessionSummarySchema = object({
|
|
12457
|
+
id: string(),
|
|
12458
|
+
userId: string(),
|
|
12459
|
+
username: string(),
|
|
12460
|
+
integrationId: string(),
|
|
12461
|
+
scopes: array(TokenScopeSchema),
|
|
12462
|
+
createdAt: number(),
|
|
12463
|
+
lastUsedAt: number(),
|
|
12464
|
+
revokedAt: number().nullable()
|
|
12465
|
+
});
|
|
12107
12466
|
const TotpSetupResultSchema = object({
|
|
12108
12467
|
secret: string(),
|
|
12109
12468
|
otpauthUrl: string()
|
|
@@ -12179,6 +12538,66 @@ const TotpStatusSchema = object({
|
|
|
12179
12538
|
object({ userId: string(), code: string() }),
|
|
12180
12539
|
object({ valid: boolean() }),
|
|
12181
12540
|
{ kind: "mutation", access: "view" }
|
|
12541
|
+
),
|
|
12542
|
+
// ── OAuth account-linking grant ────────────────────────────────
|
|
12543
|
+
//
|
|
12544
|
+
// Core's /oauth2/* endpoints delegate here. Tokens are sso-bridge
|
|
12545
|
+
// JWTs (kinds oauth-code / oauth-access / oauth-refresh) and ALWAYS
|
|
12546
|
+
// carry isAdmin:false — the operator login proves hub control; the
|
|
12547
|
+
// issued token is minimal (device scope only).
|
|
12548
|
+
oauthIssueCode: method(
|
|
12549
|
+
object({
|
|
12550
|
+
integrationId: string(),
|
|
12551
|
+
userId: string(),
|
|
12552
|
+
username: string(),
|
|
12553
|
+
scopes: array(TokenScopeSchema),
|
|
12554
|
+
redirectUri: string(),
|
|
12555
|
+
hubUrl: string()
|
|
12556
|
+
}),
|
|
12557
|
+
object({ code: string() }),
|
|
12558
|
+
{ kind: "mutation", access: "create" }
|
|
12559
|
+
),
|
|
12560
|
+
oauthExchangeCode: method(
|
|
12561
|
+
object({ code: string(), redirectUri: string() }),
|
|
12562
|
+
object({
|
|
12563
|
+
accessToken: string(),
|
|
12564
|
+
refreshToken: string(),
|
|
12565
|
+
expiresIn: number()
|
|
12566
|
+
}).nullable(),
|
|
12567
|
+
{ kind: "mutation", access: "view" }
|
|
12568
|
+
),
|
|
12569
|
+
oauthRefresh: method(
|
|
12570
|
+
object({ refreshToken: string() }),
|
|
12571
|
+
object({
|
|
12572
|
+
accessToken: string(),
|
|
12573
|
+
refreshToken: string(),
|
|
12574
|
+
expiresIn: number()
|
|
12575
|
+
}).nullable(),
|
|
12576
|
+
{ kind: "mutation", access: "view" }
|
|
12577
|
+
),
|
|
12578
|
+
oauthVerifyAccessToken: method(
|
|
12579
|
+
object({ token: string() }),
|
|
12580
|
+
object({
|
|
12581
|
+
userId: string(),
|
|
12582
|
+
username: string(),
|
|
12583
|
+
scopes: array(TokenScopeSchema)
|
|
12584
|
+
}).nullable(),
|
|
12585
|
+
{ access: "view" }
|
|
12586
|
+
),
|
|
12587
|
+
// ── OAuth linked-session management (Phase D) ──────────────────
|
|
12588
|
+
//
|
|
12589
|
+
// The admin UI lists active account-linking sessions and revokes
|
|
12590
|
+
// them; revocation makes the linked integration's tokens fail
|
|
12591
|
+
// verification immediately.
|
|
12592
|
+
listOauthSessions: method(
|
|
12593
|
+
_void(),
|
|
12594
|
+
array(OauthSessionSummarySchema),
|
|
12595
|
+
{ auth: "admin" }
|
|
12596
|
+
),
|
|
12597
|
+
revokeOauthSession: method(
|
|
12598
|
+
object({ id: string() }),
|
|
12599
|
+
object({ success: boolean() }),
|
|
12600
|
+
{ kind: "mutation", auth: "admin", access: "delete" }
|
|
12182
12601
|
)
|
|
12183
12602
|
}
|
|
12184
12603
|
});
|
|
@@ -12395,6 +12814,19 @@ const RenameNodeResultSchema = object({
|
|
|
12395
12814
|
record(string(), ClusterAddonStatusEntrySchema),
|
|
12396
12815
|
{ auth: "admin" }
|
|
12397
12816
|
),
|
|
12817
|
+
getCapUsageGraph: method(
|
|
12818
|
+
object({
|
|
12819
|
+
windowSeconds: number().int().positive().max(300).default(60)
|
|
12820
|
+
}),
|
|
12821
|
+
array(object({
|
|
12822
|
+
callerAddonId: string(),
|
|
12823
|
+
providerAddonId: string(),
|
|
12824
|
+
capName: string(),
|
|
12825
|
+
callsPerMin: number(),
|
|
12826
|
+
lastCallAtMs: number()
|
|
12827
|
+
})).readonly(),
|
|
12828
|
+
{ auth: "admin" }
|
|
12829
|
+
),
|
|
12398
12830
|
/**
|
|
12399
12831
|
* Direct per-node addon listing — calls `$agent.status` on the target
|
|
12400
12832
|
* node (or returns the hub registry for `nodeId === 'hub'`) and surfaces
|