@camstack/addon-export-hap 1.0.4 → 1.0.5

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.
@@ -5018,6 +5018,18 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
5018
5018
  */
5019
5019
  EventCategory["PipelineEngineMetricsSnapshot"] = "pipeline.engine-metrics-snapshot";
5020
5020
  /**
5021
+ * Per-node detection-engine runtime-provisioning transition. Emitted by
5022
+ * the detection-pipeline provider on every state change of its lazy
5023
+ * engine-provisioning machine (idle → installing → verifying → ready,
5024
+ * or → failed with a `nextRetryAt`). Payload is the
5025
+ * `EngineProvisioningState` snapshot; `event.source.nodeId` carries the
5026
+ * node. The Pipeline page subscribes to drive a live "installing
5027
+ * OpenVINO… / ready" indicator per node without polling
5028
+ * `pipelineExecutor.getEngineProvisioning`. Telemetry-grade (D8): the UI
5029
+ * also reads the cap snapshot on mount / reconnect. Phase 2.
5030
+ */
5031
+ EventCategory["PipelineEngineProvisioning"] = "pipeline.engine-provisioning";
5032
+ /**
5021
5033
  * Cluster topology snapshot. Carries the same payload returned by
5022
5034
  * `nodes.topology` (every reachable node + addons + processes).
5023
5035
  * Emitted by the hub on any agent / addon lifecycle change
@@ -9236,6 +9248,24 @@ var DetectorOutputSchema = object({
9236
9248
  inferenceMs: number(),
9237
9249
  modelId: string()
9238
9250
  });
9251
+ var EngineProvisioningSchema = object({
9252
+ runtimeId: _enum([
9253
+ "onnx",
9254
+ "openvino",
9255
+ "coreml"
9256
+ ]).nullable(),
9257
+ device: string().nullable(),
9258
+ state: _enum([
9259
+ "idle",
9260
+ "installing",
9261
+ "verifying",
9262
+ "ready",
9263
+ "failed"
9264
+ ]),
9265
+ progress: number().optional(),
9266
+ error: string().optional(),
9267
+ nextRetryAt: number().optional()
9268
+ });
9239
9269
  var PipelineStepInputSchema = lazy(() => object({
9240
9270
  addonId: string(),
9241
9271
  modelId: string(),
@@ -9304,7 +9334,7 @@ var PipelineRunResultBridge = custom();
9304
9334
  method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
9305
9335
  kind: "mutation",
9306
9336
  auth: "admin"
9307
- }), method(_void(), record(string(), object({
9337
+ }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
9308
9338
  modelId: string(),
9309
9339
  settings: record(string(), unknown()).readonly()
9310
9340
  }))), method(object({ steps: record(string(), object({
@@ -12916,7 +12946,10 @@ var AgentAddonConfigSchema = object({
12916
12946
  modelId: string(),
12917
12947
  settings: record(string(), unknown()).readonly()
12918
12948
  });
12919
- var AgentPipelineSettingsSchema = object({ addonDefaults: record(string(), AgentAddonConfigSchema).readonly() });
12949
+ var AgentPipelineSettingsSchema = object({
12950
+ addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
12951
+ maxCameras: number().int().nonnegative().nullable().default(null)
12952
+ });
12920
12953
  var CameraPipelineForAgentSchema = object({
12921
12954
  steps: array(PipelineStepInputSchema).readonly(),
12922
12955
  audio: object({
@@ -13018,6 +13051,133 @@ var GlobalMetricsSchema = object({
13018
13051
  * capability providers.
13019
13052
  */
13020
13053
  var CapabilityBindingsSchema = record(string(), string());
13054
+ /** Source block — always present; derives from the stream catalog. */
13055
+ var CameraSourceStatusSchema = object({ streams: array(object({
13056
+ camStreamId: string(),
13057
+ codec: string(),
13058
+ width: number(),
13059
+ height: number(),
13060
+ fps: number(),
13061
+ kind: string()
13062
+ })).readonly() });
13063
+ /** Assignment block — always present (orchestrator-local, no remote call). */
13064
+ var CameraAssignmentStatusSchema = object({
13065
+ detectionNodeId: string().nullable(),
13066
+ decoderNodeId: string().nullable(),
13067
+ audioNodeId: string().nullable(),
13068
+ pinned: object({
13069
+ detection: boolean(),
13070
+ decoder: boolean(),
13071
+ audio: boolean()
13072
+ }),
13073
+ reasons: object({
13074
+ detection: string().optional(),
13075
+ decoder: string().optional(),
13076
+ audio: string().optional()
13077
+ })
13078
+ });
13079
+ /** Broker block — null when the broker stage is unreachable or inactive. */
13080
+ var CameraBrokerStatusSchema = object({
13081
+ profiles: array(object({
13082
+ profile: string(),
13083
+ status: string(),
13084
+ codec: string(),
13085
+ width: number(),
13086
+ height: number(),
13087
+ subscribers: number(),
13088
+ inFps: number(),
13089
+ outFps: number()
13090
+ })).readonly(),
13091
+ webrtcSessions: number(),
13092
+ rtspRestream: boolean()
13093
+ });
13094
+ /** Shared-memory ring statistics within the decoder block. */
13095
+ var CameraDecoderShmSchema = object({
13096
+ framesWritten: number(),
13097
+ getFrameHits: number(),
13098
+ getFrameMisses: number(),
13099
+ budgetMb: number()
13100
+ });
13101
+ /** Decoder block — null when the decoder stage is unreachable or inactive. */
13102
+ var CameraDecoderStatusSchema = object({
13103
+ nodeId: string(),
13104
+ formats: array(string()).readonly(),
13105
+ sessionCount: number(),
13106
+ shm: CameraDecoderShmSchema
13107
+ });
13108
+ /** Motion block — null when motion detection is not active for this device. */
13109
+ var CameraMotionStatusSchema = object({
13110
+ enabled: boolean(),
13111
+ fps: number()
13112
+ });
13113
+ /** Detection provisioning sub-block. */
13114
+ var CameraDetectionProvisioningSchema = object({
13115
+ state: _enum([
13116
+ "idle",
13117
+ "installing",
13118
+ "verifying",
13119
+ "ready",
13120
+ "failed"
13121
+ ]),
13122
+ error: string().optional()
13123
+ });
13124
+ /** Detection phase — derived from the runner's engine phase. */
13125
+ var CameraDetectionPhaseSchema = _enum([
13126
+ "idle",
13127
+ "watching",
13128
+ "active"
13129
+ ]);
13130
+ /** Detection block — null when no detection node is assigned or reachable. */
13131
+ var CameraDetectionStatusSchema = object({
13132
+ nodeId: string(),
13133
+ engine: object({
13134
+ backend: string(),
13135
+ device: string()
13136
+ }),
13137
+ phase: CameraDetectionPhaseSchema,
13138
+ configuredFps: number(),
13139
+ actualFps: number(),
13140
+ queueDepth: number(),
13141
+ avgInferenceMs: number(),
13142
+ provisioning: CameraDetectionProvisioningSchema
13143
+ });
13144
+ /** Audio block — null when no audio node is assigned or reachable. */
13145
+ var CameraAudioStatusSchema = object({
13146
+ nodeId: string(),
13147
+ enabled: boolean()
13148
+ });
13149
+ /** Recording block — null when no recording cap is active for this device. */
13150
+ var CameraRecordingStatusSchema = object({
13151
+ mode: _enum([
13152
+ "off",
13153
+ "continuous",
13154
+ "events"
13155
+ ]),
13156
+ active: boolean(),
13157
+ storageBytes: number()
13158
+ });
13159
+ /**
13160
+ * Aggregated per-camera pipeline status — server-composed, single call.
13161
+ *
13162
+ * The `assignment` and `source` blocks are always present.
13163
+ * Every other block is `null` when the stage is inactive or unreachable
13164
+ * during the bounded parallel fan-out in the orchestrator implementation.
13165
+ *
13166
+ * See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
13167
+ */
13168
+ var CameraStatusSchema = object({
13169
+ deviceId: number(),
13170
+ assignment: CameraAssignmentStatusSchema,
13171
+ source: CameraSourceStatusSchema,
13172
+ broker: CameraBrokerStatusSchema.nullable(),
13173
+ decoder: CameraDecoderStatusSchema.nullable(),
13174
+ motion: CameraMotionStatusSchema.nullable(),
13175
+ detection: CameraDetectionStatusSchema.nullable(),
13176
+ audio: CameraAudioStatusSchema.nullable(),
13177
+ recording: CameraRecordingStatusSchema.nullable(),
13178
+ /** Unix timestamp (ms) when this snapshot was composed server-side. */
13179
+ fetchedAt: number()
13180
+ });
13021
13181
  method(object({
13022
13182
  deviceId: number(),
13023
13183
  agentNodeId: string()
@@ -13085,6 +13245,12 @@ method(object({
13085
13245
  }), {
13086
13246
  kind: "mutation",
13087
13247
  auth: "admin"
13248
+ }), method(object({
13249
+ agentNodeId: string(),
13250
+ maxCameras: number().int().nonnegative().nullable()
13251
+ }), object({ success: literal(true) }), {
13252
+ kind: "mutation",
13253
+ auth: "admin"
13088
13254
  }), method(object({ deviceId: number() }), CameraPipelineSettingsSchema.nullable()), method(object({
13089
13255
  deviceId: number(),
13090
13256
  addonId: string(),
@@ -13110,7 +13276,7 @@ method(object({
13110
13276
  }), method(object({
13111
13277
  deviceId: number(),
13112
13278
  agentNodeId: string().optional()
13113
- }), CameraPipelineConfigSchema), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
13279
+ }), CameraPipelineConfigSchema), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
13114
13280
  name: string(),
13115
13281
  description: string().optional(),
13116
13282
  config: CameraPipelineConfigSchema
@@ -18902,6 +19068,12 @@ Object.freeze({
18902
19068
  addonId: null,
18903
19069
  access: "view"
18904
19070
  },
19071
+ "pipelineExecutor.getEngineProvisioning": {
19072
+ capName: "pipeline-executor",
19073
+ capScope: "system",
19074
+ addonId: null,
19075
+ access: "view"
19076
+ },
18905
19077
  "pipelineExecutor.getGlobalPipelineConfig": {
18906
19078
  capName: "pipeline-executor",
18907
19079
  capScope: "system",
@@ -19106,6 +19278,18 @@ Object.freeze({
19106
19278
  addonId: null,
19107
19279
  access: "view"
19108
19280
  },
19281
+ "pipelineOrchestrator.getCameraStatus": {
19282
+ capName: "pipeline-orchestrator",
19283
+ capScope: "system",
19284
+ addonId: null,
19285
+ access: "view"
19286
+ },
19287
+ "pipelineOrchestrator.getCameraStatuses": {
19288
+ capName: "pipeline-orchestrator",
19289
+ capScope: "system",
19290
+ addonId: null,
19291
+ access: "view"
19292
+ },
19109
19293
  "pipelineOrchestrator.getCameraStepOverrides": {
19110
19294
  capName: "pipeline-orchestrator",
19111
19295
  capScope: "system",
@@ -19190,6 +19374,12 @@ Object.freeze({
19190
19374
  addonId: null,
19191
19375
  access: "create"
19192
19376
  },
19377
+ "pipelineOrchestrator.setAgentMaxCameras": {
19378
+ capName: "pipeline-orchestrator",
19379
+ capScope: "system",
19380
+ addonId: null,
19381
+ access: "create"
19382
+ },
19193
19383
  "pipelineOrchestrator.setCameraPipelineForAgent": {
19194
19384
  capName: "pipeline-orchestrator",
19195
19385
  capScope: "system",
@@ -4993,6 +4993,18 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
4993
4993
  */
4994
4994
  EventCategory["PipelineEngineMetricsSnapshot"] = "pipeline.engine-metrics-snapshot";
4995
4995
  /**
4996
+ * Per-node detection-engine runtime-provisioning transition. Emitted by
4997
+ * the detection-pipeline provider on every state change of its lazy
4998
+ * engine-provisioning machine (idle → installing → verifying → ready,
4999
+ * or → failed with a `nextRetryAt`). Payload is the
5000
+ * `EngineProvisioningState` snapshot; `event.source.nodeId` carries the
5001
+ * node. The Pipeline page subscribes to drive a live "installing
5002
+ * OpenVINO… / ready" indicator per node without polling
5003
+ * `pipelineExecutor.getEngineProvisioning`. Telemetry-grade (D8): the UI
5004
+ * also reads the cap snapshot on mount / reconnect. Phase 2.
5005
+ */
5006
+ EventCategory["PipelineEngineProvisioning"] = "pipeline.engine-provisioning";
5007
+ /**
4996
5008
  * Cluster topology snapshot. Carries the same payload returned by
4997
5009
  * `nodes.topology` (every reachable node + addons + processes).
4998
5010
  * Emitted by the hub on any agent / addon lifecycle change
@@ -9211,6 +9223,24 @@ var DetectorOutputSchema = object({
9211
9223
  inferenceMs: number(),
9212
9224
  modelId: string()
9213
9225
  });
9226
+ var EngineProvisioningSchema = object({
9227
+ runtimeId: _enum([
9228
+ "onnx",
9229
+ "openvino",
9230
+ "coreml"
9231
+ ]).nullable(),
9232
+ device: string().nullable(),
9233
+ state: _enum([
9234
+ "idle",
9235
+ "installing",
9236
+ "verifying",
9237
+ "ready",
9238
+ "failed"
9239
+ ]),
9240
+ progress: number().optional(),
9241
+ error: string().optional(),
9242
+ nextRetryAt: number().optional()
9243
+ });
9214
9244
  var PipelineStepInputSchema = lazy(() => object({
9215
9245
  addonId: string(),
9216
9246
  modelId: string(),
@@ -9279,7 +9309,7 @@ var PipelineRunResultBridge = custom();
9279
9309
  method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
9280
9310
  kind: "mutation",
9281
9311
  auth: "admin"
9282
- }), method(_void(), record(string(), object({
9312
+ }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
9283
9313
  modelId: string(),
9284
9314
  settings: record(string(), unknown()).readonly()
9285
9315
  }))), method(object({ steps: record(string(), object({
@@ -12891,7 +12921,10 @@ var AgentAddonConfigSchema = object({
12891
12921
  modelId: string(),
12892
12922
  settings: record(string(), unknown()).readonly()
12893
12923
  });
12894
- var AgentPipelineSettingsSchema = object({ addonDefaults: record(string(), AgentAddonConfigSchema).readonly() });
12924
+ var AgentPipelineSettingsSchema = object({
12925
+ addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
12926
+ maxCameras: number().int().nonnegative().nullable().default(null)
12927
+ });
12895
12928
  var CameraPipelineForAgentSchema = object({
12896
12929
  steps: array(PipelineStepInputSchema).readonly(),
12897
12930
  audio: object({
@@ -12993,6 +13026,133 @@ var GlobalMetricsSchema = object({
12993
13026
  * capability providers.
12994
13027
  */
12995
13028
  var CapabilityBindingsSchema = record(string(), string());
13029
+ /** Source block — always present; derives from the stream catalog. */
13030
+ var CameraSourceStatusSchema = object({ streams: array(object({
13031
+ camStreamId: string(),
13032
+ codec: string(),
13033
+ width: number(),
13034
+ height: number(),
13035
+ fps: number(),
13036
+ kind: string()
13037
+ })).readonly() });
13038
+ /** Assignment block — always present (orchestrator-local, no remote call). */
13039
+ var CameraAssignmentStatusSchema = object({
13040
+ detectionNodeId: string().nullable(),
13041
+ decoderNodeId: string().nullable(),
13042
+ audioNodeId: string().nullable(),
13043
+ pinned: object({
13044
+ detection: boolean(),
13045
+ decoder: boolean(),
13046
+ audio: boolean()
13047
+ }),
13048
+ reasons: object({
13049
+ detection: string().optional(),
13050
+ decoder: string().optional(),
13051
+ audio: string().optional()
13052
+ })
13053
+ });
13054
+ /** Broker block — null when the broker stage is unreachable or inactive. */
13055
+ var CameraBrokerStatusSchema = object({
13056
+ profiles: array(object({
13057
+ profile: string(),
13058
+ status: string(),
13059
+ codec: string(),
13060
+ width: number(),
13061
+ height: number(),
13062
+ subscribers: number(),
13063
+ inFps: number(),
13064
+ outFps: number()
13065
+ })).readonly(),
13066
+ webrtcSessions: number(),
13067
+ rtspRestream: boolean()
13068
+ });
13069
+ /** Shared-memory ring statistics within the decoder block. */
13070
+ var CameraDecoderShmSchema = object({
13071
+ framesWritten: number(),
13072
+ getFrameHits: number(),
13073
+ getFrameMisses: number(),
13074
+ budgetMb: number()
13075
+ });
13076
+ /** Decoder block — null when the decoder stage is unreachable or inactive. */
13077
+ var CameraDecoderStatusSchema = object({
13078
+ nodeId: string(),
13079
+ formats: array(string()).readonly(),
13080
+ sessionCount: number(),
13081
+ shm: CameraDecoderShmSchema
13082
+ });
13083
+ /** Motion block — null when motion detection is not active for this device. */
13084
+ var CameraMotionStatusSchema = object({
13085
+ enabled: boolean(),
13086
+ fps: number()
13087
+ });
13088
+ /** Detection provisioning sub-block. */
13089
+ var CameraDetectionProvisioningSchema = object({
13090
+ state: _enum([
13091
+ "idle",
13092
+ "installing",
13093
+ "verifying",
13094
+ "ready",
13095
+ "failed"
13096
+ ]),
13097
+ error: string().optional()
13098
+ });
13099
+ /** Detection phase — derived from the runner's engine phase. */
13100
+ var CameraDetectionPhaseSchema = _enum([
13101
+ "idle",
13102
+ "watching",
13103
+ "active"
13104
+ ]);
13105
+ /** Detection block — null when no detection node is assigned or reachable. */
13106
+ var CameraDetectionStatusSchema = object({
13107
+ nodeId: string(),
13108
+ engine: object({
13109
+ backend: string(),
13110
+ device: string()
13111
+ }),
13112
+ phase: CameraDetectionPhaseSchema,
13113
+ configuredFps: number(),
13114
+ actualFps: number(),
13115
+ queueDepth: number(),
13116
+ avgInferenceMs: number(),
13117
+ provisioning: CameraDetectionProvisioningSchema
13118
+ });
13119
+ /** Audio block — null when no audio node is assigned or reachable. */
13120
+ var CameraAudioStatusSchema = object({
13121
+ nodeId: string(),
13122
+ enabled: boolean()
13123
+ });
13124
+ /** Recording block — null when no recording cap is active for this device. */
13125
+ var CameraRecordingStatusSchema = object({
13126
+ mode: _enum([
13127
+ "off",
13128
+ "continuous",
13129
+ "events"
13130
+ ]),
13131
+ active: boolean(),
13132
+ storageBytes: number()
13133
+ });
13134
+ /**
13135
+ * Aggregated per-camera pipeline status — server-composed, single call.
13136
+ *
13137
+ * The `assignment` and `source` blocks are always present.
13138
+ * Every other block is `null` when the stage is inactive or unreachable
13139
+ * during the bounded parallel fan-out in the orchestrator implementation.
13140
+ *
13141
+ * See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
13142
+ */
13143
+ var CameraStatusSchema = object({
13144
+ deviceId: number(),
13145
+ assignment: CameraAssignmentStatusSchema,
13146
+ source: CameraSourceStatusSchema,
13147
+ broker: CameraBrokerStatusSchema.nullable(),
13148
+ decoder: CameraDecoderStatusSchema.nullable(),
13149
+ motion: CameraMotionStatusSchema.nullable(),
13150
+ detection: CameraDetectionStatusSchema.nullable(),
13151
+ audio: CameraAudioStatusSchema.nullable(),
13152
+ recording: CameraRecordingStatusSchema.nullable(),
13153
+ /** Unix timestamp (ms) when this snapshot was composed server-side. */
13154
+ fetchedAt: number()
13155
+ });
12996
13156
  method(object({
12997
13157
  deviceId: number(),
12998
13158
  agentNodeId: string()
@@ -13060,6 +13220,12 @@ method(object({
13060
13220
  }), {
13061
13221
  kind: "mutation",
13062
13222
  auth: "admin"
13223
+ }), method(object({
13224
+ agentNodeId: string(),
13225
+ maxCameras: number().int().nonnegative().nullable()
13226
+ }), object({ success: literal(true) }), {
13227
+ kind: "mutation",
13228
+ auth: "admin"
13063
13229
  }), method(object({ deviceId: number() }), CameraPipelineSettingsSchema.nullable()), method(object({
13064
13230
  deviceId: number(),
13065
13231
  addonId: string(),
@@ -13085,7 +13251,7 @@ method(object({
13085
13251
  }), method(object({
13086
13252
  deviceId: number(),
13087
13253
  agentNodeId: string().optional()
13088
- }), CameraPipelineConfigSchema), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
13254
+ }), CameraPipelineConfigSchema), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
13089
13255
  name: string(),
13090
13256
  description: string().optional(),
13091
13257
  config: CameraPipelineConfigSchema
@@ -18877,6 +19043,12 @@ Object.freeze({
18877
19043
  addonId: null,
18878
19044
  access: "view"
18879
19045
  },
19046
+ "pipelineExecutor.getEngineProvisioning": {
19047
+ capName: "pipeline-executor",
19048
+ capScope: "system",
19049
+ addonId: null,
19050
+ access: "view"
19051
+ },
18880
19052
  "pipelineExecutor.getGlobalPipelineConfig": {
18881
19053
  capName: "pipeline-executor",
18882
19054
  capScope: "system",
@@ -19081,6 +19253,18 @@ Object.freeze({
19081
19253
  addonId: null,
19082
19254
  access: "view"
19083
19255
  },
19256
+ "pipelineOrchestrator.getCameraStatus": {
19257
+ capName: "pipeline-orchestrator",
19258
+ capScope: "system",
19259
+ addonId: null,
19260
+ access: "view"
19261
+ },
19262
+ "pipelineOrchestrator.getCameraStatuses": {
19263
+ capName: "pipeline-orchestrator",
19264
+ capScope: "system",
19265
+ addonId: null,
19266
+ access: "view"
19267
+ },
19084
19268
  "pipelineOrchestrator.getCameraStepOverrides": {
19085
19269
  capName: "pipeline-orchestrator",
19086
19270
  capScope: "system",
@@ -19165,6 +19349,12 @@ Object.freeze({
19165
19349
  addonId: null,
19166
19350
  access: "create"
19167
19351
  },
19352
+ "pipelineOrchestrator.setAgentMaxCameras": {
19353
+ capName: "pipeline-orchestrator",
19354
+ capScope: "system",
19355
+ addonId: null,
19356
+ access: "create"
19357
+ },
19168
19358
  "pipelineOrchestrator.setCameraPipelineForAgent": {
19169
19359
  capName: "pipeline-orchestrator",
19170
19360
  capScope: "system",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-export-hap",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "HomeKit (HAP) bridge exporter for CamStack devices. Publishes a bridged accessory per exposed device — MotionSensor in this MVP; Camera/Doorbell/Switch/Light follow.",
5
5
  "keywords": [
6
6
  "camstack",