@camstack/types 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.
Files changed (95) hide show
  1. package/dist/capabilities/addon-widgets-source.cap.d.ts +73 -5
  2. package/dist/capabilities/addon-widgets-source.cap.d.ts.map +1 -1
  3. package/dist/capabilities/addon-widgets.cap.d.ts +22 -4
  4. package/dist/capabilities/addon-widgets.cap.d.ts.map +1 -1
  5. package/dist/capabilities/audio-analysis.cap.d.ts +7 -4
  6. package/dist/capabilities/audio-analysis.cap.d.ts.map +1 -1
  7. package/dist/capabilities/camera-streams.cap.d.ts +7 -5
  8. package/dist/capabilities/camera-streams.cap.d.ts.map +1 -1
  9. package/dist/capabilities/capability-definition.d.ts +114 -0
  10. package/dist/capabilities/capability-definition.d.ts.map +1 -1
  11. package/dist/capabilities/decoder.cap.d.ts +90 -0
  12. package/dist/capabilities/decoder.cap.d.ts.map +1 -1
  13. package/dist/capabilities/detection-pipeline.cap.d.ts +6 -4
  14. package/dist/capabilities/detection-pipeline.cap.d.ts.map +1 -1
  15. package/dist/capabilities/device-manager.cap.d.ts +3 -3
  16. package/dist/capabilities/device-ops.cap.d.ts +1 -1
  17. package/dist/capabilities/index.d.ts +18 -11
  18. package/dist/capabilities/index.d.ts.map +1 -1
  19. package/dist/capabilities/motion-detection.cap.d.ts +2 -0
  20. package/dist/capabilities/motion-detection.cap.d.ts.map +1 -1
  21. package/dist/capabilities/motion-zones.cap.d.ts +83 -0
  22. package/dist/capabilities/motion-zones.cap.d.ts.map +1 -0
  23. package/dist/capabilities/nodes.cap.d.ts +9 -0
  24. package/dist/capabilities/nodes.cap.d.ts.map +1 -1
  25. package/dist/capabilities/oauth-integration.cap.d.ts +103 -0
  26. package/dist/capabilities/oauth-integration.cap.d.ts.map +1 -0
  27. package/dist/capabilities/pipeline-analytics.cap.d.ts +2 -0
  28. package/dist/capabilities/pipeline-analytics.cap.d.ts.map +1 -1
  29. package/dist/capabilities/ptz-autotrack.cap.d.ts +10 -0
  30. package/dist/capabilities/ptz-autotrack.cap.d.ts.map +1 -1
  31. package/dist/capabilities/ptz.cap.d.ts +57 -1
  32. package/dist/capabilities/ptz.cap.d.ts.map +1 -1
  33. package/dist/capabilities/schemas/streaming-shared.d.ts +110 -3
  34. package/dist/capabilities/schemas/streaming-shared.d.ts.map +1 -1
  35. package/dist/capabilities/snapshot.cap.d.ts +2 -0
  36. package/dist/capabilities/snapshot.cap.d.ts.map +1 -1
  37. package/dist/capabilities/sso-bridge.cap.d.ts +120 -0
  38. package/dist/capabilities/sso-bridge.cap.d.ts.map +1 -1
  39. package/dist/capabilities/stream-broker.cap.d.ts +97 -4
  40. package/dist/capabilities/stream-broker.cap.d.ts.map +1 -1
  41. package/dist/capabilities/stream-params-config-schema.d.ts +56 -0
  42. package/dist/capabilities/stream-params-config-schema.d.ts.map +1 -0
  43. package/dist/capabilities/stream-params.cap.d.ts +482 -0
  44. package/dist/capabilities/stream-params.cap.d.ts.map +1 -0
  45. package/dist/capabilities/user-management.cap.d.ts +198 -1
  46. package/dist/capabilities/user-management.cap.d.ts.map +1 -1
  47. package/dist/capabilities/webrtc-session.cap.d.ts +2 -0
  48. package/dist/capabilities/webrtc-session.cap.d.ts.map +1 -1
  49. package/dist/generated/addon-api.d.ts +1175 -51
  50. package/dist/generated/addon-api.d.ts.map +1 -1
  51. package/dist/generated/cap-status-types.d.ts +7 -3
  52. package/dist/generated/cap-status-types.d.ts.map +1 -1
  53. package/dist/generated/capability-router-map.d.ts +13 -4
  54. package/dist/generated/capability-router-map.d.ts.map +1 -1
  55. package/dist/generated/device-local-state.d.ts +6 -0
  56. package/dist/generated/device-local-state.d.ts.map +1 -1
  57. package/dist/generated/device-proxy.d.ts +6 -0
  58. package/dist/generated/device-proxy.d.ts.map +1 -1
  59. package/dist/generated/method-access-map.d.ts +1 -1
  60. package/dist/generated/method-access-map.d.ts.map +1 -1
  61. package/dist/generated/system-proxy.d.ts +4 -4
  62. package/dist/generated/system-proxy.d.ts.map +1 -1
  63. package/dist/{index-BkSgJYP7.js → index-BblD92Si.js} +600 -98
  64. package/dist/index-BblD92Si.js.map +1 -0
  65. package/dist/{index-CWhQOnm9.mjs → index-CgPd35k5.mjs} +949 -447
  66. package/dist/index-CgPd35k5.mjs.map +1 -0
  67. package/dist/index.d.ts +2 -1
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +313 -7
  70. package/dist/index.js.map +1 -1
  71. package/dist/index.mjs +587 -281
  72. package/dist/index.mjs.map +1 -1
  73. package/dist/interfaces/addon.d.ts +123 -29
  74. package/dist/interfaces/addon.d.ts.map +1 -1
  75. package/dist/interfaces/capability.d.ts +6 -23
  76. package/dist/interfaces/capability.d.ts.map +1 -1
  77. package/dist/interfaces/config-ui.d.ts +4 -101
  78. package/dist/interfaces/config-ui.d.ts.map +1 -1
  79. package/dist/interfaces/decoder.d.ts +12 -0
  80. package/dist/interfaces/decoder.d.ts.map +1 -1
  81. package/dist/interfaces/device-provider.d.ts +0 -4
  82. package/dist/interfaces/device-provider.d.ts.map +1 -1
  83. package/dist/interfaces/event-bus.d.ts +15 -0
  84. package/dist/interfaces/event-bus.d.ts.map +1 -1
  85. package/dist/interfaces/frame-handle.d.ts +40 -0
  86. package/dist/interfaces/frame-handle.d.ts.map +1 -0
  87. package/dist/interfaces/kernel-abstractions.d.ts +6 -0
  88. package/dist/interfaces/kernel-abstractions.d.ts.map +1 -1
  89. package/dist/interfaces/stream-broker.d.ts +40 -21
  90. package/dist/interfaces/stream-broker.d.ts.map +1 -1
  91. package/dist/node.js +1 -1
  92. package/dist/node.mjs +1 -1
  93. package/package.json +1 -1
  94. package/dist/index-BkSgJYP7.js.map +0 -1
  95. package/dist/index-CWhQOnm9.mjs.map +0 -1
@@ -13,6 +13,7 @@ const WELL_KNOWN_TABS = [
13
13
  { id: "osd", label: "OSD", icon: "type", order: 18 },
14
14
  { id: "stream-broker", label: "Stream Broker", icon: "radio", order: 20 },
15
15
  { id: "streaming", label: "Streaming", icon: "video", order: 35 },
16
+ { id: "ptz", label: "PTZ", icon: "move", order: 40 },
16
17
  { id: "pipeline", label: "Detection Pipeline", icon: "cpu", order: 39 },
17
18
  { id: "zones", label: "Detection", icon: "shapes", order: 38 },
18
19
  { id: "live-stats", label: "Live Stats", icon: "activity", order: 39 },
@@ -80,6 +81,9 @@ function hydrateField(field, values) {
80
81
  return { ...field, value: items };
81
82
  }
82
83
  const rawValue = storedValue !== void 0 ? storedValue : defaultValue !== void 0 ? defaultValue : null;
84
+ if (field.type === "password") {
85
+ return { ...field, value: "" };
86
+ }
83
87
  const value = field.type === "textarea" && field.isJson && rawValue !== null && typeof rawValue === "object" ? JSON.stringify(rawValue, null, 2) : rawValue;
84
88
  const hydrated = { ...field, value };
85
89
  return hydrated;
@@ -182,7 +186,19 @@ const DecoderSessionConfigSchema = zod.z.object({
182
186
  * on every line so `grep tag=broker:5/high` filters one camera
183
187
  * profile cleanly.
184
188
  */
185
- tag: zod.z.string().optional()
189
+ tag: zod.z.string().optional(),
190
+ /**
191
+ * Where the session delivers decoded frames (Phase 5 / D9):
192
+ *
193
+ * - `'callback'` (default) — the legacy pixel path: decoded frames are
194
+ * buffered as `DecodedFrame`s and drained via `pullFrames`.
195
+ * - `'shm'` — the shared-memory frame plane: decoded frames are written
196
+ * into an OS shared-memory ring and drained as zero-pixel
197
+ * `FrameHandle`s via `pullHandles`. A session is one mode or the
198
+ * other — `pullFrames` returns nothing for an `'shm'` session and
199
+ * `pullHandles` returns nothing for a `'callback'` session.
200
+ */
201
+ frameSink: zod.z.enum(["callback", "shm"]).default("callback")
186
202
  });
187
203
  function errMsg(err) {
188
204
  if (err instanceof Error) return err.message;
@@ -768,6 +784,9 @@ function method(input, output, options) {
768
784
  function event(data) {
769
785
  return { data };
770
786
  }
787
+ function isDeviceConfigCap(def) {
788
+ return def?.deviceConfig !== void 0;
789
+ }
771
790
  const AudioClassSummarySchema = zod.z.object({
772
791
  className: zod.z.string(),
773
792
  /** Number of windows (chunks) where this class was the top hit. */
@@ -1070,6 +1089,53 @@ const DecodedFrameSchema = zod.z.object({
1070
1089
  format: zod.z.enum(["jpeg", "rgb", "bgr", "yuv420", "gray"]),
1071
1090
  timestamp: zod.z.number()
1072
1091
  });
1092
+ const FrameHandleSchema = zod.z.object({
1093
+ shmId: zod.z.string(),
1094
+ slot: zod.z.number().int().nonnegative(),
1095
+ seq: zod.z.number().int().nonnegative(),
1096
+ width: zod.z.number().int().positive(),
1097
+ height: zod.z.number().int().positive(),
1098
+ format: zod.z.enum(["jpeg", "rgb", "bgr", "yuv420", "gray"]),
1099
+ pts: zod.z.number(),
1100
+ byteLength: zod.z.number().int().nonnegative(),
1101
+ nodeId: zod.z.string(),
1102
+ slotCount: zod.z.number().int().positive()
1103
+ });
1104
+ const FrameHandleFormatSchema = zod.z.enum(["rgb", "bgr", "yuv420", "gray"]);
1105
+ const SubscribeFramesInputSchema = zod.z.object({
1106
+ brokerId: zod.z.string(),
1107
+ format: FrameHandleFormatSchema,
1108
+ /**
1109
+ * Optional reader-side cadence hint in frames per second. The broker does
1110
+ * NOT throttle — latest-wins ring reads drop frames implicitly for a slow
1111
+ * consumer. The value is echoed back in `SubscribeFramesResult.maxFps` so
1112
+ * the consumer can pace its own `pullFrameHandles` polling.
1113
+ */
1114
+ maxFps: zod.z.number().positive().optional(),
1115
+ /** Short caller-identity tag (`motion`, `detection`, …) for diagnostics. */
1116
+ tag: zod.z.string().optional()
1117
+ });
1118
+ const SubscribeFramesResultSchema = zod.z.object({
1119
+ /** Opaque id the consumer passes to `pullFrameHandles` / `unsubscribeFrames`. */
1120
+ subscriptionId: zod.z.string(),
1121
+ /** Reader-side cadence hint (frames/s) — echoes `SubscribeFramesInput.maxFps`. */
1122
+ maxFps: zod.z.number().nonnegative()
1123
+ });
1124
+ const DecodedAudioChunkSchema = zod.z.object({
1125
+ data: zod.z.instanceof(Uint8Array),
1126
+ sampleRate: zod.z.number().int().positive(),
1127
+ channels: zod.z.number().int().positive(),
1128
+ timestamp: zod.z.number()
1129
+ });
1130
+ const SubscribeAudioChunksInputSchema = zod.z.object({
1131
+ brokerId: zod.z.string(),
1132
+ /** Short caller-identity tag (`audio-analyzer`, …) for `listClients`. */
1133
+ tag: zod.z.string().optional()
1134
+ });
1135
+ const SubscribeAudioChunksResultSchema = zod.z.object({
1136
+ /** Opaque id passed to `pullAudioChunks` / `unsubscribeAudioChunks`. */
1137
+ subscriptionId: zod.z.string()
1138
+ });
1073
1139
  const BrokerStatusSchema$1 = zod.z.enum(["idle", "connecting", "streaming", "error", "stopped"]);
1074
1140
  const BrokerStatsSchema = zod.z.object({
1075
1141
  status: BrokerStatusSchema$1,
@@ -1299,9 +1365,76 @@ const streamBrokerCapability = {
1299
1365
  zod.z.object({ released: zod.z.boolean(), refcount: zod.z.number().int().nonnegative() }),
1300
1366
  { kind: "mutation", auth: "admin" }
1301
1367
  ),
1302
- getBroker: method(
1303
- zod.z.object({ brokerId: zod.z.string() }),
1304
- zod.z.custom()
1368
+ /**
1369
+ * ── Decoded audio-chunk plane (Phase 5 / D9) ──────────────────────
1370
+ *
1371
+ * The serialisable replacement for the live-object `IStreamBroker.
1372
+ * onDecodedAudioChunk` callback path. Unlike the video frame plane,
1373
+ * audio chunks are tiny (a ~500ms PCM window is a few KB) so they ship
1374
+ * their bytes INLINE over tRPC — no shared-memory ring. A consumer:
1375
+ *
1376
+ * 1. `subscribeAudioChunks({ brokerId, tag })` — the broker registers
1377
+ * a per-subscription bounded FIFO queue and returns a
1378
+ * `subscriptionId`.
1379
+ * 2. polls `pullAudioChunks({ subscriptionId, maxCount })` — drains
1380
+ * `DecodedAudioChunk[]` in arrival order (FIFO, no latest-wins
1381
+ * drop: an audio gap is audible / breaks an analysis window). The
1382
+ * queue is generously sized; it only drops its oldest chunk if a
1383
+ * truly stalled consumer lets it overflow.
1384
+ * 3. `unsubscribeAudioChunks({ subscriptionId })` on teardown.
1385
+ */
1386
+ subscribeAudioChunks: method(
1387
+ SubscribeAudioChunksInputSchema,
1388
+ SubscribeAudioChunksResultSchema,
1389
+ { kind: "mutation" }
1390
+ ),
1391
+ pullAudioChunks: method(
1392
+ zod.z.object({
1393
+ subscriptionId: zod.z.string(),
1394
+ maxCount: zod.z.number().int().positive().default(8)
1395
+ }),
1396
+ zod.z.array(DecodedAudioChunkSchema).readonly()
1397
+ ),
1398
+ unsubscribeAudioChunks: method(
1399
+ zod.z.object({ subscriptionId: zod.z.string() }),
1400
+ zod.z.object({ released: zod.z.boolean() }),
1401
+ { kind: "mutation" }
1402
+ ),
1403
+ /**
1404
+ * ── Shared-memory frame plane (Phase 5 / D9) ──────────────────────
1405
+ *
1406
+ * The handle-based replacement for the live-object `IStreamBroker.
1407
+ * onDecodedFrame` callback path. A consumer:
1408
+ *
1409
+ * 1. `subscribeFrames({ brokerId, format })` — the broker spins up
1410
+ * (or reuses) a `frameSink: 'shm'` decoder session producing that
1411
+ * `format` and returns a `subscriptionId`.
1412
+ * 2. polls `pullFrameHandles({ subscriptionId, maxCount })` — drains
1413
+ * zero-pixel `FrameHandle[]`; each handle is fed to a
1414
+ * `FrameRingReader` that opens the named shm segment and reads the
1415
+ * pixels back zero-copy.
1416
+ * 3. `unsubscribeFrames({ subscriptionId })` on teardown.
1417
+ *
1418
+ * The broker keeps one shm ring per `(brokerId, format)` actually
1419
+ * requested — no broker-side `sharp` conversion. fps throttling is
1420
+ * implicit (latest-wins ring reads drop frames for a slow consumer).
1421
+ */
1422
+ subscribeFrames: method(
1423
+ SubscribeFramesInputSchema,
1424
+ SubscribeFramesResultSchema,
1425
+ { kind: "mutation" }
1426
+ ),
1427
+ pullFrameHandles: method(
1428
+ zod.z.object({
1429
+ subscriptionId: zod.z.string(),
1430
+ maxCount: zod.z.number().int().positive().default(4)
1431
+ }),
1432
+ zod.z.array(FrameHandleSchema).readonly()
1433
+ ),
1434
+ unsubscribeFrames: method(
1435
+ zod.z.object({ subscriptionId: zod.z.string() }),
1436
+ zod.z.object({ released: zod.z.boolean() }),
1437
+ { kind: "mutation" }
1305
1438
  ),
1306
1439
  setPreBufferDuration: method(
1307
1440
  zod.z.object({ brokerId: zod.z.string(), seconds: zod.z.number().min(0).max(30) }),
@@ -1357,6 +1490,8 @@ const cameraStreamsCapability = {
1357
1490
  name: "camera-streams",
1358
1491
  scope: "device",
1359
1492
  mode: "singleton",
1493
+ kind: "wrapper",
1494
+ defaultActive: true,
1360
1495
  deviceTypes: [DeviceType.Camera],
1361
1496
  methods: {
1362
1497
  getCameraStreams: method(
@@ -1652,6 +1787,8 @@ const motionDetectionCapability = {
1652
1787
  name: "motion-detection",
1653
1788
  scope: "device",
1654
1789
  mode: "singleton",
1790
+ kind: "wrapper",
1791
+ defaultActive: true,
1655
1792
  exposesDeviceSettings: true,
1656
1793
  methods: {
1657
1794
  analyze: method(
@@ -2523,6 +2660,42 @@ const motionTriggerCapability = {
2523
2660
  },
2524
2661
  runtimeState: MotionTriggerRuntimeStateSchema
2525
2662
  };
2663
+ const MotionZoneStatusSchema = zod.z.object({
2664
+ enabled: zod.z.boolean(),
2665
+ sensitivity: zod.z.number(),
2666
+ /** Row-major active-cell grid. Length = gridWidth*gridHeight (see getOptions). */
2667
+ cells: zod.z.array(zod.z.boolean()),
2668
+ lastFetchedAt: zod.z.number()
2669
+ });
2670
+ const MotionZoneOptionsSchema = zod.z.object({
2671
+ gridWidth: zod.z.number(),
2672
+ gridHeight: zod.z.number(),
2673
+ sensitivity: zod.z.object({ min: zod.z.number(), max: zod.z.number(), step: zod.z.number() })
2674
+ });
2675
+ const MotionZonePatchSchema = zod.z.object({
2676
+ enabled: zod.z.boolean().optional(),
2677
+ sensitivity: zod.z.number().optional(),
2678
+ cells: zod.z.array(zod.z.boolean()).optional()
2679
+ });
2680
+ const motionZonesCapability = {
2681
+ name: "motion-zones",
2682
+ scope: "device",
2683
+ mode: "singleton",
2684
+ deviceTypes: [DeviceType.Camera],
2685
+ deviceConfig: {
2686
+ ui: { kind: "widget", widgetId: "host/motion-zones-grid", tab: "motion", label: "Motion Zones" }
2687
+ },
2688
+ methods: {
2689
+ getOptions: method(zod.z.object({ deviceId: zod.z.number() }), MotionZoneOptionsSchema),
2690
+ setZone: method(
2691
+ zod.z.object({ deviceId: zod.z.number(), patch: MotionZonePatchSchema }),
2692
+ zod.z.void(),
2693
+ { kind: "mutation", auth: "admin" }
2694
+ )
2695
+ },
2696
+ status: { schema: MotionZoneStatusSchema, kind: "poll" },
2697
+ runtimeState: MotionZoneStatusSchema
2698
+ };
2526
2699
  const AutotrackTargetTypeSchema = zod.z.string().describe("Vendor target string (people/vehicle/pet); empty = camera default");
2527
2700
  const PtzAutotrackSettingsSchema = zod.z.object({
2528
2701
  targetType: AutotrackTargetTypeSchema,
@@ -2561,6 +2734,9 @@ const ptzAutotrackCapability = {
2561
2734
  scope: "device",
2562
2735
  mode: "singleton",
2563
2736
  deviceTypes: [DeviceType.Camera],
2737
+ deviceConfig: {
2738
+ ui: { kind: "widget", widgetId: "host/ptz-autotrack", tab: "ptz", topTab: true, label: "Auto-Tracking", order: 5 }
2739
+ },
2564
2740
  methods: {
2565
2741
  /**
2566
2742
  * Read the current on/off state + last-applied settings.
@@ -2627,6 +2803,111 @@ const ptzAutotrackCapability = {
2627
2803
  */
2628
2804
  runtimeState: PtzAutotrackRuntimeStateSchema
2629
2805
  };
2806
+ const StreamProfileSchema = zod.z.enum(["main", "sub", "ext"]);
2807
+ const StreamProfileConfigSchema = zod.z.object({
2808
+ width: zod.z.number(),
2809
+ height: zod.z.number(),
2810
+ codec: zod.z.enum(["h264", "h265"]),
2811
+ framerate: zod.z.number(),
2812
+ bitrate: zod.z.number(),
2813
+ // kbps
2814
+ bitrateMode: zod.z.enum(["vbr", "cbr"]).optional(),
2815
+ encoderProfile: zod.z.enum(["high", "main", "baseline"]).optional(),
2816
+ gop: zod.z.number().optional(),
2817
+ audio: zod.z.boolean().optional()
2818
+ });
2819
+ const StreamParamsStatusSchema = zod.z.object({
2820
+ /** Per-profile current config. A profile absent = the camera doesn't have it. */
2821
+ main: StreamProfileConfigSchema.optional(),
2822
+ sub: StreamProfileConfigSchema.optional(),
2823
+ ext: StreamProfileConfigSchema.optional(),
2824
+ lastFetchedAt: zod.z.number()
2825
+ });
2826
+ const StreamProfileOptionsSchema = zod.z.object({
2827
+ resolutions: zod.z.array(zod.z.object({ width: zod.z.number(), height: zod.z.number() })),
2828
+ codecs: zod.z.array(zod.z.enum(["h264", "h265"])),
2829
+ framerates: zod.z.array(zod.z.number()),
2830
+ /** Allowed bitrate values (kbps). Empty if the camera takes a free range. */
2831
+ bitrates: zod.z.array(zod.z.number()),
2832
+ /** Optional [min,max] kbps when the camera accepts a continuous range. */
2833
+ bitrateRange: zod.z.tuple([zod.z.number(), zod.z.number()]).optional(),
2834
+ supportsBitrateMode: zod.z.boolean(),
2835
+ supportsEncoderProfile: zod.z.boolean(),
2836
+ supportsGop: zod.z.boolean(),
2837
+ /** Allowed GOP / keyframe-interval range, in seconds — drives the
2838
+ * I-frame-interval selector. Absent when the camera advertises GOP
2839
+ * support but no concrete range (callers then fall back to a free
2840
+ * numeric input). `{ min, max, step }` per the getOptions convention. */
2841
+ gop: zod.z.object({ min: zod.z.number(), max: zod.z.number(), step: zod.z.number() }).optional()
2842
+ });
2843
+ const StreamParamsOptionsSchema = zod.z.object({
2844
+ main: StreamProfileOptionsSchema.optional(),
2845
+ sub: StreamProfileOptionsSchema.optional(),
2846
+ ext: StreamProfileOptionsSchema.optional()
2847
+ });
2848
+ const StreamProfilePatchSchema = zod.z.object({
2849
+ width: zod.z.number().optional(),
2850
+ height: zod.z.number().optional(),
2851
+ codec: zod.z.enum(["h264", "h265"]).optional(),
2852
+ framerate: zod.z.number().optional(),
2853
+ bitrate: zod.z.number().optional(),
2854
+ bitrateMode: zod.z.enum(["vbr", "cbr"]).optional(),
2855
+ encoderProfile: zod.z.enum(["high", "main", "baseline"]).optional(),
2856
+ gop: zod.z.number().optional(),
2857
+ audio: zod.z.boolean().optional()
2858
+ });
2859
+ const streamParamsCapability = {
2860
+ name: "stream-params",
2861
+ scope: "device",
2862
+ mode: "singleton",
2863
+ deviceTypes: [DeviceType.Camera],
2864
+ deviceConfig: {
2865
+ ui: { kind: "derived-form", builderId: "stream-params", tab: "streaming" }
2866
+ },
2867
+ methods: {
2868
+ getOptions: method(
2869
+ zod.z.object({ deviceId: zod.z.number() }),
2870
+ StreamParamsOptionsSchema
2871
+ ),
2872
+ setProfile: method(
2873
+ zod.z.object({
2874
+ deviceId: zod.z.number(),
2875
+ profile: StreamProfileSchema,
2876
+ patch: StreamProfilePatchSchema
2877
+ }),
2878
+ zod.z.void(),
2879
+ { kind: "mutation", auth: "admin" }
2880
+ ),
2881
+ /**
2882
+ * Build the `ConfigUISchema` (admin-ui `ConfigFormBuilder` input
2883
+ * shape) for this camera's stream-encoder settings — one section per
2884
+ * profile (main / sub / ext) with the resolution / codec / framerate
2885
+ * / bitrate / bitrate-mode / encoder-profile / GOP controls the
2886
+ * firmware actually exposes.
2887
+ *
2888
+ * Driven by `getOptions` (camera-probed availability) + `getStatus`
2889
+ * (current per-profile config); each field's `default` is seeded
2890
+ * from the live config so the form renders the camera state in one
2891
+ * pass. Returns `null` when the camera exposes no configurable
2892
+ * stream property — the renderer then shows the unsupported message.
2893
+ *
2894
+ * Output is `z.unknown().nullable()` — the same convention every
2895
+ * other `ConfigUISchema`-returning cap method uses (`device-ops`,
2896
+ * `device-manager`); `ConfigUISchema` is a TS-only type with no
2897
+ * companion Zod schema, and a concrete object would collapse
2898
+ * unrelated AppRouter branches to `unknown` during codegen.
2899
+ */
2900
+ getConfigSchema: method(
2901
+ zod.z.object({ deviceId: zod.z.number() }),
2902
+ zod.z.unknown().nullable()
2903
+ )
2904
+ },
2905
+ status: {
2906
+ schema: StreamParamsStatusSchema,
2907
+ kind: "poll"
2908
+ },
2909
+ runtimeState: StreamParamsStatusSchema
2910
+ };
2630
2911
  const SwitchStatusSchema = zod.z.object({
2631
2912
  on: zod.z.boolean(),
2632
2913
  /** Ms epoch of the last state change. Useful for UI "X minutes ago". */
@@ -3631,6 +3912,83 @@ const adminUiCapability = {
3631
3912
  getVersion: method(zod.z.void(), VersionOutputSchema)
3632
3913
  }
3633
3914
  };
3915
+ const MethodAccessSchema = zod.z.enum(["view", "create", "delete"]);
3916
+ const AllowedProviderSchema = zod.z.union([zod.z.literal("*"), zod.z.array(zod.z.string())]);
3917
+ const AllowedDevicesSchema = zod.z.record(zod.z.string(), zod.z.union([zod.z.literal("*"), zod.z.array(zod.z.string())]));
3918
+ const CapScopeSchema = zod.z.enum(["device", "system"]);
3919
+ const TokenScopeSchema = zod.z.discriminatedUnion("type", [
3920
+ zod.z.object({
3921
+ type: zod.z.literal("category"),
3922
+ target: CapScopeSchema,
3923
+ access: zod.z.array(MethodAccessSchema).min(1)
3924
+ }),
3925
+ zod.z.object({
3926
+ type: zod.z.literal("capability"),
3927
+ target: zod.z.string(),
3928
+ access: zod.z.array(MethodAccessSchema).min(1)
3929
+ }),
3930
+ zod.z.object({
3931
+ type: zod.z.literal("addon"),
3932
+ target: zod.z.string(),
3933
+ access: zod.z.array(MethodAccessSchema).min(1)
3934
+ }),
3935
+ zod.z.object({
3936
+ type: zod.z.literal("device"),
3937
+ /**
3938
+ * One or more deviceIds (serialised as strings for wire-format
3939
+ * consistency with the rest of the union). Matcher accepts if
3940
+ * `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
3941
+ * of one scope-per-device when granting access to a set of cameras.
3942
+ */
3943
+ targets: zod.z.array(zod.z.string()).min(1),
3944
+ access: zod.z.array(MethodAccessSchema).min(1)
3945
+ })
3946
+ ]);
3947
+ const UserRecordSchema = zod.z.object({
3948
+ id: zod.z.string(),
3949
+ username: zod.z.string(),
3950
+ passwordHash: zod.z.string(),
3951
+ /**
3952
+ * Admin bypass. When true, the middleware skips the scope-access
3953
+ * check entirely. There is no other axis of privilege; the legacy
3954
+ * role enum collapsed onto this boolean in v2.
3955
+ */
3956
+ isAdmin: zod.z.boolean().default(false),
3957
+ allowedProviders: AllowedProviderSchema,
3958
+ allowedDevices: AllowedDevicesSchema,
3959
+ /**
3960
+ * Scopes granted to this user. Admins bypass; their `scopes` is
3961
+ * ignored. Non-admins without scopes are locked out of every
3962
+ * protected call.
3963
+ */
3964
+ scopes: zod.z.array(TokenScopeSchema).default([]),
3965
+ createdAt: zod.z.number(),
3966
+ updatedAt: zod.z.number()
3967
+ });
3968
+ const ApiKeyRecordSchema = zod.z.object({
3969
+ id: zod.z.string(),
3970
+ label: zod.z.string(),
3971
+ isAdmin: zod.z.boolean().default(false),
3972
+ allowedProviders: AllowedProviderSchema,
3973
+ allowedDevices: AllowedDevicesSchema,
3974
+ tokenHash: zod.z.string(),
3975
+ tokenPrefix: zod.z.string(),
3976
+ createdAt: zod.z.number(),
3977
+ lastUsedAt: zod.z.number().optional()
3978
+ });
3979
+ const ScopedTokenSchema = zod.z.object({
3980
+ id: zod.z.string(),
3981
+ userId: zod.z.string(),
3982
+ name: zod.z.string(),
3983
+ tokenHash: zod.z.string(),
3984
+ tokenPrefix: zod.z.string(),
3985
+ scopes: zod.z.array(TokenScopeSchema),
3986
+ // SQLite/JSON storage round-trips undefined → null. Use `nullish` so the
3987
+ // schema accepts both `null` (read from disk) and `undefined` (in-memory).
3988
+ expiresAt: zod.z.number().nullish(),
3989
+ lastUsedAt: zod.z.number().nullish(),
3990
+ createdAt: zod.z.number()
3991
+ });
3634
3992
  const SsoBridgeClaimsSchema = zod.z.object({
3635
3993
  userId: zod.z.string(),
3636
3994
  username: zod.z.string(),
@@ -3646,7 +4004,18 @@ const SsoBridgeClaimsSchema = zod.z.object({
3646
4004
  * JWT WITHOUT verifying the signature — the hub re-verifies on every
3647
4005
  * inbound call so trust still rests with the signing hub.
3648
4006
  */
3649
- hubUrl: zod.z.string().optional()
4007
+ hubUrl: zod.z.string().optional(),
4008
+ /** Permission scopes baked into the token. Set by the OAuth
4009
+ * account-linking grant; absent on ordinary SSO-login tokens. */
4010
+ scopes: zod.z.array(TokenScopeSchema).optional(),
4011
+ /** OAuth authorization-code binding — set only on `oauth-code` tokens. */
4012
+ redirectUri: zod.z.string().optional(),
4013
+ integrationId: zod.z.string().optional(),
4014
+ /** JWT ID — unique per issued code; consumed-set enforces single-use. */
4015
+ jti: zod.z.string().optional(),
4016
+ /** OAuth session registry id — set on `oauth-access`/`oauth-refresh`
4017
+ * tokens so the verify path can check the session is not revoked. */
4018
+ sessionId: zod.z.string().optional()
3650
4019
  });
3651
4020
  const ssoBridgeCapability = {
3652
4021
  name: "sso-bridge",
@@ -3667,6 +4036,27 @@ const ssoBridgeCapability = {
3667
4036
  )
3668
4037
  }
3669
4038
  };
4039
+ const OauthIntegrationDescriptorSchema = zod.z.object({
4040
+ /** Stable id used as the `integration=` query param, e.g. 'export-alexa'. */
4041
+ integrationId: zod.z.string(),
4042
+ /** Human label rendered on the consent page. */
4043
+ displayName: zod.z.string(),
4044
+ /** Scopes baked into every token issued for this integration. */
4045
+ requestedScopes: zod.z.array(TokenScopeSchema),
4046
+ /** Allowed redirect_uri prefixes. /api/oauth2/authorize rejects any
4047
+ * redirect_uri that does not start with one of these. Required —
4048
+ * an empty list means the integration can never complete linking. */
4049
+ allowedRedirectPrefixes: zod.z.array(zod.z.string()).min(1)
4050
+ });
4051
+ const oauthIntegrationCapability = {
4052
+ name: "oauth-integration",
4053
+ scope: "system",
4054
+ mode: "collection",
4055
+ internal: true,
4056
+ methods: {
4057
+ getDescriptor: method(zod.z.void(), OauthIntegrationDescriptorSchema)
4058
+ }
4059
+ };
3670
4060
  const PasskeySummarySchema = zod.z.object({
3671
4061
  credentialId: zod.z.string(),
3672
4062
  label: zod.z.string(),
@@ -3982,21 +4372,30 @@ const addonPagesSourceCapability = {
3982
4372
  };
3983
4373
  const WidgetHostEnum = zod.z.enum(["device-tab", "dashboard", "integration-detail"]);
3984
4374
  const WidgetSizeEnum = zod.z.enum(["xs", "sm", "md", "lg", "xl"]);
4375
+ const WidgetRemoteSchema = zod.z.object({
4376
+ remoteName: zod.z.string(),
4377
+ exposedModule: zod.z.string(),
4378
+ componentKey: zod.z.string().optional()
4379
+ });
3985
4380
  const WidgetMetadataSchema = zod.z.object({
3986
- /** Stable id within the addon — kebab-case. */
3987
- stableId: zod.z.string(),
4381
+ // ── UiContribution core (kind:'remote') ──────────────────────────
4382
+ /** Primary host tab — `'dashboard'`, `'device-tab'`, or a device-detail tab id. */
4383
+ tab: zod.z.string(),
4384
+ /** Optional sub-tab within `tab`. */
4385
+ subTab: zod.z.string().optional(),
3988
4386
  /** Operator-facing label. */
3989
4387
  label: zod.z.string(),
4388
+ /** Ordering within `(tab, subTab)`, ascending. */
4389
+ order: zod.z.number().optional(),
4390
+ /** Always `'remote'` — a widget is a Module Federation remote. */
4391
+ kind: zod.z.literal("remote"),
4392
+ /** MF remote descriptor. */
4393
+ remote: WidgetRemoteSchema,
4394
+ // ── Widget-only metadata ─────────────────────────────────────────
4395
+ /** Stable id within the addon — kebab-case. Equals `remote.componentKey`. */
4396
+ stableId: zod.z.string(),
3990
4397
  description: zod.z.string().optional(),
3991
4398
  icon: zod.z.string().optional(),
3992
- /**
3993
- * Module Federation remote name — must match the `name` field on the
3994
- * widget addon's `federation()` plugin config. Used by the host's
3995
- * `<WidgetRegistryProvider>` to call `loadRemote('<remoteName>/widgets')`.
3996
- * Conventionally `addon_<addonid>_widgets` (snake_case; MF names
3997
- * cannot contain hyphens).
3998
- */
3999
- remoteName: zod.z.string(),
4000
4399
  /**
4001
4400
  * Bundle filename inside the addon's `dist/` dir served at
4002
4401
  * `/api/addon-widgets/<addonId>/<bundle>`. With Module Federation
@@ -4005,9 +4404,9 @@ const WidgetMetadataSchema = zod.z.object({
4005
4404
  * cache-buster URL without a separate filesystem stat.
4006
4405
  */
4007
4406
  bundle: zod.z.string(),
4008
- /** Where the widget makes sense to render. */
4407
+ /** Every host the widget supports. The picker filters on this set. */
4009
4408
  hosts: zod.z.array(WidgetHostEnum).readonly(),
4010
- /** Required props the host must supply. Validated at <WidgetSlot> mount. */
4409
+ /** Required props the host must supply. Validated at `<WidgetSlot>` mount. */
4011
4410
  requires: zod.z.object({
4012
4411
  deviceContext: zod.z.boolean().default(false),
4013
4412
  integrationContext: zod.z.boolean().default(false)
@@ -4107,6 +4506,16 @@ const DEFAULT_DECODER_HWACCEL_CONFIG = {
4107
4506
  hwaccel: "auto",
4108
4507
  probedBestHwaccel: ""
4109
4508
  };
4509
+ const ShmRingStatsSchema = zod.z.object({
4510
+ sessionId: zod.z.string(),
4511
+ slotCount: zod.z.number().int(),
4512
+ slotByteLength: zod.z.number().int(),
4513
+ segmentBytes: zod.z.number().int(),
4514
+ budgetMb: zod.z.number().int(),
4515
+ framesWritten: zod.z.number().int(),
4516
+ getFrameHits: zod.z.number().int(),
4517
+ getFrameMisses: zod.z.number().int()
4518
+ });
4110
4519
  const decoderCapability = {
4111
4520
  name: "decoder",
4112
4521
  scope: "system",
@@ -4143,10 +4552,27 @@ const decoderCapability = {
4143
4552
  url: zod.z.string()
4144
4553
  }), zod.z.void()),
4145
4554
  // ── Output — polling-based frame retrieval ────────────────────
4555
+ // `pullFrames` drains the pixel `DecodedFrame[]` of a `frameSink:
4556
+ // 'callback'` session. `pullHandles` (Phase 5 / D9) drains the
4557
+ // zero-pixel `FrameHandle[]` of a `frameSink: 'shm'` session — the
4558
+ // broker hands each handle to a `FrameRingReader` that opens the
4559
+ // named segment and reads the pixels back zero-copy. A session is
4560
+ // one mode or the other; the unmatched method returns an empty
4561
+ // array.
4146
4562
  pullFrames: method(zod.z.object({
4147
4563
  sessionId: zod.z.string(),
4148
4564
  maxCount: zod.z.number().default(1)
4149
4565
  }), zod.z.array(DecodedFrameSchema)),
4566
+ pullHandles: method(zod.z.object({
4567
+ sessionId: zod.z.string(),
4568
+ maxCount: zod.z.number().default(1)
4569
+ }), zod.z.array(FrameHandleSchema)),
4570
+ // ── Frame fetch (Phase 5 / D9 downstream access) ──────────────
4571
+ // Read the pixels a FrameHandle refers to from this node's shm ring.
4572
+ // Returns null when the slot was already recycled (latest-wins).
4573
+ getFrame: method(zod.z.object({ handle: FrameHandleSchema }), DecodedFrameSchema.nullable()),
4574
+ // shm ring usage stats for a session.
4575
+ getShmStats: method(zod.z.object({ sessionId: zod.z.string() }), ShmRingStatsSchema.nullable()),
4150
4576
  // ── Control ───────────────────────────────────────────────────
4151
4577
  updateConfig: method(zod.z.object({
4152
4578
  sessionId: zod.z.string(),
@@ -4299,6 +4725,8 @@ const webrtcSessionCapability = {
4299
4725
  name: "webrtc-session",
4300
4726
  scope: "device",
4301
4727
  mode: "singleton",
4728
+ kind: "wrapper",
4729
+ defaultActive: true,
4302
4730
  deviceTypes: [DeviceType.Camera],
4303
4731
  methods: {
4304
4732
  /**
@@ -4817,6 +5245,8 @@ const audioAnalysisCapability = {
4817
5245
  name: "audio-analysis",
4818
5246
  scope: "device",
4819
5247
  mode: "singleton",
5248
+ kind: "wrapper",
5249
+ defaultActive: true,
4820
5250
  deviceTypes: [DeviceType.Camera],
4821
5251
  exposesDeviceSettings: true,
4822
5252
  methods: {
@@ -5356,8 +5786,8 @@ const deviceManagerCapability = {
5356
5786
  /**
5357
5787
  * Return the addon ids that declared a wrapper provider for `capName`.
5358
5788
  * Backs the device-bindings UI's wrapper-picker dropdown. Entries are
5359
- * sourced from `CapabilityRegistry.wrapperProviders`, populated at
5360
- * `ProviderRegistration.kind === 'wrapper'` time.
5789
+ * sourced from `CapabilityRegistry.wrapperProviders`, populated when
5790
+ * the cap definition declares `kind: 'wrapper'`.
5361
5791
  */
5362
5792
  listWrappersForCap: method(
5363
5793
  zod.z.object({ capName: zod.z.string() }),
@@ -5752,6 +6182,8 @@ const snapshotCapability = {
5752
6182
  name: "snapshot",
5753
6183
  scope: "device",
5754
6184
  mode: "singleton",
6185
+ kind: "wrapper",
6186
+ defaultActive: true,
5755
6187
  deviceTypes: [DeviceType.Camera],
5756
6188
  // Owns per-device snapshot settings (preferred stream, debug logging).
5757
6189
  // The three DeviceSettingsContribution methods are auto-added to the
@@ -6101,6 +6533,8 @@ const detectionPipelineCapability = {
6101
6533
  name: "detection-pipeline",
6102
6534
  scope: "device",
6103
6535
  mode: "singleton",
6536
+ kind: "wrapper",
6537
+ defaultActive: true,
6104
6538
  deviceTypes: [DeviceType.Camera],
6105
6539
  exposesDeviceSettings: true,
6106
6540
  methods: {}
@@ -6209,6 +6643,8 @@ const pipelineAnalyticsCapability = {
6209
6643
  name: "pipeline-analytics",
6210
6644
  scope: "device",
6211
6645
  mode: "singleton",
6646
+ kind: "wrapper",
6647
+ defaultActive: true,
6212
6648
  deviceTypes: [DeviceType.Camera],
6213
6649
  exposesDeviceSettings: true,
6214
6650
  methods: {
@@ -6498,11 +6934,26 @@ const PtzMoveCommandSchema = zod.z.object({
6498
6934
  zoom: zod.z.number().optional(),
6499
6935
  speed: zod.z.number().optional()
6500
6936
  });
6937
+ const PtzStatusSchema = PtzPositionSchema.extend({ autofocus: zod.z.boolean() });
6938
+ const PtzOptionsSchema = zod.z.object({
6939
+ hasPan: zod.z.boolean(),
6940
+ hasTilt: zod.z.boolean(),
6941
+ hasZoom: zod.z.boolean(),
6942
+ supportsPresets: zod.z.boolean(),
6943
+ /** Max number of named presets the camera supports, when known. */
6944
+ maxPresets: zod.z.number().optional(),
6945
+ /** Whether the camera exposes a controllable autofocus toggle
6946
+ * (boolean `hasX` per the getOptions availability convention). */
6947
+ hasAutofocus: zod.z.boolean()
6948
+ });
6501
6949
  const ptzCapability = {
6502
6950
  name: "ptz",
6503
6951
  scope: "device",
6504
6952
  mode: "singleton",
6505
6953
  deviceTypes: [DeviceType.Camera],
6954
+ deviceConfig: {
6955
+ ui: { kind: "widget", widgetId: "host/ptz-panel", tab: "ptz", topTab: true, label: "PTZ", order: 0 }
6956
+ },
6506
6957
  methods: {
6507
6958
  move: method(
6508
6959
  PtzMoveCommandSchema.extend({ deviceId: zod.z.number() }),
@@ -6528,6 +6979,20 @@ const ptzCapability = {
6528
6979
  zod.z.void(),
6529
6980
  { kind: "mutation" }
6530
6981
  ),
6982
+ savePreset: method(
6983
+ zod.z.object({ deviceId: zod.z.number(), presetId: zod.z.string(), name: zod.z.string() }),
6984
+ zod.z.void(),
6985
+ { kind: "mutation", auth: "admin" }
6986
+ ),
6987
+ deletePreset: method(
6988
+ zod.z.object({ deviceId: zod.z.number(), presetId: zod.z.string() }),
6989
+ zod.z.void(),
6990
+ { kind: "mutation", auth: "admin" }
6991
+ ),
6992
+ getOptions: method(
6993
+ zod.z.object({ deviceId: zod.z.number() }),
6994
+ PtzOptionsSchema
6995
+ ),
6531
6996
  goHome: method(
6532
6997
  zod.z.object({ deviceId: zod.z.number() }),
6533
6998
  zod.z.void(),
@@ -6542,10 +7007,17 @@ const ptzCapability = {
6542
7007
  getPosition: method(
6543
7008
  zod.z.object({ deviceId: zod.z.number() }),
6544
7009
  PtzPositionSchema
7010
+ ),
7011
+ /** Toggle the camera's autofocus. Only meaningful when
7012
+ * `getOptions().hasAutofocus` is true. */
7013
+ setAutofocus: method(
7014
+ zod.z.object({ deviceId: zod.z.number(), enabled: zod.z.boolean() }),
7015
+ zod.z.void(),
7016
+ { kind: "mutation" }
6545
7017
  )
6546
7018
  },
6547
7019
  status: {
6548
- schema: PtzPositionSchema,
7020
+ schema: PtzStatusSchema,
6549
7021
  kind: "command-driven"
6550
7022
  }
6551
7023
  };
@@ -7569,83 +8041,6 @@ const meshNetworkCapability = {
7569
8041
  // tabs driven by this cap.
7570
8042
  }
7571
8043
  };
7572
- const MethodAccessSchema = zod.z.enum(["view", "create", "delete"]);
7573
- const AllowedProviderSchema = zod.z.union([zod.z.literal("*"), zod.z.array(zod.z.string())]);
7574
- const AllowedDevicesSchema = zod.z.record(zod.z.string(), zod.z.union([zod.z.literal("*"), zod.z.array(zod.z.string())]));
7575
- const CapScopeSchema = zod.z.enum(["device", "system"]);
7576
- const TokenScopeSchema = zod.z.discriminatedUnion("type", [
7577
- zod.z.object({
7578
- type: zod.z.literal("category"),
7579
- target: CapScopeSchema,
7580
- access: zod.z.array(MethodAccessSchema).min(1)
7581
- }),
7582
- zod.z.object({
7583
- type: zod.z.literal("capability"),
7584
- target: zod.z.string(),
7585
- access: zod.z.array(MethodAccessSchema).min(1)
7586
- }),
7587
- zod.z.object({
7588
- type: zod.z.literal("addon"),
7589
- target: zod.z.string(),
7590
- access: zod.z.array(MethodAccessSchema).min(1)
7591
- }),
7592
- zod.z.object({
7593
- type: zod.z.literal("device"),
7594
- /**
7595
- * One or more deviceIds (serialised as strings for wire-format
7596
- * consistency with the rest of the union). Matcher accepts if
7597
- * `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
7598
- * of one scope-per-device when granting access to a set of cameras.
7599
- */
7600
- targets: zod.z.array(zod.z.string()).min(1),
7601
- access: zod.z.array(MethodAccessSchema).min(1)
7602
- })
7603
- ]);
7604
- const UserRecordSchema = zod.z.object({
7605
- id: zod.z.string(),
7606
- username: zod.z.string(),
7607
- passwordHash: zod.z.string(),
7608
- /**
7609
- * Admin bypass. When true, the middleware skips the scope-access
7610
- * check entirely. There is no other axis of privilege; the legacy
7611
- * role enum collapsed onto this boolean in v2.
7612
- */
7613
- isAdmin: zod.z.boolean().default(false),
7614
- allowedProviders: AllowedProviderSchema,
7615
- allowedDevices: AllowedDevicesSchema,
7616
- /**
7617
- * Scopes granted to this user. Admins bypass; their `scopes` is
7618
- * ignored. Non-admins without scopes are locked out of every
7619
- * protected call.
7620
- */
7621
- scopes: zod.z.array(TokenScopeSchema).default([]),
7622
- createdAt: zod.z.number(),
7623
- updatedAt: zod.z.number()
7624
- });
7625
- const ApiKeyRecordSchema = zod.z.object({
7626
- id: zod.z.string(),
7627
- label: zod.z.string(),
7628
- isAdmin: zod.z.boolean().default(false),
7629
- allowedProviders: AllowedProviderSchema,
7630
- allowedDevices: AllowedDevicesSchema,
7631
- tokenHash: zod.z.string(),
7632
- tokenPrefix: zod.z.string(),
7633
- createdAt: zod.z.number(),
7634
- lastUsedAt: zod.z.number().optional()
7635
- });
7636
- const ScopedTokenSchema = zod.z.object({
7637
- id: zod.z.string(),
7638
- userId: zod.z.string(),
7639
- name: zod.z.string(),
7640
- tokenHash: zod.z.string(),
7641
- tokenPrefix: zod.z.string(),
7642
- scopes: zod.z.array(TokenScopeSchema),
7643
- // SQLite/JSON storage round-trips undefined → null. Use `nullish` so the
7644
- // schema accepts both `null` (read from disk) and `undefined` (in-memory).
7645
- expiresAt: zod.z.number().nullish(),
7646
- lastUsedAt: zod.z.number().nullish(),
7647
- createdAt: zod.z.number()
7648
- });
7649
8044
  const UserSummarySchema = zod.z.object({
7650
8045
  id: zod.z.string(),
7651
8046
  username: zod.z.string(),
@@ -7718,6 +8113,16 @@ const CreateScopedTokenResultSchema = zod.z.object({
7718
8113
  token: zod.z.string(),
7719
8114
  record: ScopedTokenSummarySchema
7720
8115
  });
8116
+ const OauthSessionSummarySchema = zod.z.object({
8117
+ id: zod.z.string(),
8118
+ userId: zod.z.string(),
8119
+ username: zod.z.string(),
8120
+ integrationId: zod.z.string(),
8121
+ scopes: zod.z.array(TokenScopeSchema),
8122
+ createdAt: zod.z.number(),
8123
+ lastUsedAt: zod.z.number(),
8124
+ revokedAt: zod.z.number().nullable()
8125
+ });
7721
8126
  const TotpSetupResultSchema = zod.z.object({
7722
8127
  secret: zod.z.string(),
7723
8128
  otpauthUrl: zod.z.string()
@@ -7796,6 +8201,66 @@ const userManagementCapability = {
7796
8201
  zod.z.object({ userId: zod.z.string(), code: zod.z.string() }),
7797
8202
  zod.z.object({ valid: zod.z.boolean() }),
7798
8203
  { kind: "mutation", access: "view" }
8204
+ ),
8205
+ // ── OAuth account-linking grant ────────────────────────────────
8206
+ //
8207
+ // Core's /oauth2/* endpoints delegate here. Tokens are sso-bridge
8208
+ // JWTs (kinds oauth-code / oauth-access / oauth-refresh) and ALWAYS
8209
+ // carry isAdmin:false — the operator login proves hub control; the
8210
+ // issued token is minimal (device scope only).
8211
+ oauthIssueCode: method(
8212
+ zod.z.object({
8213
+ integrationId: zod.z.string(),
8214
+ userId: zod.z.string(),
8215
+ username: zod.z.string(),
8216
+ scopes: zod.z.array(TokenScopeSchema),
8217
+ redirectUri: zod.z.string(),
8218
+ hubUrl: zod.z.string()
8219
+ }),
8220
+ zod.z.object({ code: zod.z.string() }),
8221
+ { kind: "mutation", access: "create" }
8222
+ ),
8223
+ oauthExchangeCode: method(
8224
+ zod.z.object({ code: zod.z.string(), redirectUri: zod.z.string() }),
8225
+ zod.z.object({
8226
+ accessToken: zod.z.string(),
8227
+ refreshToken: zod.z.string(),
8228
+ expiresIn: zod.z.number()
8229
+ }).nullable(),
8230
+ { kind: "mutation", access: "view" }
8231
+ ),
8232
+ oauthRefresh: method(
8233
+ zod.z.object({ refreshToken: zod.z.string() }),
8234
+ zod.z.object({
8235
+ accessToken: zod.z.string(),
8236
+ refreshToken: zod.z.string(),
8237
+ expiresIn: zod.z.number()
8238
+ }).nullable(),
8239
+ { kind: "mutation", access: "view" }
8240
+ ),
8241
+ oauthVerifyAccessToken: method(
8242
+ zod.z.object({ token: zod.z.string() }),
8243
+ zod.z.object({
8244
+ userId: zod.z.string(),
8245
+ username: zod.z.string(),
8246
+ scopes: zod.z.array(TokenScopeSchema)
8247
+ }).nullable(),
8248
+ { access: "view" }
8249
+ ),
8250
+ // ── OAuth linked-session management (Phase D) ──────────────────
8251
+ //
8252
+ // The admin UI lists active account-linking sessions and revokes
8253
+ // them; revocation makes the linked integration's tokens fail
8254
+ // verification immediately.
8255
+ listOauthSessions: method(
8256
+ zod.z.void(),
8257
+ zod.z.array(OauthSessionSummarySchema),
8258
+ { auth: "admin" }
8259
+ ),
8260
+ revokeOauthSession: method(
8261
+ zod.z.object({ id: zod.z.string() }),
8262
+ zod.z.object({ success: zod.z.boolean() }),
8263
+ { kind: "mutation", auth: "admin", access: "delete" }
7799
8264
  )
7800
8265
  }
7801
8266
  };
@@ -8025,6 +8490,19 @@ const nodesCapability = {
8025
8490
  zod.z.record(zod.z.string(), ClusterAddonStatusEntrySchema),
8026
8491
  { auth: "admin" }
8027
8492
  ),
8493
+ getCapUsageGraph: method(
8494
+ zod.z.object({
8495
+ windowSeconds: zod.z.number().int().positive().max(300).default(60)
8496
+ }),
8497
+ zod.z.array(zod.z.object({
8498
+ callerAddonId: zod.z.string(),
8499
+ providerAddonId: zod.z.string(),
8500
+ capName: zod.z.string(),
8501
+ callsPerMin: zod.z.number(),
8502
+ lastCallAtMs: zod.z.number()
8503
+ })).readonly(),
8504
+ { auth: "admin" }
8505
+ ),
8028
8506
  /**
8029
8507
  * Direct per-node addon listing — calls `$agent.status` on the target
8030
8508
  * node (or returns the hub registry for `nodeId === 'hub'`) and surfaces
@@ -8675,6 +9153,7 @@ exports.DEFAULT_DECODER_HWACCEL_CONFIG = DEFAULT_DECODER_HWACCEL_CONFIG;
8675
9153
  exports.DEVICE_PROFILES = DEVICE_PROFILES;
8676
9154
  exports.DEVICE_SETTINGS_CONTRIBUTION_METHODS = DEVICE_SETTINGS_CONTRIBUTION_METHODS;
8677
9155
  exports.DEVICE_STATUS_METHOD = DEVICE_STATUS_METHOD;
9156
+ exports.DecodedAudioChunkSchema = DecodedAudioChunkSchema;
8678
9157
  exports.DecodedFrameSchema = DecodedFrameSchema;
8679
9158
  exports.DecoderAssignmentSchema = DecoderAssignmentSchema;
8680
9159
  exports.DecoderSessionConfigSchema = DecoderSessionConfigSchema;
@@ -8709,6 +9188,8 @@ exports.ExposedResourceSchema = ExposedResourceSchema;
8709
9188
  exports.FeatureManifestSchema = FeatureManifestSchema;
8710
9189
  exports.FeatureProbeStatusSchema = FeatureProbeStatusSchema;
8711
9190
  exports.FinalizeUploadInputSchema = FinalizeUploadInputSchema;
9191
+ exports.FrameHandleFormatSchema = FrameHandleFormatSchema;
9192
+ exports.FrameHandleSchema = FrameHandleSchema;
8712
9193
  exports.FrameInputSchema = FrameInputSchema;
8713
9194
  exports.GetStreamWithCodecInputSchema = GetStreamWithCodecInputSchema;
8714
9195
  exports.GlobalMetricsSchema = GlobalMetricsSchema;
@@ -8739,6 +9220,9 @@ exports.MotionSourcesSchema = MotionSourcesSchema;
8739
9220
  exports.MotionStatusSchema = MotionStatusSchema;
8740
9221
  exports.MotionTriggerRuntimeStateSchema = MotionTriggerRuntimeStateSchema;
8741
9222
  exports.MotionTriggerStatusSchema = MotionTriggerStatusSchema;
9223
+ exports.MotionZoneOptionsSchema = MotionZoneOptionsSchema;
9224
+ exports.MotionZonePatchSchema = MotionZonePatchSchema;
9225
+ exports.MotionZoneStatusSchema = MotionZoneStatusSchema;
8742
9226
  exports.NativeDetectionSchema = NativeDetectionSchema;
8743
9227
  exports.NativeObjectClassEnum = NativeObjectClassEnum;
8744
9228
  exports.NativeObjectDetectionStatusSchema = NativeObjectDetectionStatusSchema;
@@ -8748,6 +9232,7 @@ exports.NetworkEndpointSchema = NetworkEndpointSchema;
8748
9232
  exports.NotificationHistoryEntrySchema = NotificationHistoryEntrySchema;
8749
9233
  exports.NotificationRuleSchema = NotificationRuleSchema;
8750
9234
  exports.NotificationSchema = NotificationSchema;
9235
+ exports.OauthIntegrationDescriptorSchema = OauthIntegrationDescriptorSchema;
8751
9236
  exports.ObjectEventSchema = ObjectEventSchema;
8752
9237
  exports.OrchestratorMetricsSchema = OrchestratorMetricsSchema;
8753
9238
  exports.OsdOverlayKindEnum = OsdOverlayKindEnum;
@@ -8780,6 +9265,7 @@ exports.PtzAutotrackTargetOptionSchema = PtzAutotrackTargetOptionSchema;
8780
9265
  exports.PtzMoveCommandSchema = PtzMoveCommandSchema;
8781
9266
  exports.PtzPositionSchema = PtzPositionSchema;
8782
9267
  exports.PtzPresetSchema = PtzPresetSchema;
9268
+ exports.PtzStatusSchema = PtzStatusSchema;
8783
9269
  exports.QueryFilterSchema = QueryFilterSchema;
8784
9270
  exports.ReadChunkInputSchema = ReadChunkInputSchema;
8785
9271
  exports.RegisteredStreamSchema = RegisteredStreamSchema;
@@ -8801,6 +9287,7 @@ exports.SettingsPatchSchema = SettingsPatchSchema;
8801
9287
  exports.SettingsRecordSchema = SettingsRecordSchema;
8802
9288
  exports.SettingsSchemaWithValuesSchema = SettingsSchemaWithValuesSchema;
8803
9289
  exports.SettingsUpdateResultSchema = SettingsUpdateResultSchema;
9290
+ exports.ShmRingStatsSchema = ShmRingStatsSchema;
8804
9291
  exports.SmtpStatusSchema = SmtpStatusSchema;
8805
9292
  exports.SnapshotImageSchema = SnapshotImageSchema;
8806
9293
  exports.SpatialDetectionSchema = SpatialDetectionSchema;
@@ -8813,8 +9300,18 @@ exports.StorageLocationTypeSchema = StorageLocationTypeSchema;
8813
9300
  exports.StreamFormatSchema = StreamFormatSchema;
8814
9301
  exports.StreamInfoSchema = StreamInfoSchema;
8815
9302
  exports.StreamNetworkStatsSchema = StreamNetworkStatsSchema;
9303
+ exports.StreamParamsOptionsSchema = StreamParamsOptionsSchema;
9304
+ exports.StreamParamsStatusSchema = StreamParamsStatusSchema;
9305
+ exports.StreamProfileConfigSchema = StreamProfileConfigSchema;
9306
+ exports.StreamProfileOptionsSchema = StreamProfileOptionsSchema;
9307
+ exports.StreamProfilePatchSchema = StreamProfilePatchSchema;
9308
+ exports.StreamProfileSchema = StreamProfileSchema;
8816
9309
  exports.StreamSourceEntrySchema = StreamSourceEntrySchema$1;
8817
9310
  exports.StreamSourceSchema = StreamSourceSchema;
9311
+ exports.SubscribeAudioChunksInputSchema = SubscribeAudioChunksInputSchema;
9312
+ exports.SubscribeAudioChunksResultSchema = SubscribeAudioChunksResultSchema;
9313
+ exports.SubscribeFramesInputSchema = SubscribeFramesInputSchema;
9314
+ exports.SubscribeFramesResultSchema = SubscribeFramesResultSchema;
8818
9315
  exports.SwitchStatusSchema = SwitchStatusSchema;
8819
9316
  exports.SystemMetricsSchema = SystemMetricsSchema;
8820
9317
  exports.TestConnectionResultSchema = TestConnectionResultSchema;
@@ -8839,6 +9336,7 @@ exports.WebrtcStreamChoiceSchema = WebrtcStreamChoiceSchema;
8839
9336
  exports.WebrtcStreamTargetSchema = WebrtcStreamTargetSchema;
8840
9337
  exports.WidgetHostEnum = WidgetHostEnum;
8841
9338
  exports.WidgetMetadataSchema = WidgetMetadataSchema;
9339
+ exports.WidgetRemoteSchema = WidgetRemoteSchema;
8842
9340
  exports.WidgetSizeEnum = WidgetSizeEnum;
8843
9341
  exports.WriteChunkInputSchema = WriteChunkInputSchema;
8844
9342
  exports.YAMNET_TO_MACRO = YAMNET_TO_MACRO;
@@ -8892,6 +9390,7 @@ exports.getAudioMacroClassIds = getAudioMacroClassIds;
8892
9390
  exports.hydrateSchema = hydrateSchema;
8893
9391
  exports.integrationsCapability = integrationsCapability;
8894
9392
  exports.intercomCapability = intercomCapability;
9393
+ exports.isDeviceConfigCap = isDeviceConfigCap;
8895
9394
  exports.localNetworkCapability = localNetworkCapability;
8896
9395
  exports.logDestinationCapability = logDestinationCapability;
8897
9396
  exports.mapAudioLabelToMacro = mapAudioLabelToMacro;
@@ -8901,12 +9400,14 @@ exports.metricsProviderCapability = metricsProviderCapability;
8901
9400
  exports.motionCapability = motionCapability;
8902
9401
  exports.motionDetectionCapability = motionDetectionCapability;
8903
9402
  exports.motionTriggerCapability = motionTriggerCapability;
9403
+ exports.motionZonesCapability = motionZonesCapability;
8904
9404
  exports.mqttBrokerCapability = mqttBrokerCapability;
8905
9405
  exports.nativeObjectDetectionCapability = nativeObjectDetectionCapability;
8906
9406
  exports.networkAccessCapability = networkAccessCapability;
8907
9407
  exports.networkQualityCapability = networkQualityCapability;
8908
9408
  exports.nodesCapability = nodesCapability;
8909
9409
  exports.notificationOutputCapability = notificationOutputCapability;
9410
+ exports.oauthIntegrationCapability = oauthIntegrationCapability;
8910
9411
  exports.osdCapability = osdCapability;
8911
9412
  exports.pipelineAnalyticsCapability = pipelineAnalyticsCapability;
8912
9413
  exports.pipelineExecutorCapability = pipelineExecutorCapability;
@@ -8928,6 +9429,7 @@ exports.ssoBridgeCapability = ssoBridgeCapability;
8928
9429
  exports.storageCapability = storageCapability;
8929
9430
  exports.storageProviderCapability = storageProviderCapability;
8930
9431
  exports.streamBrokerCapability = streamBrokerCapability;
9432
+ exports.streamParamsCapability = streamParamsCapability;
8931
9433
  exports.streamingEngineCapability = streamingEngineCapability;
8932
9434
  exports.switchCapability = switchCapability;
8933
9435
  exports.systemCapability = systemCapability;
@@ -8941,4 +9443,4 @@ exports.webrtcSessionCapability = webrtcSessionCapability;
8941
9443
  exports.zoneAnalyticsCapability = zoneAnalyticsCapability;
8942
9444
  exports.zoneRulesCapability = zoneRulesCapability;
8943
9445
  exports.zonesCapability = zonesCapability;
8944
- //# sourceMappingURL=index-BkSgJYP7.js.map
9446
+ //# sourceMappingURL=index-BblD92Si.js.map