@camstack/types 0.1.30 → 0.1.32

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 (98) hide show
  1. package/dist/addon/build-addon-route-provider.d.ts +55 -0
  2. package/dist/addon/build-addon-route-provider.d.ts.map +1 -0
  3. package/dist/capabilities/addon-routes.cap.d.ts +64 -0
  4. package/dist/capabilities/addon-routes.cap.d.ts.map +1 -1
  5. package/dist/capabilities/admin-ui.cap.d.ts +6 -2
  6. package/dist/capabilities/admin-ui.cap.d.ts.map +1 -1
  7. package/dist/capabilities/advanced-notifier.cap.d.ts +202 -29
  8. package/dist/capabilities/advanced-notifier.cap.d.ts.map +1 -1
  9. package/dist/capabilities/audio-codec.cap.d.ts +2 -2
  10. package/dist/capabilities/auth-provider.cap.d.ts +4 -4
  11. package/dist/capabilities/auth-provider.cap.d.ts.map +1 -1
  12. package/dist/capabilities/authentication.cap.d.ts +4 -0
  13. package/dist/capabilities/authentication.cap.d.ts.map +1 -1
  14. package/dist/capabilities/capability-definition.d.ts +22 -0
  15. package/dist/capabilities/capability-definition.d.ts.map +1 -1
  16. package/dist/capabilities/device-export.cap.d.ts +77 -0
  17. package/dist/capabilities/device-export.cap.d.ts.map +1 -0
  18. package/dist/capabilities/embedding-encoder.cap.d.ts +14 -7
  19. package/dist/capabilities/embedding-encoder.cap.d.ts.map +1 -1
  20. package/dist/capabilities/index.d.ts +24 -5
  21. package/dist/capabilities/index.d.ts.map +1 -1
  22. package/dist/capabilities/intercom.cap.d.ts +34 -0
  23. package/dist/capabilities/intercom.cap.d.ts.map +1 -1
  24. package/dist/capabilities/mesh-network.cap.d.ts +32 -16
  25. package/dist/capabilities/mesh-network.cap.d.ts.map +1 -1
  26. package/dist/capabilities/mqtt-broker.cap.d.ts +153 -0
  27. package/dist/capabilities/mqtt-broker.cap.d.ts.map +1 -0
  28. package/dist/capabilities/network-access.cap.d.ts +41 -1
  29. package/dist/capabilities/network-access.cap.d.ts.map +1 -1
  30. package/dist/capabilities/platform-probe.cap.d.ts +234 -15
  31. package/dist/capabilities/platform-probe.cap.d.ts.map +1 -1
  32. package/dist/capabilities/settings-store.cap.d.ts +8 -2
  33. package/dist/capabilities/settings-store.cap.d.ts.map +1 -1
  34. package/dist/capabilities/smtp-provider.cap.d.ts +63 -0
  35. package/dist/capabilities/smtp-provider.cap.d.ts.map +1 -0
  36. package/dist/capabilities/sso-bridge.cap.d.ts +61 -0
  37. package/dist/capabilities/sso-bridge.cap.d.ts.map +1 -0
  38. package/dist/capabilities/stream-broker.cap.d.ts +90 -85
  39. package/dist/capabilities/stream-broker.cap.d.ts.map +1 -1
  40. package/dist/capabilities/user-management.cap.d.ts +513 -184
  41. package/dist/capabilities/user-management.cap.d.ts.map +1 -1
  42. package/dist/capabilities/user-passkeys.cap.d.ts +92 -0
  43. package/dist/capabilities/user-passkeys.cap.d.ts.map +1 -0
  44. package/dist/capabilities/webrtc-session.cap.d.ts +34 -0
  45. package/dist/capabilities/webrtc-session.cap.d.ts.map +1 -1
  46. package/dist/generated/addon-api.d.ts +11940 -9272
  47. package/dist/generated/addon-api.d.ts.map +1 -1
  48. package/dist/generated/cap-status-types.d.ts +5 -1
  49. package/dist/generated/cap-status-types.d.ts.map +1 -1
  50. package/dist/generated/capability-router-map.d.ts +19 -4
  51. package/dist/generated/capability-router-map.d.ts.map +1 -1
  52. package/dist/generated/device-proxy.d.ts +2 -0
  53. package/dist/generated/device-proxy.d.ts.map +1 -1
  54. package/dist/generated/method-access-map.d.ts +6 -1
  55. package/dist/generated/method-access-map.d.ts.map +1 -1
  56. package/dist/generated/provider-kind-map.d.ts +22 -0
  57. package/dist/generated/provider-kind-map.d.ts.map +1 -0
  58. package/dist/generated/scope-presets.d.ts +17 -0
  59. package/dist/generated/scope-presets.d.ts.map +1 -0
  60. package/dist/generated/system-proxy.d.ts +8 -4
  61. package/dist/generated/system-proxy.d.ts.map +1 -1
  62. package/dist/{index-DVKPWMwv.mjs → index-BBVUwOlZ.mjs} +1040 -500
  63. package/dist/index-BBVUwOlZ.mjs.map +1 -0
  64. package/dist/{index-s8uJNgNs.js → index-BUBhoPUu.js} +741 -201
  65. package/dist/index-BUBhoPUu.js.map +1 -0
  66. package/dist/index.d.ts +9 -3
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +852 -500
  69. package/dist/index.js.map +1 -1
  70. package/dist/index.mjs +1109 -757
  71. package/dist/index.mjs.map +1 -1
  72. package/dist/interfaces/addon-routes.d.ts +8 -2
  73. package/dist/interfaces/addon-routes.d.ts.map +1 -1
  74. package/dist/interfaces/addon.d.ts +14 -3
  75. package/dist/interfaces/addon.d.ts.map +1 -1
  76. package/dist/interfaces/advanced-notifier.d.ts +8 -7
  77. package/dist/interfaces/advanced-notifier.d.ts.map +1 -1
  78. package/dist/interfaces/api-responses.d.ts +2 -3
  79. package/dist/interfaces/api-responses.d.ts.map +1 -1
  80. package/dist/interfaces/auth.d.ts +14 -11
  81. package/dist/interfaces/auth.d.ts.map +1 -1
  82. package/dist/interfaces/capability.d.ts +6 -6
  83. package/dist/interfaces/capability.d.ts.map +1 -1
  84. package/dist/interfaces/embedding-encoder.d.ts +16 -7
  85. package/dist/interfaces/embedding-encoder.d.ts.map +1 -1
  86. package/dist/interfaces/rtp-egress.d.ts +45 -0
  87. package/dist/interfaces/rtp-egress.d.ts.map +1 -0
  88. package/dist/interfaces/storage.d.ts +15 -2
  89. package/dist/interfaces/storage.d.ts.map +1 -1
  90. package/dist/node.js +1 -1
  91. package/dist/node.mjs +1 -1
  92. package/dist/schemas/auth-records.d.ts +105 -50
  93. package/dist/schemas/auth-records.d.ts.map +1 -1
  94. package/package.json +1 -1
  95. package/dist/index-DVKPWMwv.mjs.map +0 -1
  96. package/dist/index-s8uJNgNs.js.map +0 -1
  97. package/dist/interfaces/auth-provider.d.ts +0 -39
  98. package/dist/interfaces/auth-provider.d.ts.map +0 -1
@@ -1070,9 +1070,9 @@ const DecodedFrameSchema = zod.z.object({
1070
1070
  format: zod.z.enum(["jpeg", "rgb", "bgr", "yuv420", "gray"]),
1071
1071
  timestamp: zod.z.number()
1072
1072
  });
1073
- const BrokerStatusSchema = zod.z.enum(["idle", "connecting", "streaming", "error", "stopped"]);
1073
+ const BrokerStatusSchema$1 = zod.z.enum(["idle", "connecting", "streaming", "error", "stopped"]);
1074
1074
  const BrokerStatsSchema = zod.z.object({
1075
- status: BrokerStatusSchema,
1075
+ status: BrokerStatusSchema$1,
1076
1076
  inputFps: zod.z.number(),
1077
1077
  decodeFps: zod.z.number(),
1078
1078
  encodedSubscribers: zod.z.number(),
@@ -1178,22 +1178,34 @@ const PlaceholderReasonSchema = zod.z.enum([
1178
1178
  "disabled",
1179
1179
  "waking"
1180
1180
  ]);
1181
+ const VideoCodecTargetSchema = zod.z.enum(["H264", "H265", "auto"]);
1182
+ const AudioCodecTargetSchema = zod.z.enum(["AAC", "Opus", "PCMU", "none"]);
1183
+ const MaxResolutionSchema = zod.z.object({
1184
+ width: zod.z.number().int().positive(),
1185
+ height: zod.z.number().int().positive()
1186
+ });
1187
+ const GetStreamWithCodecInputSchema = zod.z.object({
1188
+ deviceId: zod.z.number().int().nonnegative(),
1189
+ videoCodec: VideoCodecTargetSchema,
1190
+ audioCodec: AudioCodecTargetSchema.optional(),
1191
+ maxResolution: MaxResolutionSchema.optional(),
1192
+ tag: zod.z.string().optional()
1193
+ });
1194
+ const RtpSourceSchema = zod.z.object({
1195
+ url: zod.z.string(),
1196
+ videoCodec: zod.z.enum(["H264", "H265"]),
1197
+ audioCodec: zod.z.string(),
1198
+ resolution: MaxResolutionSchema,
1199
+ transcoded: zod.z.boolean(),
1200
+ encoder: zod.z.string(),
1201
+ pipelineKey: zod.z.string()
1202
+ });
1181
1203
  const streamBrokerCapability = {
1182
1204
  name: "stream-broker",
1183
1205
  scope: "system",
1184
1206
  mode: "singleton",
1185
1207
  exposesDeviceSettings: true,
1186
1208
  methods: {
1187
- // ── Cam stream lifecycle (publish / retract) ─────────────────────
1188
- /**
1189
- * Register a physical camera stream as an input option. Idempotent
1190
- * for (deviceId, camStreamId) — re-publishing updates metadata
1191
- * without disturbing profile assignments. The provider calls this
1192
- * in `onInitialize` for each stream it offers. Third-party addons
1193
- * may also publish (e.g. an RTMP discovery layer) — camstack has
1194
- * no notion of "owner" beyond the (deviceId, camStreamId) key.
1195
- */
1196
- /* — see STREAM_BROKER_CAP_EVENTS below for cap-event category strings — */
1197
1209
  publishCameraStream: method(
1198
1210
  zod.z.object({
1199
1211
  deviceId: zod.z.number().int().nonnegative(),
@@ -1204,28 +1216,8 @@ const streamBrokerCapability = {
1204
1216
  resolution: CamStreamResolutionSchema.optional(),
1205
1217
  fps: zod.z.number().positive().optional(),
1206
1218
  label: zod.z.string().optional(),
1207
- /**
1208
- * Device-level features that the broker / manager / snapshot
1209
- * orchestrator consult to derive per-stream policy (e.g.
1210
- * `BatteryOperated` → relax stall watchdog, default pre-buffer
1211
- * to off, raise snapshot rate-limit). Single source of truth —
1212
- * publishers no longer set per-stream flags like `allowStall`.
1213
- */
1214
1219
  deviceFeatures: zod.z.array(zod.z.string()).optional(),
1215
- /**
1216
- * Whether this stream participates in the broker's automatic
1217
- * profile assignment. Defaults `true`. Publishers set `false` for
1218
- * streams that should be SELECTABLE but not auto-picked — e.g.
1219
- * Reolink publishes native Baichuan as eligible and RTSP/RTMP
1220
- * mirrors as ineligible (still assignable manually via
1221
- * `assignProfile`).
1222
- */
1223
1220
  autoEligible: zod.z.boolean().optional(),
1224
- /**
1225
- * Transport-specific opaque metadata stashed alongside the stream
1226
- * record. `pull-rfc4571` publishers put the SDP here so the broker
1227
- * reader can route packets without an in-band DESCRIBE phase.
1228
- */
1229
1221
  metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
1230
1222
  }),
1231
1223
  zod.z.object({ success: zod.z.literal(true) }),
@@ -1239,12 +1231,6 @@ const streamBrokerCapability = {
1239
1231
  zod.z.object({ success: zod.z.literal(true) }),
1240
1232
  { kind: "mutation", auth: "admin" }
1241
1233
  ),
1242
- // ── Profile assignment ───────────────────────────────────────────
1243
- /**
1244
- * Assign a cam stream to a profile slot. Tears down any existing
1245
- * broker for that slot and rebuilds against the new source. Persists
1246
- * the choice — survives reboots.
1247
- */
1248
1234
  assignProfile: method(
1249
1235
  zod.z.object({
1250
1236
  deviceId: zod.z.number().int().nonnegative(),
@@ -1262,12 +1248,6 @@ const streamBrokerCapability = {
1262
1248
  zod.z.object({ success: zod.z.literal(true) }),
1263
1249
  { kind: "mutation", auth: "admin" }
1264
1250
  ),
1265
- // ── System-wide views ────────────────────────────────────────────
1266
- /**
1267
- * Full dump of every published cam stream across every device.
1268
- * For dashboards and cross-device tooling; per-device reads go
1269
- * through the `camera-streams` cap on the device proxy.
1270
- */
1271
1251
  listAllCameraStreams: method(
1272
1252
  zod.z.void(),
1273
1253
  zod.z.array(CameraStreamSchema).readonly()
@@ -1276,7 +1256,6 @@ const streamBrokerCapability = {
1276
1256
  zod.z.void(),
1277
1257
  zod.z.array(ProfileSlotSchema).readonly()
1278
1258
  ),
1279
- // ── Broker runtime (stats + client inventory) ────────────────────
1280
1259
  getBrokerStats: method(
1281
1260
  zod.z.object({ brokerId: zod.z.string() }),
1282
1261
  BrokerStatsSchema
@@ -1294,7 +1273,6 @@ const streamBrokerCapability = {
1294
1273
  zod.z.object({ killed: zod.z.boolean() }),
1295
1274
  { kind: "mutation", auth: "admin" }
1296
1275
  ),
1297
- /** Rebuild the broker for a profile slot in place (re-dial source). */
1298
1276
  restartProfile: method(
1299
1277
  zod.z.object({
1300
1278
  deviceId: zod.z.number().int().nonnegative(),
@@ -1303,24 +1281,28 @@ const streamBrokerCapability = {
1303
1281
  zod.z.object({ success: zod.z.boolean() }),
1304
1282
  { kind: "mutation", auth: "admin" }
1305
1283
  ),
1306
- // ── Stream URLs ──────────────────────────────────────────────────
1307
1284
  getStreamUrl: method(
1308
1285
  zod.z.object({ streamId: zod.z.string(), format: StreamFormatSchema }),
1309
1286
  zod.z.object({ url: zod.z.string() })
1310
1287
  ),
1311
- // ── In-process broker access ─────────────────────────────────────
1312
1288
  /**
1313
- * Return the live IStreamBroker instance for a given brokerId. Same
1314
- * LOCAL-ONLY contract as before: callers (pipeline-runner) must be
1315
- * co-located. BrokerId is `${deviceId}/${camStreamId}` — profile
1316
- * lookup goes through `assignments` if a caller starts from a
1317
- * profile.
1289
+ * Shared codec-targeted stream API see Task #184.
1290
+ * Resolution order: source-select HW transcode libx264/libx265.
1318
1291
  */
1292
+ getStreamWithCodec: method(
1293
+ GetStreamWithCodecInputSchema,
1294
+ RtpSourceSchema,
1295
+ { kind: "mutation", auth: "admin" }
1296
+ ),
1297
+ releaseStreamWithCodec: method(
1298
+ zod.z.object({ pipelineKey: zod.z.string() }),
1299
+ zod.z.object({ released: zod.z.boolean(), refcount: zod.z.number().int().nonnegative() }),
1300
+ { kind: "mutation", auth: "admin" }
1301
+ ),
1319
1302
  getBroker: method(
1320
1303
  zod.z.object({ brokerId: zod.z.string() }),
1321
1304
  zod.z.custom()
1322
1305
  ),
1323
- // ── Pre-buffer ───────────────────────────────────────────────────
1324
1306
  setPreBufferDuration: method(
1325
1307
  zod.z.object({ brokerId: zod.z.string(), seconds: zod.z.number().min(0).max(30) }),
1326
1308
  zod.z.void(),
@@ -1330,7 +1312,6 @@ const streamBrokerCapability = {
1330
1312
  zod.z.object({ brokerId: zod.z.string() }),
1331
1313
  zod.z.object({ configuredSec: zod.z.number(), bufferedMs: zod.z.number(), packetCount: zod.z.number() })
1332
1314
  ),
1333
- // ── RTSP restream ────────────────────────────────────────────────
1334
1315
  getRtspPort: method(zod.z.void(), zod.z.number()),
1335
1316
  getAllRtspEntries: method(
1336
1317
  zod.z.object({ hostname: zod.z.string().optional() }),
@@ -1356,37 +1337,15 @@ const streamBrokerCapability = {
1356
1337
  )
1357
1338
  },
1358
1339
  events: {
1359
- /**
1360
- * Emitted when a profile starts consuming a push-kind cam stream.
1361
- * Push-kind providers (e.g. Reolink Baichuan native streams)
1362
- * subscribe and begin emitting packets only on demand — battery
1363
- * cams stay asleep until someone actually watches.
1364
- */
1365
1340
  onCamStreamDemand: event(zod.z.object({
1366
1341
  deviceId: zod.z.number().int().nonnegative(),
1367
1342
  camStreamId: zod.z.string(),
1368
1343
  profile: CamProfileSchema
1369
1344
  })),
1370
- /**
1371
- * Emitted when the last profile consuming a push-kind cam stream
1372
- * releases it. Providers tear down upstream connections on this
1373
- * signal.
1374
- */
1375
1345
  onCamStreamIdle: event(zod.z.object({
1376
1346
  deviceId: zod.z.number().int().nonnegative(),
1377
1347
  camStreamId: zod.z.string()
1378
1348
  })),
1379
- /**
1380
- * Emitted by a broker that failed to dial a managed-loopback
1381
- * source (today: `pull-rfc4571`). The publisher's transport (e.g.
1382
- * the Reolink lib's RFC 4571 TCP server) idle-tears-down after
1383
- * ~15s with no clients and may rebind to a different port on
1384
- * recreate, leaving the URL the broker has cached stale. The owning
1385
- * camera provider re-runs its publish pipeline on receipt — that
1386
- * call self-heals the loopback server (`ensureRfc4571Server`) and
1387
- * `publishCameraStream` propagates the fresh URL back into the
1388
- * broker's source resolver.
1389
- */
1390
1349
  onRequestStreamSourceRefresh: event(zod.z.object({
1391
1350
  deviceId: zod.z.number().int().nonnegative(),
1392
1351
  camStreamId: zod.z.string(),
@@ -3545,7 +3504,7 @@ const SettingsRecordSchema = zod.z.object({
3545
3504
  });
3546
3505
  const CollectionColumnSchema = zod.z.object({
3547
3506
  name: zod.z.string(),
3548
- type: zod.z.enum(["TEXT", "INTEGER", "REAL", "JSON"]),
3507
+ type: zod.z.enum(["TEXT", "INTEGER", "REAL", "JSON", "BOOLEAN"]),
3549
3508
  primaryKey: zod.z.boolean().optional(),
3550
3509
  notNull: zod.z.boolean().optional(),
3551
3510
  unique: zod.z.boolean().optional()
@@ -3660,14 +3619,293 @@ const logDestinationCapability = {
3660
3619
  )
3661
3620
  }
3662
3621
  };
3622
+ const StaticDirOutputSchema = zod.z.object({ staticDir: zod.z.string() });
3623
+ const VersionOutputSchema = zod.z.object({ version: zod.z.string() });
3663
3624
  const adminUiCapability = {
3664
3625
  name: "admin-ui",
3665
3626
  scope: "system",
3666
3627
  mode: "singleton",
3667
3628
  internal: true,
3668
3629
  methods: {
3669
- getStaticDir: method(zod.z.void(), zod.z.string()),
3670
- getVersion: method(zod.z.void(), zod.z.string())
3630
+ getStaticDir: method(zod.z.void(), StaticDirOutputSchema),
3631
+ getVersion: method(zod.z.void(), VersionOutputSchema)
3632
+ }
3633
+ };
3634
+ const SsoBridgeClaimsSchema = zod.z.object({
3635
+ userId: zod.z.string(),
3636
+ username: zod.z.string(),
3637
+ isAdmin: zod.z.boolean(),
3638
+ provider: zod.z.string(),
3639
+ email: zod.z.string().optional(),
3640
+ displayName: zod.z.string().optional(),
3641
+ /**
3642
+ * Public HTTPS URL of the hub that issued this token. Used by
3643
+ * cloud-mode OAuth proxies (Alexa Smart Home Lambda, future Google
3644
+ * Home Lambda) to route a request back to the originating hub
3645
+ * without holding routing state of their own. The Lambda decodes the
3646
+ * JWT WITHOUT verifying the signature — the hub re-verifies on every
3647
+ * inbound call so trust still rests with the signing hub.
3648
+ */
3649
+ hubUrl: zod.z.string().optional()
3650
+ });
3651
+ const ssoBridgeCapability = {
3652
+ name: "sso-bridge",
3653
+ scope: "system",
3654
+ mode: "singleton",
3655
+ internal: true,
3656
+ methods: {
3657
+ signBridgeToken: method(
3658
+ zod.z.object({
3659
+ claims: SsoBridgeClaimsSchema,
3660
+ ttlSec: zod.z.number().int().positive().optional()
3661
+ }),
3662
+ zod.z.object({ token: zod.z.string() })
3663
+ ),
3664
+ verifyBridgeToken: method(
3665
+ zod.z.object({ token: zod.z.string() }),
3666
+ SsoBridgeClaimsSchema.nullable()
3667
+ )
3668
+ }
3669
+ };
3670
+ const PasskeySummarySchema = zod.z.object({
3671
+ credentialId: zod.z.string(),
3672
+ label: zod.z.string(),
3673
+ createdAt: zod.z.number(),
3674
+ lastUsedAt: zod.z.number().nullable(),
3675
+ transports: zod.z.array(zod.z.string()).default([])
3676
+ });
3677
+ const userPasskeysCapability = {
3678
+ name: "user-passkeys",
3679
+ scope: "system",
3680
+ mode: "collection",
3681
+ internal: true,
3682
+ methods: {
3683
+ beginRegistration: method(
3684
+ zod.z.object({ userId: zod.z.string(), username: zod.z.string() }),
3685
+ // PublicKeyCredentialCreationOptionsJSON — opaque JSON shape from
3686
+ // @simplewebauthn. The browser passes it straight to credentials.create().
3687
+ zod.z.object({ optionsJSON: zod.z.record(zod.z.string(), zod.z.unknown()) }),
3688
+ { kind: "mutation", auth: "admin", access: "create" }
3689
+ ),
3690
+ finishRegistration: method(
3691
+ zod.z.object({
3692
+ userId: zod.z.string(),
3693
+ /** RegistrationResponseJSON from the browser. */
3694
+ response: zod.z.record(zod.z.string(), zod.z.unknown()),
3695
+ /** Operator-visible label (e.g. "MacBook Touch ID"). */
3696
+ label: zod.z.string()
3697
+ }),
3698
+ zod.z.object({ success: zod.z.literal(true), credentialId: zod.z.string() }),
3699
+ { kind: "mutation", auth: "admin", access: "create" }
3700
+ ),
3701
+ beginAuthentication: method(
3702
+ // userId optional — when absent, the addon emits a "passkey discovery"
3703
+ // (`allowCredentials: []`) so the browser shows every available
3704
+ // credential. When present, only the user's credentials are allowed.
3705
+ zod.z.object({ userId: zod.z.string().optional() }),
3706
+ zod.z.object({ optionsJSON: zod.z.record(zod.z.string(), zod.z.unknown()) }),
3707
+ { kind: "mutation", access: "view" }
3708
+ ),
3709
+ finishAuthentication: method(
3710
+ zod.z.object({
3711
+ /** Required — the user the assertion belongs to (verified). */
3712
+ userId: zod.z.string(),
3713
+ /** AuthenticationResponseJSON from the browser. */
3714
+ response: zod.z.record(zod.z.string(), zod.z.unknown())
3715
+ }),
3716
+ zod.z.object({ verified: zod.z.boolean() }),
3717
+ { kind: "mutation", access: "view" }
3718
+ ),
3719
+ listPasskeys: method(
3720
+ zod.z.object({ userId: zod.z.string() }),
3721
+ zod.z.array(PasskeySummarySchema),
3722
+ { auth: "admin" }
3723
+ ),
3724
+ removePasskey: method(
3725
+ zod.z.object({ userId: zod.z.string(), credentialId: zod.z.string() }),
3726
+ zod.z.object({ success: zod.z.literal(true) }),
3727
+ { kind: "mutation", auth: "admin", access: "delete" }
3728
+ )
3729
+ }
3730
+ };
3731
+ const EmailAddressSchema = zod.z.email();
3732
+ const SendEmailInputSchema = zod.z.object({
3733
+ to: zod.z.union([EmailAddressSchema, zod.z.array(EmailAddressSchema).min(1)]),
3734
+ cc: zod.z.array(EmailAddressSchema).optional(),
3735
+ bcc: zod.z.array(EmailAddressSchema).optional(),
3736
+ /** RFC 5322 `From` field. Most relays will reject if the domain
3737
+ * isn't authorised — the addon is responsible for substituting a
3738
+ * sane default when omitted. */
3739
+ from: zod.z.string().optional(),
3740
+ /** Optional `Reply-To` override. */
3741
+ replyTo: zod.z.string().optional(),
3742
+ subject: zod.z.string(),
3743
+ /** Plain-text body. Required even when `html` is present (fallback
3744
+ * for clients that strip HTML — including most spam filters). */
3745
+ text: zod.z.string(),
3746
+ /** Optional HTML body. Renders alongside `text` as multi-part. */
3747
+ html: zod.z.string().optional()
3748
+ });
3749
+ const SendEmailResultSchema = zod.z.object({
3750
+ messageId: zod.z.string(),
3751
+ accepted: zod.z.array(EmailAddressSchema).default([]),
3752
+ rejected: zod.z.array(EmailAddressSchema).default([])
3753
+ });
3754
+ const SmtpStatusSchema = zod.z.object({
3755
+ /** True iff the addon has successfully verified the relay. */
3756
+ ready: zod.z.boolean(),
3757
+ /** Operator-visible host string (no credentials). */
3758
+ host: zod.z.string(),
3759
+ /** Last error message reported by the relay, when not ready. */
3760
+ error: zod.z.string().optional(),
3761
+ /** Last successful verify timestamp (unix ms). */
3762
+ lastVerifiedAt: zod.z.number().optional()
3763
+ });
3764
+ const smtpProviderCapability = {
3765
+ name: "smtp-provider",
3766
+ scope: "system",
3767
+ mode: "collection",
3768
+ internal: true,
3769
+ providerKind: "email",
3770
+ methods: {
3771
+ sendEmail: method(
3772
+ SendEmailInputSchema,
3773
+ SendEmailResultSchema,
3774
+ { kind: "mutation", auth: "admin", access: "create" }
3775
+ ),
3776
+ /** Round-trip ping against the SMTP relay (EHLO + AUTH if configured).
3777
+ * Used by the operator's "Test connection" button. */
3778
+ verify: method(
3779
+ zod.z.void(),
3780
+ SmtpStatusSchema,
3781
+ { kind: "mutation", auth: "admin", access: "view" }
3782
+ ),
3783
+ getStatus: method(
3784
+ zod.z.void(),
3785
+ SmtpStatusSchema,
3786
+ { auth: "admin" }
3787
+ )
3788
+ }
3789
+ };
3790
+ const BrokerKindSchema = zod.z.enum(["external", "embedded"]);
3791
+ const BrokerStatusSchema = zod.z.enum([
3792
+ "connected",
3793
+ "disconnected",
3794
+ "auth-failed",
3795
+ "unreachable",
3796
+ "tls-error"
3797
+ ]);
3798
+ const BrokerInfoSchema = zod.z.object({
3799
+ id: zod.z.string(),
3800
+ name: zod.z.string(),
3801
+ url: zod.z.string(),
3802
+ kind: BrokerKindSchema,
3803
+ status: BrokerStatusSchema,
3804
+ latencyMs: zod.z.number().nullable(),
3805
+ error: zod.z.string().optional(),
3806
+ /** Embedded brokers only: number of MQTT clients currently connected. */
3807
+ connectedClients: zod.z.number().int().nonnegative().optional(),
3808
+ /** Epoch ms of the last live probe (external) or aedes snapshot (embedded). */
3809
+ lastCheckedAt: zod.z.number().optional()
3810
+ });
3811
+ const BrokerConnectionDetailsSchema = zod.z.object({
3812
+ url: zod.z.string(),
3813
+ username: zod.z.string().optional(),
3814
+ password: zod.z.string().optional(),
3815
+ /**
3816
+ * Suggested prefix for `clientId`. Each consumer should suffix this
3817
+ * with its own discriminator (addon id, instance id) so reconnects
3818
+ * don't kick each other off (MQTT spec: clientId must be unique per
3819
+ * broker).
3820
+ */
3821
+ clientIdPrefix: zod.z.string().optional()
3822
+ });
3823
+ const AddBrokerInputSchema = zod.z.object({
3824
+ name: zod.z.string().min(1),
3825
+ url: zod.z.string().regex(/^(mqtt|mqtts|ws|wss):\/\//, "URL must start with mqtt(s):// or ws(s)://"),
3826
+ username: zod.z.string().optional(),
3827
+ password: zod.z.string().optional(),
3828
+ clientIdPrefix: zod.z.string().optional()
3829
+ });
3830
+ const AddBrokerResultSchema = zod.z.object({
3831
+ id: zod.z.string()
3832
+ });
3833
+ const IdInputSchema = zod.z.object({ id: zod.z.string() });
3834
+ const TestResultSchema = zod.z.discriminatedUnion("ok", [
3835
+ zod.z.object({ ok: zod.z.literal(true), latencyMs: zod.z.number() }),
3836
+ zod.z.object({ ok: zod.z.literal(false), error: zod.z.string() })
3837
+ ]);
3838
+ const StartEmbeddedInputSchema = zod.z.object({
3839
+ port: zod.z.number().int().min(1).max(65535).default(1883),
3840
+ /** Allow anonymous connect (no username/password). Default: false. */
3841
+ allowAnonymous: zod.z.boolean().default(false),
3842
+ /** Optional shared username/password for clients. */
3843
+ username: zod.z.string().optional(),
3844
+ password: zod.z.string().optional()
3845
+ });
3846
+ const StartEmbeddedResultSchema = zod.z.object({
3847
+ id: zod.z.string(),
3848
+ url: zod.z.string()
3849
+ });
3850
+ const StatusSchema = zod.z.object({
3851
+ brokerCount: zod.z.number(),
3852
+ embeddedRunning: zod.z.boolean()
3853
+ });
3854
+ const mqttBrokerCapability = {
3855
+ name: "mqtt-broker",
3856
+ scope: "system",
3857
+ mode: "collection",
3858
+ providerKind: "broker",
3859
+ status: { schema: StatusSchema, kind: "poll" },
3860
+ methods: {
3861
+ listBrokers: method(zod.z.void(), zod.z.array(BrokerInfoSchema)),
3862
+ getBrokerConfig: method(IdInputSchema, BrokerConnectionDetailsSchema),
3863
+ addBroker: method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }),
3864
+ removeBroker: method(IdInputSchema, zod.z.void(), { kind: "mutation" }),
3865
+ testConnection: method(IdInputSchema, TestResultSchema, { kind: "mutation" }),
3866
+ startEmbeddedBroker: method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }),
3867
+ stopEmbeddedBroker: method(IdInputSchema, zod.z.void(), { kind: "mutation" }),
3868
+ getStatus: method(zod.z.void(), StatusSchema)
3869
+ }
3870
+ };
3871
+ const LinkStateSchema = zod.z.enum(["unlinked", "linked", "error"]);
3872
+ const DeviceExportStatusSchema = zod.z.object({
3873
+ linkState: LinkStateSchema,
3874
+ exposedDeviceCount: zod.z.number(),
3875
+ error: zod.z.string().optional()
3876
+ });
3877
+ const DeviceKindSchema = zod.z.string();
3878
+ const ExposedDeviceSchema = zod.z.object({
3879
+ deviceId: zod.z.string(),
3880
+ exposedAs: zod.z.string().optional(),
3881
+ capabilities: zod.z.array(zod.z.string()).optional()
3882
+ });
3883
+ const ExposeInputSchema = zod.z.object({
3884
+ deviceId: zod.z.string(),
3885
+ capabilities: zod.z.array(zod.z.string()).optional()
3886
+ });
3887
+ const UnexposeInputSchema = zod.z.object({ deviceId: zod.z.string() });
3888
+ const deviceExportCapability = {
3889
+ name: "device-export",
3890
+ scope: "system",
3891
+ mode: "collection",
3892
+ providerKind: "device-export",
3893
+ status: { schema: DeviceExportStatusSchema, kind: "poll" },
3894
+ /**
3895
+ * Each export provider contributes its own per-device "Export" panel
3896
+ * (enabled toggle, preferred stream, addon-specific knobs like the
3897
+ * HA-MQTT discovery prefix). The three framework methods
3898
+ * `getDeviceSettingsContribution` / `getDeviceLiveContribution` /
3899
+ * `applyDeviceSettingsPatch` are auto-injected on the provider
3900
+ * interface and routed to the device-details aggregator.
3901
+ */
3902
+ exposesDeviceSettings: true,
3903
+ methods: {
3904
+ getStatus: method(zod.z.void(), DeviceExportStatusSchema),
3905
+ listSupportedDeviceKinds: method(zod.z.void(), zod.z.array(DeviceKindSchema)),
3906
+ listExposedDevices: method(zod.z.void(), zod.z.array(ExposedDeviceSchema)),
3907
+ exposeDevice: method(ExposeInputSchema, zod.z.void(), { kind: "mutation" }),
3908
+ unexposeDevice: method(UnexposeInputSchema, zod.z.void(), { kind: "mutation" })
3671
3909
  }
3672
3910
  };
3673
3911
  const AddonPageDeclarationSchema$1 = zod.z.object({
@@ -3783,7 +4021,35 @@ const addonWidgetsCapability = {
3783
4021
  };
3784
4022
  const AddonHttpRouteSchema = zod.z.object({
3785
4023
  method: zod.z.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]),
3786
- path: zod.z.string()
4024
+ path: zod.z.string(),
4025
+ access: zod.z.enum(["public", "authenticated", "admin"]).optional(),
4026
+ description: zod.z.string().optional()
4027
+ });
4028
+ const InvokeRequestSchema = zod.z.object({
4029
+ method: zod.z.string(),
4030
+ path: zod.z.string(),
4031
+ params: zod.z.record(zod.z.string(), zod.z.string()),
4032
+ query: zod.z.record(zod.z.string(), zod.z.string()),
4033
+ body: zod.z.unknown(),
4034
+ headers: zod.z.record(zod.z.string(), zod.z.string()),
4035
+ user: zod.z.object({
4036
+ id: zod.z.string(),
4037
+ username: zod.z.string(),
4038
+ isAdmin: zod.z.boolean()
4039
+ }).optional(),
4040
+ scopedToken: zod.z.unknown().optional()
4041
+ });
4042
+ const InvokeReplyEnvelopeSchema = zod.z.object({
4043
+ status: zod.z.number().int(),
4044
+ headers: zod.z.record(zod.z.string(), zod.z.string()),
4045
+ /** When set, the hub MUST `reply.redirect(redirectUrl)` instead of
4046
+ * sending `body`. Status defaults to 302 when this is set unless
4047
+ * the handler called `reply.code(...)` explicitly. */
4048
+ redirectUrl: zod.z.string().nullable(),
4049
+ /** JSON-serializable body. `undefined` is treated as "no body". */
4050
+ body: zod.z.unknown().optional(),
4051
+ /** Set when the handler called `reply.type(mime)`. */
4052
+ contentType: zod.z.string().optional()
3787
4053
  });
3788
4054
  const addonRoutesCapability = {
3789
4055
  name: "addon-routes",
@@ -3791,7 +4057,16 @@ const addonRoutesCapability = {
3791
4057
  mode: "collection",
3792
4058
  internal: true,
3793
4059
  methods: {
3794
- getRoutes: method(zod.z.void(), zod.z.array(AddonHttpRouteSchema))
4060
+ getRoutes: method(zod.z.void(), zod.z.array(AddonHttpRouteSchema)),
4061
+ /**
4062
+ * Cross-process dispatch entry point. Forked addons implement this
4063
+ * (via `buildAddonRouteProvider`) so the hub's Fastify catch-all
4064
+ * can route through Moleculer when the handler lives in a worker.
4065
+ *
4066
+ * Local addons can implement it for free with the same helper;
4067
+ * the hub bypasses the wire on co-located addons.
4068
+ */
4069
+ invoke: method(InvokeRequestSchema, InvokeReplyEnvelopeSchema, { kind: "mutation" })
3795
4070
  }
3796
4071
  };
3797
4072
  const HWACCEL_OPTIONS = [
@@ -4032,6 +4307,29 @@ const webrtcSessionCapability = {
4032
4307
  zod.z.object({ sessionId: zod.z.string(), sdpOffer: zod.z.string() }),
4033
4308
  { kind: "mutation" }
4034
4309
  ),
4310
+ /**
4311
+ * Accept a CLIENT-generated SDP offer and return the server's
4312
+ * SDP answer. Mirrors the WHEP / Alexa
4313
+ * `Alexa.RTCSessionController.InitiateSessionWithOffer` direction
4314
+ * (client offers, server answers) — the dual of `createSession`,
4315
+ * which has the server offer first.
4316
+ *
4317
+ * `target` defaults to the device's adaptive selection when
4318
+ * omitted; an optional `sessionId` lets the client provide its
4319
+ * own correlation id (Alexa supplies one in the directive
4320
+ * payload). The returned `sessionId` is whatever the server uses
4321
+ * to track the session and accepts via `closeSession`.
4322
+ */
4323
+ handleOffer: method(
4324
+ zod.z.object({
4325
+ deviceId: zod.z.number().int().nonnegative(),
4326
+ target: WebrtcStreamTargetSchema.optional(),
4327
+ sdpOffer: zod.z.string(),
4328
+ sessionId: zod.z.string().optional()
4329
+ }),
4330
+ zod.z.object({ sessionId: zod.z.string(), sdpAnswer: zod.z.string() }),
4331
+ { kind: "mutation" }
4332
+ ),
4035
4333
  handleAnswer: method(
4036
4334
  zod.z.object({
4037
4335
  deviceId: zod.z.number().int().nonnegative(),
@@ -4671,12 +4969,12 @@ const audioCodecCapability = {
4671
4969
  };
4672
4970
  const EmbeddingResultSchema = zod.z.object({
4673
4971
  embedding: zod.z.array(zod.z.number()),
4674
- dimensions: zod.z.number()
4972
+ inferenceMs: zod.z.number()
4675
4973
  });
4676
4974
  const EmbeddingInfoSchema = zod.z.object({
4677
4975
  modelId: zod.z.string(),
4678
- dimensions: zod.z.number(),
4679
- inputSize: zod.z.number()
4976
+ embeddingDim: zod.z.number(),
4977
+ ready: zod.z.boolean()
4680
4978
  });
4681
4979
  const embeddingEncoderCapability = {
4682
4980
  name: "embedding-encoder",
@@ -5327,7 +5625,14 @@ const AuthResultSchema = zod.z.object({
5327
5625
  username: zod.z.string(),
5328
5626
  email: zod.z.string().optional(),
5329
5627
  displayName: zod.z.string().optional(),
5330
- roles: zod.z.array(zod.z.string()).optional()
5628
+ /**
5629
+ * Whether the authenticating user is an admin. The auth-provider
5630
+ * surface returns this so the server's login flow can mint a JWT
5631
+ * with the correct bypass flag. Non-admin users authenticated via
5632
+ * an external IdP still need their scopes assigned by an admin via
5633
+ * `setUserScopes` — the SSO flow doesn't carry permissions.
5634
+ */
5635
+ isAdmin: zod.z.boolean().default(false)
5331
5636
  });
5332
5637
  const authProviderCapability = {
5333
5638
  name: "auth-provider",
@@ -5348,6 +5653,14 @@ const authProviderCapability = {
5348
5653
  const AuthProviderInfoSchema = zod.z.object({
5349
5654
  /** Stable id matching the addon id (used for `getLoginUrl({addonId,…})`). */
5350
5655
  addonId: zod.z.string(),
5656
+ /**
5657
+ * Per-instance id when one addon registers multiple "logical"
5658
+ * providers (e.g. OIDC with Google + Microsoft + custom). The login
5659
+ * URL becomes `/addon/${addonId}/${instanceId}/start` — handler reads
5660
+ * `:instanceId` from the route. Empty/unset means the addon is a
5661
+ * single-instance provider; the URL is `/addon/${addonId}/start`.
5662
+ */
5663
+ instanceId: zod.z.string().optional(),
5351
5664
  /** Display label shown on the login button + admin row. */
5352
5665
  displayName: zod.z.string(),
5353
5666
  /** Optional iconography hint (lucide-react icon name OR emoji). */
@@ -5358,6 +5671,8 @@ const AuthProviderInfoSchema = zod.z.object({
5358
5671
  /** When true, the provider exposes a credential-form login flow
5359
5672
  * (`validateCredentials` accepts username + password). */
5360
5673
  hasCredentialFlow: zod.z.boolean(),
5674
+ /** Provider kind, drives admin-UI hint dispatch (oidc / saml / totp / …). */
5675
+ kind: zod.z.string().optional(),
5361
5676
  /** Operator-facing status string (e.g. "Connected to https://login.acme.com"). */
5362
5677
  status: zod.z.string().optional(),
5363
5678
  /** When false, the provider is registered but disabled by config; the
@@ -5394,16 +5709,36 @@ const NetworkAccessStatusSchema = zod.z.object({
5394
5709
  endpoint: NetworkEndpointSchema.nullable(),
5395
5710
  error: zod.z.string().optional()
5396
5711
  });
5712
+ const NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
5713
+ /**
5714
+ * Stable id within the provider — typically `<mode>-<sourcePort>` so
5715
+ * the orchestrator can dedupe across `listEndpoints` polls.
5716
+ */
5717
+ id: zod.z.string(),
5718
+ /** Operator-facing label (mirrors `MeshEndpoint.label`). */
5719
+ label: zod.z.string(),
5720
+ /** Optional provider-specific mode tag, used for icon/colour in admin UI. */
5721
+ mode: zod.z.string().optional(),
5722
+ /** Originating local port the ingress fronts (informational). */
5723
+ sourcePort: zod.z.number().optional()
5724
+ });
5397
5725
  const networkAccessCapability = {
5398
5726
  name: "network-access",
5399
5727
  scope: "system",
5400
5728
  mode: "collection",
5401
5729
  internal: true,
5730
+ providerKind: "ingress",
5402
5731
  methods: {
5403
5732
  start: method(zod.z.void(), NetworkEndpointSchema, { kind: "mutation" }),
5404
5733
  stop: method(zod.z.void(), zod.z.void(), { kind: "mutation" }),
5405
5734
  getEndpoint: method(zod.z.void(), NetworkEndpointSchema.nullable()),
5406
- getStatus: method(zod.z.void(), NetworkAccessStatusSchema)
5735
+ getStatus: method(zod.z.void(), NetworkAccessStatusSchema),
5736
+ /**
5737
+ * Enumerate every active ingress entry. Default implementation (when
5738
+ * the provider omits this method) is derived from `getEndpoint()` —
5739
+ * see the remote-access orchestrator for the fallback path.
5740
+ */
5741
+ listEndpoints: method(zod.z.void(), zod.z.array(NetworkEndpointEntrySchema).readonly())
5407
5742
  }
5408
5743
  };
5409
5744
  const RemoteAccessEndpointSchema = zod.z.object({
@@ -5607,20 +5942,59 @@ const notificationOutputCapability = {
5607
5942
  )
5608
5943
  }
5609
5944
  };
5945
+ const NotificationRuleConditionsSchema = zod.z.object({
5946
+ deviceIds: zod.z.array(zod.z.number()).readonly().optional(),
5947
+ classNames: zod.z.array(zod.z.string()).readonly().optional(),
5948
+ zoneIds: zod.z.array(zod.z.string()).readonly().optional(),
5949
+ minConfidence: zod.z.number().optional(),
5950
+ source: zod.z.enum(["pipeline", "onboard", "any"]).optional(),
5951
+ schedule: zod.z.object({
5952
+ days: zod.z.array(zod.z.number()).readonly(),
5953
+ startHour: zod.z.number(),
5954
+ endHour: zod.z.number()
5955
+ }).optional(),
5956
+ cooldownSeconds: zod.z.number().optional(),
5957
+ minDwellSeconds: zod.z.number().optional()
5958
+ });
5959
+ const NotificationRuleTemplateSchema = zod.z.object({
5960
+ title: zod.z.string(),
5961
+ body: zod.z.string(),
5962
+ imageMode: zod.z.enum(["crop", "annotated", "full", "none"])
5963
+ });
5610
5964
  const NotificationRuleSchema = zod.z.object({
5611
5965
  id: zod.z.string(),
5612
5966
  name: zod.z.string(),
5613
5967
  enabled: zod.z.boolean(),
5614
- conditions: zod.z.record(zod.z.string(), zod.z.unknown()),
5615
- actions: zod.z.array(zod.z.record(zod.z.string(), zod.z.unknown()))
5968
+ eventTypes: zod.z.array(zod.z.string()).readonly(),
5969
+ conditions: NotificationRuleConditionsSchema,
5970
+ outputs: zod.z.array(zod.z.string()).readonly(),
5971
+ template: NotificationRuleTemplateSchema.optional(),
5972
+ priority: zod.z.enum(["low", "normal", "high", "critical"])
5973
+ });
5974
+ const NotificationTestResultSchema = zod.z.object({
5975
+ ruleId: zod.z.string(),
5976
+ eventId: zod.z.string(),
5977
+ timestamp: zod.z.number(),
5978
+ wouldFire: zod.z.boolean(),
5979
+ reason: zod.z.string().optional()
5616
5980
  });
5617
5981
  const NotificationHistoryEntrySchema = zod.z.object({
5618
5982
  id: zod.z.string(),
5619
5983
  ruleId: zod.z.string(),
5984
+ ruleName: zod.z.string(),
5985
+ eventId: zod.z.string(),
5620
5986
  timestamp: zod.z.number(),
5621
- deviceId: zod.z.number().optional(),
5987
+ outputs: zod.z.array(zod.z.string()).readonly(),
5622
5988
  success: zod.z.boolean(),
5623
- error: zod.z.string().optional()
5989
+ error: zod.z.string().optional(),
5990
+ deviceId: zod.z.number().optional()
5991
+ });
5992
+ const NotificationHistoryFilterSchema = zod.z.object({
5993
+ ruleId: zod.z.string().optional(),
5994
+ deviceId: zod.z.number().optional(),
5995
+ from: zod.z.number().optional(),
5996
+ to: zod.z.number().optional(),
5997
+ limit: zod.z.number().optional()
5624
5998
  });
5625
5999
  const advancedNotifierCapability = {
5626
6000
  name: "advanced-notifier",
@@ -5628,17 +6002,28 @@ const advancedNotifierCapability = {
5628
6002
  mode: "singleton",
5629
6003
  internal: true,
5630
6004
  methods: {
5631
- getRules: method(zod.z.void(), zod.z.array(NotificationRuleSchema).readonly()),
5632
- upsertRule: method(NotificationRuleSchema, zod.z.void(), { kind: "mutation" }),
5633
- deleteRule: method(zod.z.object({ ruleId: zod.z.string() }), zod.z.void(), { kind: "mutation" }),
6005
+ getRules: method(
6006
+ zod.z.void(),
6007
+ zod.z.object({ rules: zod.z.array(NotificationRuleSchema).readonly() })
6008
+ ),
6009
+ upsertRule: method(
6010
+ zod.z.object({ rule: NotificationRuleSchema }),
6011
+ zod.z.object({ success: zod.z.literal(true) }),
6012
+ { kind: "mutation" }
6013
+ ),
6014
+ deleteRule: method(
6015
+ zod.z.object({ ruleId: zod.z.string() }),
6016
+ zod.z.object({ success: zod.z.literal(true) }),
6017
+ { kind: "mutation" }
6018
+ ),
5634
6019
  testRule: method(
5635
6020
  zod.z.object({ ruleId: zod.z.string(), lookbackMinutes: zod.z.number() }),
5636
- zod.z.array(zod.z.record(zod.z.string(), zod.z.unknown())),
6021
+ zod.z.object({ results: zod.z.array(NotificationTestResultSchema).readonly() }),
5637
6022
  { kind: "mutation" }
5638
6023
  ),
5639
6024
  getHistory: method(
5640
- zod.z.object({ ruleId: zod.z.string().optional(), limit: zod.z.number().optional() }),
5641
- zod.z.array(NotificationHistoryEntrySchema)
6025
+ zod.z.object({ filter: NotificationHistoryFilterSchema.optional() }),
6026
+ zod.z.object({ entries: zod.z.array(NotificationHistoryEntrySchema).readonly() })
5642
6027
  )
5643
6028
  }
5644
6029
  };
@@ -6660,6 +7045,47 @@ const intercomCapability = {
6660
7045
  zod.z.object({ deviceId: zod.z.number(), sessionId: zod.z.string() }),
6661
7046
  zod.z.void(),
6662
7047
  { kind: "mutation", auth: "admin" }
7048
+ ),
7049
+ /**
7050
+ * Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
7051
+ * non-WebRTC consumers (HomeKit export, Alexa raw audio, test
7052
+ * harnesses) that already have decoded PCM frames and just need a
7053
+ * direct path onto the camera's talk channel. Mutually exclusive
7054
+ * with `startSession` (an active WebRTC session must be stopped
7055
+ * before a raw-PCM session can be opened on the same device, and
7056
+ * vice versa).
7057
+ */
7058
+ startTalkSession: method(
7059
+ zod.z.object({ deviceId: zod.z.number() }),
7060
+ zod.z.object({ sessionId: zod.z.string() }),
7061
+ { kind: "mutation", auth: "admin" }
7062
+ ),
7063
+ /**
7064
+ * Push a chunk of PCM s16le mono onto the active raw-PCM talk
7065
+ * session. Frames are encoded to the firmware's expected codec
7066
+ * (Reolink: IMA ADPCM @ camera sample rate; Hikvision: G.711 @
7067
+ * 8 kHz) inside the provider — callers must resample upstream to
7068
+ * the rate the camera negotiated (typical: 16 kHz Reolink,
7069
+ * 8 kHz Hikvision). PCM is shipped base64-encoded so the payload
7070
+ * survives JSON serialization across tRPC.
7071
+ */
7072
+ pushTalkPcm: method(
7073
+ zod.z.object({
7074
+ deviceId: zod.z.number(),
7075
+ /** PCM frames as little-endian s16, mono. Base64-encoded so
7076
+ * the payload survives tRPC JSON serialization. */
7077
+ pcmBase64: zod.z.string(),
7078
+ /** Sequence number for ordering / dropping out-of-order frames. */
7079
+ sequenceNumber: zod.z.number().int()
7080
+ }),
7081
+ zod.z.object({ accepted: zod.z.boolean() }),
7082
+ { kind: "mutation", auth: "admin" }
7083
+ ),
7084
+ /** Close the raw-PCM talk session. Idempotent. */
7085
+ endTalkSession: method(
7086
+ zod.z.object({ deviceId: zod.z.number() }),
7087
+ zod.z.void(),
7088
+ { kind: "mutation", auth: "admin" }
6663
7089
  )
6664
7090
  },
6665
7091
  events: {
@@ -6735,6 +7161,33 @@ const HwAccelBackendInputSchema = zod.z.enum([
6735
7161
  const HwAccelResolutionSchema = zod.z.object({
6736
7162
  preferred: zod.z.array(zod.z.string()).readonly()
6737
7163
  });
7164
+ const HardwareEncoderIdSchema = zod.z.enum([
7165
+ "h264_videotoolbox",
7166
+ "hevc_videotoolbox",
7167
+ "h264_vaapi",
7168
+ "hevc_vaapi",
7169
+ "h264_nvenc",
7170
+ "hevc_nvenc",
7171
+ "h264_qsv",
7172
+ "hevc_qsv",
7173
+ "h264_amf",
7174
+ "hevc_amf",
7175
+ "libx264",
7176
+ "libx265"
7177
+ ]);
7178
+ const HardwareEncoderProbeSchema = zod.z.object({
7179
+ encoder: HardwareEncoderIdSchema,
7180
+ codec: zod.z.enum(["H264", "H265"]),
7181
+ family: zod.z.enum(["videotoolbox", "vaapi", "nvenc", "qsv", "amf", "software"]),
7182
+ available: zod.z.boolean(),
7183
+ reason: zod.z.string().optional()
7184
+ });
7185
+ const HardwareEncodersSchema = zod.z.object({
7186
+ encoders: zod.z.array(HardwareEncoderProbeSchema).readonly(),
7187
+ defaultH264: HardwareEncoderIdSchema,
7188
+ defaultH265: HardwareEncoderIdSchema,
7189
+ probedAt: zod.z.number()
7190
+ });
6738
7191
  const HardwarePlatformSchema = zod.z.enum(["darwin", "linux", "win32"]);
6739
7192
  const HardwareArchSchema = zod.z.enum(["arm64", "x64"]);
6740
7193
  const GpuInfoSchema = zod.z.object({
@@ -6788,41 +7241,24 @@ const platformProbeCapability = {
6788
7241
  scope: "system",
6789
7242
  mode: "singleton",
6790
7243
  methods: {
6791
- /** Return cached hardware + scored backends for this node. */
6792
- getCapabilities: method(
6793
- zod.z.void(),
6794
- PlatformCapabilitiesSchema
6795
- ),
6796
- /** Convenience getter — hardware only. */
6797
- getHardware: method(
6798
- zod.z.void(),
6799
- HardwareInfoSchema
6800
- ),
6801
- /**
6802
- * Resolve the best (model, runtime, backend, format) for a given list
6803
- * of requirements, using this node's scored backends as the candidate
6804
- * pool. Always returns a config — falls back to CPU when no preferred
6805
- * backend matches.
6806
- */
7244
+ getCapabilities: method(zod.z.void(), PlatformCapabilitiesSchema),
7245
+ getHardware: method(zod.z.void(), HardwareInfoSchema),
6807
7246
  resolveInferenceConfig: method(
6808
- zod.z.object({
6809
- requirements: zod.z.array(ModelRequirementSchema).readonly()
6810
- }),
7247
+ zod.z.object({ requirements: zod.z.array(ModelRequirementSchema).readonly() }),
6811
7248
  ResolvedInferenceConfigSchema
6812
7249
  ),
6813
- /**
6814
- * Resolve the ordered HW acceleration backend list for this node.
6815
- * Pass `prefer` to hint a specific backend; pass `null` or omit
6816
- * for auto-detection. Replaces the legacy `$hwaccel.resolve`
6817
- * Moleculer action — cap routing handles per-node dispatch via
6818
- * `nodeId`.
6819
- */
6820
7250
  resolveHwAccel: method(
6821
- zod.z.object({
6822
- prefer: HwAccelBackendInputSchema,
6823
- nodeId: zod.z.string().optional()
6824
- }),
7251
+ zod.z.object({ prefer: HwAccelBackendInputSchema, nodeId: zod.z.string().optional() }),
6825
7252
  HwAccelResolutionSchema
7253
+ ),
7254
+ /**
7255
+ * Hardware-encoder probe — see Task #185. Cached after first call.
7256
+ */
7257
+ getHardwareEncoders: method(zod.z.void(), HardwareEncodersSchema),
7258
+ refreshHardwareEncoders: method(
7259
+ zod.z.void(),
7260
+ HardwareEncodersSchema,
7261
+ { kind: "mutation", auth: "admin" }
6826
7262
  )
6827
7263
  }
6828
7264
  };
@@ -7059,25 +7495,11 @@ const MeshStatusSchema = zod.z.object({
7059
7495
  /** Last error from the daemon, when not joined. */
7060
7496
  error: zod.z.string().optional()
7061
7497
  });
7062
- const PublicIngressConfigSchema = zod.z.object({
7063
- /** Whether the provider should expose CamStack via its public
7064
- * ingress (Tailscale Funnel, etc.). */
7065
- enabled: zod.z.boolean(),
7066
- /** Local port to forward. Auto-detected from the hub HTTP port
7067
- * when omitted. */
7068
- port: zod.z.number().int().min(1).max(65535).optional()
7069
- });
7070
- const MeshIngressConfigSchema = zod.z.object({
7071
- /** Whether the provider should expose CamStack inside the mesh
7072
- * via HTTPS (Tailscale Serve, etc.) instead of just raw IP. */
7073
- enabled: zod.z.boolean(),
7074
- /** Local port to forward. Auto-detected when omitted. */
7075
- port: zod.z.number().int().min(1).max(65535).optional()
7076
- });
7077
7498
  const meshNetworkCapability = {
7078
7499
  name: "mesh-network",
7079
7500
  scope: "system",
7080
7501
  mode: "collection",
7502
+ providerKind: "mesh",
7081
7503
  methods: {
7082
7504
  /** Return the current join state + endpoints + peer count. Cheap
7083
7505
  * poll-target — admin UI hits this every few seconds. */
@@ -7099,6 +7521,29 @@ const meshNetworkCapability = {
7099
7521
  zod.z.object({ joined: zod.z.literal(true) }),
7100
7522
  { kind: "mutation" }
7101
7523
  ),
7524
+ /**
7525
+ * Start an interactive browser-redirect login flow.
7526
+ *
7527
+ * The provider spawns its daemon's join command without a pre-auth
7528
+ * key, captures the verification URL the daemon prints, and returns
7529
+ * it. The admin UI opens the URL in a new tab; the user completes
7530
+ * authentication in the provider's web console and the background
7531
+ * process self-terminates. The caller then polls `getStatus()` until
7532
+ * `joined: true`.
7533
+ *
7534
+ * Mirrors the `tailscale up` (no `--auth-key`) flow.
7535
+ */
7536
+ startLogin: method(
7537
+ zod.z.object({
7538
+ /** Optional hostname override the host should advertise once joined. */
7539
+ hostname: zod.z.string().optional()
7540
+ }),
7541
+ zod.z.object({
7542
+ /** Authentication URL the operator should open in a browser. */
7543
+ loginUrl: zod.z.string()
7544
+ }),
7545
+ { kind: "mutation" }
7546
+ ),
7102
7547
  /** Leave the mesh. After this the meshIp/magicDnsHostname/etc.
7103
7548
  * vanish until the next `join`. */
7104
7549
  leave: method(
@@ -7111,21 +7556,32 @@ const meshNetworkCapability = {
7111
7556
  peers: zod.z.array(MeshPeerSchema).readonly()
7112
7557
  })),
7113
7558
  /**
7114
- * Toggle the public ingress (e.g. Tailscale Funnel) that maps a
7115
- * local port to the open internet via the provider's edge. Idempotent.
7116
- */
7117
- setPublicIngress: method(
7118
- PublicIngressConfigSchema,
7119
- zod.z.object({ success: zod.z.literal(true) }),
7120
- { kind: "mutation" }
7121
- ),
7122
- /**
7123
- * Toggle the in-mesh HTTPS ingress (e.g. Tailscale Serve) so peers
7124
- * inside the mesh hit a valid TLS endpoint instead of the raw IP.
7559
+ * Probe the mesh daemon / API for a sanity check WITHOUT joining.
7560
+ * Operator-facing "test connection" button: validates the auth key
7561
+ * + reaches the control plane and reports back what would happen
7562
+ * on join.
7563
+ *
7564
+ * Tailscale: dry-runs `tailscale up --auth-key=… --reset` against
7565
+ * an idempotency probe; for an already-joined node it just returns
7566
+ * the current status.
7125
7567
  */
7126
- setMeshIngress: method(
7127
- MeshIngressConfigSchema,
7128
- zod.z.object({ success: zod.z.literal(true) }),
7568
+ testConnection: method(
7569
+ zod.z.object({
7570
+ /** Optional auth key — when provided, probes the key validity
7571
+ * against the provider's API. Omit when already joined to
7572
+ * just ping the daemon. */
7573
+ authKey: zod.z.string().optional()
7574
+ }),
7575
+ zod.z.object({
7576
+ ok: zod.z.boolean(),
7577
+ /** Provider-side identifier resolved by the probe (tailnet
7578
+ * name for Tailscale, network id for ZeroTier, etc.). */
7579
+ tenant: zod.z.string().optional(),
7580
+ /** Daemon binary version, when reachable. */
7581
+ daemonVersion: zod.z.string().optional(),
7582
+ /** Human-readable error when `ok: false`. */
7583
+ error: zod.z.string().optional()
7584
+ }),
7129
7585
  { kind: "mutation" }
7130
7586
  )
7131
7587
  }
@@ -7184,26 +7640,54 @@ const meshOrchestratorCapability = {
7184
7640
  )
7185
7641
  }
7186
7642
  };
7187
- const UserRoleSchema = zod.z.enum(["admin", "viewer", "agent", "scoped"]);
7643
+ const MethodAccessSchema = zod.z.enum(["view", "create", "delete"]);
7188
7644
  const AllowedProviderSchema = zod.z.union([zod.z.literal("*"), zod.z.array(zod.z.string())]);
7189
7645
  const AllowedDevicesSchema = zod.z.record(zod.z.string(), zod.z.union([zod.z.literal("*"), zod.z.array(zod.z.string())]));
7190
- const MethodAccessSchema = zod.z.enum(["view", "create", "delete"]);
7191
- const TokenScopeSchema = zod.z.object({
7192
- type: zod.z.enum(["addon", "capability"]),
7193
- target: zod.z.string(),
7194
- access: zod.z.array(MethodAccessSchema).min(1)
7195
- });
7646
+ const CapScopeSchema = zod.z.enum(["device", "system"]);
7647
+ const TokenScopeSchema = zod.z.discriminatedUnion("type", [
7648
+ zod.z.object({
7649
+ type: zod.z.literal("category"),
7650
+ target: CapScopeSchema,
7651
+ access: zod.z.array(MethodAccessSchema).min(1)
7652
+ }),
7653
+ zod.z.object({
7654
+ type: zod.z.literal("capability"),
7655
+ target: zod.z.string(),
7656
+ access: zod.z.array(MethodAccessSchema).min(1)
7657
+ }),
7658
+ zod.z.object({
7659
+ type: zod.z.literal("addon"),
7660
+ target: zod.z.string(),
7661
+ access: zod.z.array(MethodAccessSchema).min(1)
7662
+ }),
7663
+ zod.z.object({
7664
+ type: zod.z.literal("device"),
7665
+ /**
7666
+ * One or more deviceIds (serialised as strings for wire-format
7667
+ * consistency with the rest of the union). Matcher accepts if
7668
+ * `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
7669
+ * of one scope-per-device when granting access to a set of cameras.
7670
+ */
7671
+ targets: zod.z.array(zod.z.string()).min(1),
7672
+ access: zod.z.array(MethodAccessSchema).min(1)
7673
+ })
7674
+ ]);
7196
7675
  const UserRecordSchema = zod.z.object({
7197
7676
  id: zod.z.string(),
7198
7677
  username: zod.z.string(),
7199
7678
  passwordHash: zod.z.string(),
7200
- role: UserRoleSchema,
7679
+ /**
7680
+ * Admin bypass. When true, the middleware skips the scope-access
7681
+ * check entirely. There is no other axis of privilege; the legacy
7682
+ * role enum collapsed onto this boolean in v2.
7683
+ */
7684
+ isAdmin: zod.z.boolean().default(false),
7201
7685
  allowedProviders: AllowedProviderSchema,
7202
7686
  allowedDevices: AllowedDevicesSchema,
7203
7687
  /**
7204
- * Scopes granted to this user. Admins bypass; their `scopes` is ignored.
7205
- * Non-admins (`viewer`, `agent`, `scoped`) without scopes are locked out
7206
- * of every protected call.
7688
+ * Scopes granted to this user. Admins bypass; their `scopes` is
7689
+ * ignored. Non-admins without scopes are locked out of every
7690
+ * protected call.
7207
7691
  */
7208
7692
  scopes: zod.z.array(TokenScopeSchema).default([]),
7209
7693
  createdAt: zod.z.number(),
@@ -7212,7 +7696,7 @@ const UserRecordSchema = zod.z.object({
7212
7696
  const ApiKeyRecordSchema = zod.z.object({
7213
7697
  id: zod.z.string(),
7214
7698
  label: zod.z.string(),
7215
- role: UserRoleSchema,
7699
+ isAdmin: zod.z.boolean().default(false),
7216
7700
  allowedProviders: AllowedProviderSchema,
7217
7701
  allowedDevices: AllowedDevicesSchema,
7218
7702
  tokenHash: zod.z.string(),
@@ -7236,7 +7720,7 @@ const ScopedTokenSchema = zod.z.object({
7236
7720
  const UserSummarySchema = zod.z.object({
7237
7721
  id: zod.z.string(),
7238
7722
  username: zod.z.string(),
7239
- role: UserRoleSchema,
7723
+ isAdmin: zod.z.boolean().default(false),
7240
7724
  allowedProviders: zod.z.union([zod.z.array(zod.z.string()), zod.z.literal("*")]),
7241
7725
  allowedDevices: zod.z.record(zod.z.string(), zod.z.union([zod.z.array(zod.z.string()), zod.z.literal("*")])),
7242
7726
  scopes: zod.z.array(TokenScopeSchema).default([]),
@@ -7246,14 +7730,14 @@ const UserSummarySchema = zod.z.object({
7246
7730
  const CreateUserInputSchema = zod.z.object({
7247
7731
  username: zod.z.string(),
7248
7732
  password: zod.z.string().min(6),
7249
- role: UserRoleSchema,
7733
+ isAdmin: zod.z.boolean().default(false),
7250
7734
  allowedProviders: zod.z.union([zod.z.array(zod.z.string()), zod.z.literal("*")]).optional(),
7251
7735
  allowedDevices: zod.z.record(zod.z.string(), zod.z.union([zod.z.array(zod.z.string()), zod.z.literal("*")])).optional(),
7252
7736
  scopes: zod.z.array(TokenScopeSchema).optional()
7253
7737
  });
7254
7738
  const UpdateUserInputSchema = zod.z.object({
7255
7739
  id: zod.z.string(),
7256
- role: UserRoleSchema.optional(),
7740
+ isAdmin: zod.z.boolean().optional(),
7257
7741
  allowedProviders: zod.z.union([zod.z.array(zod.z.string()), zod.z.literal("*")]).optional(),
7258
7742
  allowedDevices: zod.z.record(zod.z.string(), zod.z.union([zod.z.array(zod.z.string()), zod.z.literal("*")])).optional(),
7259
7743
  scopes: zod.z.array(TokenScopeSchema).optional()
@@ -7261,7 +7745,7 @@ const UpdateUserInputSchema = zod.z.object({
7261
7745
  const ApiKeySummarySchema = zod.z.object({
7262
7746
  id: zod.z.string(),
7263
7747
  label: zod.z.string(),
7264
- role: UserRoleSchema,
7748
+ isAdmin: zod.z.boolean().default(false),
7265
7749
  allowedProviders: zod.z.union([zod.z.array(zod.z.string()), zod.z.literal("*")]).optional(),
7266
7750
  allowedDevices: zod.z.record(zod.z.string(), zod.z.union([zod.z.array(zod.z.string()), zod.z.literal("*")])).optional(),
7267
7751
  tokenPrefix: zod.z.string(),
@@ -7270,7 +7754,7 @@ const ApiKeySummarySchema = zod.z.object({
7270
7754
  });
7271
7755
  const CreateApiKeyInputSchema = zod.z.object({
7272
7756
  label: zod.z.string(),
7273
- role: UserRoleSchema,
7757
+ isAdmin: zod.z.boolean().default(false),
7274
7758
  allowedProviders: zod.z.union([zod.z.array(zod.z.string()), zod.z.literal("*")]).optional(),
7275
7759
  allowedDevices: zod.z.record(zod.z.string(), zod.z.union([zod.z.array(zod.z.string()), zod.z.literal("*")])).optional()
7276
7760
  });
@@ -7284,16 +7768,11 @@ const ScopedTokenSummarySchema = zod.z.object({
7284
7768
  name: zod.z.string(),
7285
7769
  tokenPrefix: zod.z.string(),
7286
7770
  scopes: zod.z.array(TokenScopeSchema),
7287
- // Mirror the storage schema: `.nullish()` accepts the SQLite-native
7288
- // `null` for absent timestamps as well as in-memory `undefined`.
7289
7771
  expiresAt: zod.z.number().nullish(),
7290
7772
  lastUsedAt: zod.z.number().nullish(),
7291
7773
  createdAt: zod.z.number()
7292
7774
  });
7293
7775
  const CreateScopedTokenInputSchema = zod.z.object({
7294
- // The owner the token is issued on behalf of. `adminProcedure` gates
7295
- // this call so an admin can mint tokens for any user; the CLI passes
7296
- // its own logged-in `user.id` here.
7297
7776
  userId: zod.z.string(),
7298
7777
  name: zod.z.string(),
7299
7778
  scopes: zod.z.array(TokenScopeSchema),
@@ -7303,45 +7782,85 @@ const CreateScopedTokenResultSchema = zod.z.object({
7303
7782
  token: zod.z.string(),
7304
7783
  record: ScopedTokenSummarySchema
7305
7784
  });
7785
+ const TotpSetupResultSchema = zod.z.object({
7786
+ secret: zod.z.string(),
7787
+ otpauthUrl: zod.z.string()
7788
+ });
7789
+ const TotpStatusSchema = zod.z.object({
7790
+ /** True iff `confirmedAt != null` — a pending half-enrollment is reported as `enabled: false`. */
7791
+ enabled: zod.z.boolean(),
7792
+ /** Null when no row exists OR the row is still pending confirmation. */
7793
+ confirmedAt: zod.z.number().nullable()
7794
+ });
7306
7795
  const userManagementCapability = {
7307
7796
  name: "user-management",
7308
7797
  scope: "system",
7309
7798
  mode: "singleton",
7310
7799
  methods: {
7311
- // ── Users ──────────────────────────────────────────────────────
7312
7800
  listUsers: method(zod.z.void(), zod.z.array(UserSummarySchema), { auth: "admin" }),
7313
- createUser: method(CreateUserInputSchema, UserSummarySchema, { kind: "mutation", auth: "admin" }),
7314
- updateUser: method(UpdateUserInputSchema, zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation", auth: "admin" }),
7315
- deleteUser: method(zod.z.object({ id: zod.z.string() }), zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation", auth: "admin" }),
7801
+ createUser: method(CreateUserInputSchema, UserSummarySchema, { kind: "mutation", auth: "admin", access: "create" }),
7802
+ updateUser: method(UpdateUserInputSchema, zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation", auth: "admin", access: "create" }),
7803
+ deleteUser: method(zod.z.object({ id: zod.z.string() }), zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation", auth: "admin", access: "delete" }),
7316
7804
  resetPassword: method(
7317
7805
  zod.z.object({ id: zod.z.string(), newPassword: zod.z.string().min(6) }),
7318
7806
  zod.z.object({ success: zod.z.literal(true) }),
7319
- { kind: "mutation", auth: "admin" }
7807
+ { kind: "mutation", auth: "admin", access: "create" }
7320
7808
  ),
7321
- /**
7322
- * Replace the scope set on a user. Subset check: the caller's scopes
7323
- * must include every requested scope+access (admin bypasses).
7324
- */
7325
7809
  setUserScopes: method(
7326
7810
  zod.z.object({ userId: zod.z.string(), scopes: zod.z.array(TokenScopeSchema) }),
7327
7811
  zod.z.object({ success: zod.z.literal(true) }),
7328
- { kind: "mutation", auth: "admin" }
7812
+ { kind: "mutation", auth: "admin", access: "create" }
7329
7813
  ),
7330
7814
  validateCredentials: method(
7331
7815
  zod.z.object({ username: zod.z.string(), password: zod.z.string() }),
7332
7816
  UserSummarySchema.extend({ passwordHash: zod.z.string() }).nullable(),
7333
- { kind: "mutation" }
7817
+ { kind: "mutation", access: "view" }
7334
7818
  ),
7335
- // ── API Keys ──────────────────────────────────────────────────
7336
7819
  listApiKeys: method(zod.z.void(), zod.z.array(ApiKeySummarySchema), { auth: "admin" }),
7337
- createApiKey: method(CreateApiKeyInputSchema, CreateApiKeyResultSchema, { kind: "mutation", auth: "admin" }),
7338
- revokeApiKey: method(zod.z.object({ id: zod.z.string() }), zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation", auth: "admin" }),
7339
- validateApiKey: method(zod.z.object({ token: zod.z.string() }), ApiKeySummarySchema.nullable(), { kind: "mutation" }),
7340
- // ── Scoped Tokens ─────────────────────────────────────────────
7341
- createScopedToken: method(CreateScopedTokenInputSchema, CreateScopedTokenResultSchema, { kind: "mutation", auth: "admin" }),
7342
- revokeScopedToken: method(zod.z.object({ id: zod.z.string() }), zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation", auth: "admin" }),
7343
- validateScopedToken: method(zod.z.object({ token: zod.z.string() }), ScopedTokenSummarySchema.nullable()),
7344
- listScopedTokens: method(zod.z.object({ userId: zod.z.string() }), zod.z.array(ScopedTokenSummarySchema), { auth: "admin" })
7820
+ createApiKey: method(CreateApiKeyInputSchema, CreateApiKeyResultSchema, { kind: "mutation", auth: "admin", access: "create" }),
7821
+ revokeApiKey: method(zod.z.object({ id: zod.z.string() }), zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation", auth: "admin", access: "delete" }),
7822
+ validateApiKey: method(zod.z.object({ token: zod.z.string() }), ApiKeySummarySchema.nullable(), { kind: "mutation", access: "view" }),
7823
+ createScopedToken: method(CreateScopedTokenInputSchema, CreateScopedTokenResultSchema, { kind: "mutation", auth: "admin", access: "create" }),
7824
+ revokeScopedToken: method(zod.z.object({ id: zod.z.string() }), zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation", auth: "admin", access: "delete" }),
7825
+ validateScopedToken: method(zod.z.object({ token: zod.z.string() }), ScopedTokenSummarySchema.nullable(), { access: "view" }),
7826
+ listScopedTokens: method(zod.z.object({ userId: zod.z.string() }), zod.z.array(ScopedTokenSummarySchema), { auth: "admin" }),
7827
+ // ── TOTP / 2FA ─────────────────────────────────────────────────
7828
+ //
7829
+ // Setup → Confirm → (Verify on login) → Disable.
7830
+ //
7831
+ // Admin-only for now: operator enrolls TOTP on a user's behalf by
7832
+ // pairing their authenticator with the returned QR. Self-service
7833
+ // enrollment is a follow-up (needs the cap framework to expose the
7834
+ // caller's identity to the provider so the provider can enforce
7835
+ // self-or-admin).
7836
+ setupTotp: method(
7837
+ zod.z.object({ userId: zod.z.string() }),
7838
+ TotpSetupResultSchema,
7839
+ { kind: "mutation", auth: "admin", access: "create" }
7840
+ ),
7841
+ confirmTotp: method(
7842
+ zod.z.object({ userId: zod.z.string(), code: zod.z.string() }),
7843
+ zod.z.object({ success: zod.z.literal(true) }),
7844
+ { kind: "mutation", auth: "admin", access: "create" }
7845
+ ),
7846
+ disableTotp: method(
7847
+ zod.z.object({ userId: zod.z.string() }),
7848
+ zod.z.object({ success: zod.z.literal(true) }),
7849
+ { kind: "mutation", auth: "admin", access: "delete" }
7850
+ ),
7851
+ getTotpStatus: method(
7852
+ zod.z.object({ userId: zod.z.string() }),
7853
+ TotpStatusSchema,
7854
+ { auth: "admin" }
7855
+ ),
7856
+ // Public (no `auth`) — used by the login flow's second-step
7857
+ // challenge endpoint. The userId comes from the password-validation
7858
+ // step (signed bridge token), so this method is not a free oracle.
7859
+ verifyTotp: method(
7860
+ zod.z.object({ userId: zod.z.string(), code: zod.z.string() }),
7861
+ zod.z.object({ valid: zod.z.boolean() }),
7862
+ { kind: "mutation", access: "view" }
7863
+ )
7345
7864
  }
7346
7865
  };
7347
7866
  const FeatureManifestSchema = zod.z.object({
@@ -7969,6 +8488,7 @@ exports.AUDIO_MACRO_LABELS = AUDIO_MACRO_LABELS;
7969
8488
  exports.AbortUploadInputSchema = AbortUploadInputSchema;
7970
8489
  exports.AccessoriesStatusSchema = AccessoriesStatusSchema;
7971
8490
  exports.AccessoryKind = AccessoryKind;
8491
+ exports.AddBrokerInputSchema = AddBrokerInputSchema;
7972
8492
  exports.AddonAutoUpdateSchema = AddonAutoUpdateSchema;
7973
8493
  exports.AddonListItemSchema = AddonListItemSchema;
7974
8494
  exports.AddonPageDeclarationSchema = AddonPageDeclarationSchema;
@@ -8014,10 +8534,12 @@ exports.BoundingBoxSchema = BoundingBoxSchema;
8014
8534
  exports.BrightnessStatusSchema = BrightnessStatusSchema;
8015
8535
  exports.BrokerAudioClientSchema = BrokerAudioClientSchema;
8016
8536
  exports.BrokerClientsSchema = BrokerClientsSchema;
8537
+ exports.BrokerConnectionDetailsSchema = BrokerConnectionDetailsSchema;
8017
8538
  exports.BrokerDecodedClientSchema = BrokerDecodedClientSchema;
8539
+ exports.BrokerInfoSchema = BrokerInfoSchema;
8018
8540
  exports.BrokerRtspClientSchema = BrokerRtspClientSchema;
8019
8541
  exports.BrokerStatsSchema = BrokerStatsSchema;
8020
- exports.BrokerStatusSchema = BrokerStatusSchema;
8542
+ exports.BrokerStatusSchema = BrokerStatusSchema$1;
8021
8543
  exports.CAM_PROFILE_ORDER = CAM_PROFILE_ORDER;
8022
8544
  exports.CamProfileSchema = CamProfileSchema;
8023
8545
  exports.CamStreamKindSchema = CamStreamKindSchema;
@@ -8027,6 +8549,7 @@ exports.CameraCredentialsStatusSchema = CameraCredentialsStatusSchema;
8027
8549
  exports.CameraMetricsSchema = CameraMetricsSchema;
8028
8550
  exports.CameraMetricsWithDeviceIdSchema = CameraMetricsWithDeviceIdSchema;
8029
8551
  exports.CameraStreamSchema = CameraStreamSchema;
8552
+ exports.CapScopeSchema = CapScopeSchema;
8030
8553
  exports.CapabilityBindingsSchema = CapabilityBindingsSchema;
8031
8554
  exports.ChargingStatus = ChargingStatus;
8032
8555
  exports.ClientNetworkStatsSchema = ClientNetworkStatsSchema;
@@ -8056,6 +8579,7 @@ exports.DecoderStatsSchema = DecoderStatsSchema;
8056
8579
  exports.DeleteIntegrationResultSchema = DeleteIntegrationResultSchema;
8057
8580
  exports.DetectorOutputSchema = DetectorOutputSchema;
8058
8581
  exports.DeviceDiscoveryStatusSchema = DeviceDiscoveryStatusSchema;
8582
+ exports.DeviceExportStatusSchema = DeviceExportStatusSchema;
8059
8583
  exports.DeviceFeature = DeviceFeature;
8060
8584
  exports.DeviceInfoSchema = DeviceInfoSchema;
8061
8585
  exports.DeviceNetworkStatsSchema = DeviceNetworkStatsSchema;
@@ -8074,11 +8598,14 @@ exports.EndDownloadInputSchema = EndDownloadInputSchema;
8074
8598
  exports.EnrichedWidgetMetadataSchema = EnrichedWidgetMetadataSchema;
8075
8599
  exports.EventItemSchema = EventItemSchema;
8076
8600
  exports.EventKindSchema = EventKindSchema;
8601
+ exports.ExposeInputSchema = ExposeInputSchema;
8602
+ exports.ExposedDeviceSchema = ExposedDeviceSchema;
8077
8603
  exports.ExposedResourceSchema = ExposedResourceSchema;
8078
8604
  exports.FeatureManifestSchema = FeatureManifestSchema;
8079
8605
  exports.FeatureProbeStatusSchema = FeatureProbeStatusSchema;
8080
8606
  exports.FinalizeUploadInputSchema = FinalizeUploadInputSchema;
8081
8607
  exports.FrameInputSchema = FrameInputSchema;
8608
+ exports.GetStreamWithCodecInputSchema = GetStreamWithCodecInputSchema;
8082
8609
  exports.GlobalMetricsSchema = GlobalMetricsSchema;
8083
8610
  exports.HWACCEL_OPTIONS = HWACCEL_OPTIONS;
8084
8611
  exports.HealthStatusSchema = HealthStatusSchema;
@@ -8125,6 +8652,7 @@ exports.PIPELINE_FLOW_CAPABILITY_NAMES = PIPELINE_FLOW_CAPABILITY_NAMES;
8125
8652
  exports.PIPELINE_OWNER_CAPABILITY_NAMES = PIPELINE_OWNER_CAPABILITY_NAMES;
8126
8653
  exports.PackageUpdateSchema = PackageUpdateSchema;
8127
8654
  exports.PackageVersionInfoSchema = PackageVersionInfoSchema;
8655
+ exports.PasskeySummarySchema = PasskeySummarySchema;
8128
8656
  exports.PcmSampleFormatSchema = PcmSampleFormatSchema;
8129
8657
  exports.PerScopeBreakdownSchema = PerScopeBreakdownSchema;
8130
8658
  exports.PipelineAssignmentSchema = PipelineAssignmentSchema;
@@ -8151,6 +8679,7 @@ exports.RegisteredStreamSchema = RegisteredStreamSchema;
8151
8679
  exports.RemoteAccessEndpointSchema = RemoteAccessEndpointSchema;
8152
8680
  exports.RemoteAccessProviderInfoSchema = RemoteAccessProviderInfoSchema;
8153
8681
  exports.ReportMotionInputSchema = ReportMotionInputSchema;
8682
+ exports.RtpSourceSchema = RtpSourceSchema;
8154
8683
  exports.RtspRestreamEntrySchema = RtspRestreamEntrySchema;
8155
8684
  exports.RunnerCameraConfigSchema = RunnerCameraConfigSchema;
8156
8685
  exports.RunnerCameraDeviceUIFields = RunnerCameraDeviceUIFields;
@@ -8161,12 +8690,18 @@ exports.ScopedTokenSchema = ScopedTokenSchema;
8161
8690
  exports.ScopedTokenSummarySchema = ScopedTokenSummarySchema;
8162
8691
  exports.SearchResultSchema = SearchResultSchema;
8163
8692
  exports.SegmentSchema = SegmentSchema;
8693
+ exports.SendEmailInputSchema = SendEmailInputSchema;
8694
+ exports.SendEmailResultSchema = SendEmailResultSchema;
8164
8695
  exports.SettingsPatchSchema = SettingsPatchSchema;
8165
8696
  exports.SettingsRecordSchema = SettingsRecordSchema;
8166
8697
  exports.SettingsSchemaWithValuesSchema = SettingsSchemaWithValuesSchema;
8167
8698
  exports.SettingsUpdateResultSchema = SettingsUpdateResultSchema;
8699
+ exports.SmtpStatusSchema = SmtpStatusSchema;
8168
8700
  exports.SnapshotImageSchema = SnapshotImageSchema;
8169
8701
  exports.SpatialDetectionSchema = SpatialDetectionSchema;
8702
+ exports.SsoBridgeClaimsSchema = SsoBridgeClaimsSchema;
8703
+ exports.StartEmbeddedInputSchema = StartEmbeddedInputSchema;
8704
+ exports.StatusSchema = StatusSchema;
8170
8705
  exports.StorageLocationRefSchema = StorageLocationRefSchema;
8171
8706
  exports.StorageLocationSchema = StorageLocationSchema;
8172
8707
  exports.StorageLocationTypeSchema = StorageLocationTypeSchema;
@@ -8189,10 +8724,10 @@ exports.TrackStateSchema = TrackStateSchema;
8189
8724
  exports.TrackedDetectionSchema = TrackedDetectionSchema;
8190
8725
  exports.TurnProviderInfoSchema = TurnProviderInfoSchema;
8191
8726
  exports.TurnServerSchema = TurnServerSchema;
8727
+ exports.UnexposeInputSchema = UnexposeInputSchema;
8192
8728
  exports.UpdateIntegrationInputSchema = UpdateIntegrationInputSchema;
8193
8729
  exports.UpdateUserInputSchema = UpdateUserInputSchema;
8194
8730
  exports.UserRecordSchema = UserRecordSchema;
8195
- exports.UserRoleSchema = UserRoleSchema;
8196
8731
  exports.UserSummarySchema = UserSummarySchema;
8197
8732
  exports.WELL_KNOWN_TABS = WELL_KNOWN_TABS;
8198
8733
  exports.WELL_KNOWN_TAB_MAP = WELL_KNOWN_TAB_MAP;
@@ -8236,6 +8771,7 @@ exports.cameraStreamsCapability = cameraStreamsCapability;
8236
8771
  exports.decoderCapability = decoderCapability;
8237
8772
  exports.detectionPipelineCapability = detectionPipelineCapability;
8238
8773
  exports.deviceDiscoveryCapability = deviceDiscoveryCapability;
8774
+ exports.deviceExportCapability = deviceExportCapability;
8239
8775
  exports.deviceManagerCapability = deviceManagerCapability;
8240
8776
  exports.deviceMatchesProfile = deviceMatchesProfile;
8241
8777
  exports.deviceOpsCapability = deviceOpsCapability;
@@ -8263,6 +8799,7 @@ exports.metricsProviderCapability = metricsProviderCapability;
8263
8799
  exports.motionCapability = motionCapability;
8264
8800
  exports.motionDetectionCapability = motionDetectionCapability;
8265
8801
  exports.motionTriggerCapability = motionTriggerCapability;
8802
+ exports.mqttBrokerCapability = mqttBrokerCapability;
8266
8803
  exports.nativeObjectDetectionCapability = nativeObjectDetectionCapability;
8267
8804
  exports.networkAccessCapability = networkAccessCapability;
8268
8805
  exports.networkQualityCapability = networkQualityCapability;
@@ -8283,8 +8820,10 @@ exports.remoteAccessCapability = remoteAccessCapability;
8283
8820
  exports.resolveDeviceProfile = resolveDeviceProfile;
8284
8821
  exports.restreamerCapability = restreamerCapability;
8285
8822
  exports.settingsStoreCapability = settingsStoreCapability;
8823
+ exports.smtpProviderCapability = smtpProviderCapability;
8286
8824
  exports.snapshotCapability = snapshotCapability;
8287
8825
  exports.snapshotProviderCapability = snapshotProviderCapability;
8826
+ exports.ssoBridgeCapability = ssoBridgeCapability;
8288
8827
  exports.storageCapability = storageCapability;
8289
8828
  exports.storageProviderCapability = storageProviderCapability;
8290
8829
  exports.streamBrokerCapability = streamBrokerCapability;
@@ -8295,10 +8834,11 @@ exports.toastCapability = toastCapability;
8295
8834
  exports.turnOrchestratorCapability = turnOrchestratorCapability;
8296
8835
  exports.turnProviderCapability = turnProviderCapability;
8297
8836
  exports.userManagementCapability = userManagementCapability;
8837
+ exports.userPasskeysCapability = userPasskeysCapability;
8298
8838
  exports.webrtcCapability = webrtcCapability;
8299
8839
  exports.webrtcClientHintsSchema = webrtcClientHintsSchema;
8300
8840
  exports.webrtcSessionCapability = webrtcSessionCapability;
8301
8841
  exports.zoneAnalyticsCapability = zoneAnalyticsCapability;
8302
8842
  exports.zoneRulesCapability = zoneRulesCapability;
8303
8843
  exports.zonesCapability = zonesCapability;
8304
- //# sourceMappingURL=index-s8uJNgNs.js.map
8844
+ //# sourceMappingURL=index-BUBhoPUu.js.map