@camstack/addon-export-alexa 1.2.134 → 1.2.136

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.
@@ -26,7 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  //#endregion
27
27
  let node_crypto = require("node:crypto");
28
28
  node_crypto = __toESM(node_crypto, 1);
29
- //#region ../types/dist/event-category-BGmz_oxW.mjs
29
+ //#region ../types/dist/event-category-B_0XEcLF.mjs
30
30
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
31
31
  EventCategory["SystemBoot"] = "system.boot";
32
32
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -527,15 +527,6 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
527
527
  EventCategory["AgentWsDisconnected"] = "agent.ws.disconnected";
528
528
  EventCategory["AgentBackupActivated"] = "agent.backup.activated";
529
529
  EventCategory["OrchestrationSettingsUpdated"] = "orchestration.settings-updated";
530
- /**
531
- * Per-agent hwaccel preference changed (user override set, cleared, or
532
- * re-probed). Observability event — decoders pull the current pref at
533
- * `createSession`, so running sessions keep their current backend until
534
- * they rotate naturally (camera add/remove, stream restart). Future
535
- * work can wire a listener in stream-broker that force-rotates live
536
- * sessions; for now this event powers logs + admin-UI toast feedback.
537
- */
538
- EventCategory["PipelineAgentHwaccelChanged"] = "pipeline.agent-hwaccel-changed";
539
530
  EventCategory["MotionAnalysis"] = "detection.motion-analysis";
540
531
  /** All raw motion zones from CCL before minArea filter — for UI debug overlay. */
541
532
  EventCategory["MotionZonesRaw"] = "detection.motion-zones-raw";
@@ -5406,7 +5397,7 @@ var ZodIssueCode = {
5406
5397
  var ZodFirstPartyTypeKind;
5407
5398
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5408
5399
  //#endregion
5409
- //#region ../types/dist/sleep-DAJ9Er57.mjs
5400
+ //#region ../types/dist/sleep-BNeXenvr.mjs
5410
5401
  /**
5411
5402
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5412
5403
  * window to float samples (D455).
@@ -6772,32 +6763,12 @@ object({
6772
6763
  audioCodec: string().optional(),
6773
6764
  metadata: record(string(), unknown()).readonly().optional()
6774
6765
  });
6775
- var EncodedPacketSchema = object({
6776
- type: _enum(["video", "audio"]),
6777
- data: _instanceof(Uint8Array),
6778
- pts: number(),
6779
- dts: number(),
6780
- keyframe: boolean(),
6781
- codec: string()
6782
- });
6783
- var DecodedFrameSchema = object({
6784
- data: _instanceof(Uint8Array),
6785
- width: number(),
6786
- height: number(),
6787
- format: _enum([
6788
- "jpeg",
6789
- "rgb",
6790
- "bgr",
6791
- "yuv420",
6792
- "gray"
6793
- ]),
6794
- timestamp: number()
6795
- });
6796
6766
  /**
6797
6767
  * Wire schema for `FrameHandle` — a zero-pixel, serialisable reference to a
6798
6768
  * frame in a shared-memory ring (Phase 5 / D9). Mirrors the `FrameHandle` TS
6799
- * interface in `interfaces/frame-handle.ts` field-for-field so a decoder cap
6800
- * method (`pullHandles`) can carry handles over tRPC / Moleculer.
6769
+ * interface in `interfaces/frame-handle.ts` field-for-field so a cap method
6770
+ * (the runner's native-crop and pipeline-executor inputs) can carry handles
6771
+ * over tRPC / Moleculer.
6801
6772
  *
6802
6773
  * The `satisfies` assertion below pins this schema to the interface: if a
6803
6774
  * field is added to / removed from `FrameHandle` without a matching schema
@@ -6823,50 +6794,6 @@ var FrameHandleSchema = object({
6823
6794
  slotCount: number().int().positive()
6824
6795
  });
6825
6796
  /**
6826
- * Pixel format a frame-handle subscriber can request from the broker
6827
- * (Phase 5 / D9). The packed, raster subset of `FrameFormat` — the formats a
6828
- * `frameSink: 'shm'` decoder session can write into a `FrameRing` slot.
6829
- * `jpeg` is excluded: a variable-length stream is not a fixed-stride ring
6830
- * payload, and no decoded-frame consumer requests it over the shm plane.
6831
- */
6832
- var FrameHandleFormatSchema = _enum([
6833
- "rgb",
6834
- "bgr",
6835
- "yuv420",
6836
- "gray"
6837
- ]);
6838
- /**
6839
- * Input for `stream-broker.subscribeFrames` (Phase 5 / D9). A consumer asks the
6840
- * broker for a `FrameHandle` stream of one `format`; the broker maintains one
6841
- * shared-memory ring per `(brokerId, format)` actually requested, so a `gray`
6842
- * subscriber (motion) and an `rgb` subscriber (detection) each read the format
6843
- * they asked for with no broker-side conversion.
6844
- */
6845
- var SubscribeFramesInputSchema = object({
6846
- brokerId: string(),
6847
- format: FrameHandleFormatSchema,
6848
- /**
6849
- * Optional reader-side cadence hint in frames per second. The broker does
6850
- * NOT throttle — latest-wins ring reads drop frames implicitly for a slow
6851
- * consumer. The value is echoed back in `SubscribeFramesResult.maxFps` so
6852
- * the consumer can pace its own `pullFrameHandles` polling.
6853
- */
6854
- maxFps: number().positive().optional(),
6855
- /** Short caller-identity tag (`motion`, `detection`, …) for diagnostics. */
6856
- tag: string().optional()
6857
- });
6858
- /**
6859
- * Result of `stream-broker.subscribeFrames`. The consumer then polls
6860
- * `pullFrameHandles({ subscriptionId, maxCount })` and feeds each returned
6861
- * `FrameHandle` to a `FrameRingReader`.
6862
- */
6863
- var SubscribeFramesResultSchema = object({
6864
- /** Opaque id the consumer passes to `pullFrameHandles` / `unsubscribeFrames`. */
6865
- subscriptionId: string(),
6866
- /** Reader-side cadence hint (frames/s) — echoes `SubscribeFramesInput.maxFps`. */
6867
- maxFps: number().nonnegative()
6868
- });
6869
- /**
6870
6797
  * Wire schema for a decoded audio chunk (Phase 5 / D9). Mirrors the
6871
6798
  * `DecodedAudioChunk` TS interface in `interfaces/stream-broker.ts`: PCM
6872
6799
  * sample bytes plus the track parameters a consumer needs to interpret them.
@@ -6874,8 +6801,8 @@ var SubscribeFramesResultSchema = object({
6874
6801
  * Audio chunks are tiny (a ~500ms AudioCodecSession window is a few KB of
6875
6802
  * Float32 PCM), so unlike video frames they ship their bytes INLINE over
6876
6803
  * tRPC / Moleculer — no shared-memory plane. `data` is typed `Uint8Array`
6877
- * (the wire-serialisable supertype of `Buffer`) to match `DecodedFrameSchema`
6878
- * / `EncodedPacketSchema`'s precedent; a `Buffer` is assignable to it.
6804
+ * (the wire-serialisable supertype of `Buffer`); a `Buffer` is assignable to
6805
+ * it.
6879
6806
  */
6880
6807
  var AudioChunkFormatSchema = _enum(AUDIO_CHUNK_FORMATS);
6881
6808
  var DecodedAudioChunkSchema = object({
@@ -6893,11 +6820,10 @@ var DecodedAudioChunkSchema = object({
6893
6820
  format: AudioChunkFormatSchema.optional()
6894
6821
  });
6895
6822
  /**
6896
- * Input for `stream-broker.subscribeAudioChunks` (Phase 5 / D9). The
6897
- * handle-free, ship-bytes-inline analogue of `subscribeFrames`: a consumer
6898
- * asks the broker for the decoded audio-chunk stream of one broker; the
6899
- * broker keeps a per-subscription bounded FIFO queue the consumer drains via
6900
- * `pullAudioChunks`.
6823
+ * Input for `stream-broker.subscribeAudioChunks` (Phase 5 / D9). Audio ships
6824
+ * its bytes inline: a consumer asks the broker for the decoded audio-chunk
6825
+ * stream of one broker; the broker keeps a per-subscription bounded FIFO queue
6826
+ * the consumer drains via `pullAudioChunks`.
6901
6827
  */
6902
6828
  var SubscribeAudioChunksInputSchema = object({
6903
6829
  brokerId: string(),
@@ -6928,9 +6854,7 @@ var BrokerStatsSchema = object({
6928
6854
  "stopped"
6929
6855
  ]),
6930
6856
  inputFps: number(),
6931
- decodeFps: number(),
6932
6857
  encodedSubscribers: number(),
6933
- decodedSubscribers: number(),
6934
6858
  uptimeMs: number(),
6935
6859
  bitrateKbps: number(),
6936
6860
  idrIntervalMs: number(),
@@ -6943,14 +6867,6 @@ var BrokerStatsSchema = object({
6943
6867
  preBufferMs: number(),
6944
6868
  preBufferPackets: number(),
6945
6869
  /**
6946
- * Moleculer node id of the decoder provider currently servicing this
6947
- * stream's decoded subscribers. `null` until the deferred decoder is
6948
- * created (no decoded clients yet, or codec not detected). Surfaces the
6949
- * runtime decoder placement so the UI can show "Decoder: <agent>" without
6950
- * a separate cap call per broker.
6951
- */
6952
- decoderNodeId: string().nullable(),
6953
- /**
6954
6870
  * Detected audio track parameters from the RTSP DESCRIBE / SDP. `null`
6955
6871
  * when the stream has no audio track or the broker is in cold start.
6956
6872
  * `supported = false` means the codec was detected but the local
@@ -10473,70 +10389,6 @@ var StorageLocationDeclarationSchema = object({
10473
10389
  "backup"
10474
10390
  ]).optional()
10475
10391
  });
10476
- var DecoderStatsSchema = object({
10477
- inputFps: number(),
10478
- outputFps: number(),
10479
- avgDecodeTimeMs: number(),
10480
- droppedFrames: number(),
10481
- /**
10482
- * Pull-mode adaptive-fps telemetry (optional — only pull sessions run the
10483
- * lag-driven controller; push sessions omit these). `lagMs` is the EWMA of
10484
- * the decoder's real-time drift (rising = falling behind live); `adaptiveFps`
10485
- * is the current lag-throttled emit rate (≤ `effectiveFps` ceiling).
10486
- */
10487
- lagMs: number().optional(),
10488
- effectiveFps: number().optional(),
10489
- adaptiveFps: number().optional()
10490
- });
10491
- var DecoderSessionConfigSchema = object({
10492
- codec: string(),
10493
- maxFps: number().default(0),
10494
- outputFormat: _enum([
10495
- "jpeg",
10496
- "rgb",
10497
- "bgr",
10498
- "yuv420",
10499
- "gray"
10500
- ]).default("jpeg"),
10501
- scale: number().default(1),
10502
- width: number().optional(),
10503
- height: number().optional(),
10504
- /**
10505
- * Identifier of the camera this decoder session serves. Optional
10506
- * because the cap is generic (any caller could request decode), but
10507
- * stream-broker passes it so decoder logs include `deviceId` for
10508
- * per-camera filtering when diagnosing failures (e.g. node-av
10509
- * sendPacket errors on a single hung camera).
10510
- */
10511
- deviceId: number().int().nonnegative().optional(),
10512
- /**
10513
- * Free-form tag for log scoping. Stream-broker uses
10514
- * `broker:<deviceId>/<profile>`. Decoder session logger surfaces it
10515
- * on every line so `grep tag=broker:5/high` filters one camera
10516
- * profile cleanly.
10517
- */
10518
- tag: string().optional(),
10519
- /**
10520
- * Where the session delivers decoded frames (Phase 5 / D9):
10521
- *
10522
- * - `'callback'` (default) — the legacy pixel path: decoded frames are
10523
- * buffered as `DecodedFrame`s and drained via `pullFrames`.
10524
- * - `'shm'` — the shared-memory frame plane: decoded frames are written
10525
- * into an OS shared-memory ring and drained as zero-pixel
10526
- * `FrameHandle`s via `pullHandles`. A session is one mode or the
10527
- * other — `pullFrames` returns nothing for an `'shm'` session and
10528
- * `pullHandles` returns nothing for a `'callback'` session.
10529
- */
10530
- frameSink: _enum(["callback", "shm"]).default("callback"),
10531
- /**
10532
- * Per-camera decoder DEBUG facility. When `true`, a pull-mode session emits
10533
- * a throttled (~1Hz) structured `decoder debug` line (effective/adaptive fps,
10534
- * real-time lag, dropped-frame delta, avg decode time, hwaccel). Mirrors the
10535
- * stream-broker's `streamingDebug` gate — off by default so production logs
10536
- * stay quiet and the emit path pays zero per-frame cost when disabled.
10537
- */
10538
- debug: boolean().optional()
10539
- });
10540
10392
  /**
10541
10393
  * Per-component log CHANNELS — the gate a hot path consults, and the registry
10542
10394
  * an addon declares its channels in.
@@ -12797,13 +12649,6 @@ var BrokerRtspClientSchema = object({
12797
12649
  lastRtpAt: number(),
12798
12650
  bytesSent: number()
12799
12651
  });
12800
- var BrokerDecodedClientSchema = object({
12801
- tag: string(),
12802
- subscribedAt: number(),
12803
- maxFps: number(),
12804
- framesDelivered: number(),
12805
- framesDropped: number()
12806
- });
12807
12652
  var BrokerAudioClientSchema = object({
12808
12653
  tag: string(),
12809
12654
  subscribedAt: number(),
@@ -12895,7 +12740,6 @@ var BrokerEncodedClientSchema = object({
12895
12740
  });
12896
12741
  var BrokerClientsSchema = object({
12897
12742
  rtsp: array(BrokerRtspClientSchema).readonly(),
12898
- decoded: array(BrokerDecodedClientSchema).readonly(),
12899
12743
  audio: array(BrokerAudioClientSchema).readonly(),
12900
12744
  encoded: array(BrokerEncodedClientSchema).readonly(),
12901
12745
  pipeClients: number(),
@@ -13264,11 +13108,7 @@ method(object({
13264
13108
  auth: "admin"
13265
13109
  }), method(object({ brokerId: string() }), BrokerClientsSchema), method(object({
13266
13110
  brokerId: string(),
13267
- channel: _enum([
13268
- "rtsp",
13269
- "decoded",
13270
- "audio"
13271
- ]),
13111
+ channel: _enum(["rtsp", "audio"]),
13272
13112
  handle: string()
13273
13113
  }), object({ killed: boolean() }), {
13274
13114
  kind: "mutation",
@@ -13303,10 +13143,7 @@ method(object({
13303
13143
  }), method(SubscribeAudioChunksInputSchema, SubscribeAudioChunksResultSchema, { kind: "mutation" }), method(object({
13304
13144
  subscriptionId: string(),
13305
13145
  maxCount: number().int().positive().default(8)
13306
- }), array(DecodedAudioChunkSchema).readonly()), method(object({ subscriptionId: string() }), object({ released: boolean() }), { kind: "mutation" }), method(SubscribeFramesInputSchema, SubscribeFramesResultSchema, { kind: "mutation" }), method(object({
13307
- subscriptionId: string(),
13308
- maxCount: number().int().positive().default(4)
13309
- }), array(FrameHandleSchema).readonly()), method(object({ subscriptionId: string() }), object({ released: boolean() }), { kind: "mutation" }), method(object({
13146
+ }), array(DecodedAudioChunkSchema).readonly()), method(object({ subscriptionId: string() }), object({ released: boolean() }), { kind: "mutation" }), method(object({
13310
13147
  brokerId: string(),
13311
13148
  seconds: number().min(0).max(30)
13312
13149
  }), _void(), {
@@ -14463,54 +14300,12 @@ id: union([string(), number()]) }), {
14463
14300
  kind: "mutation",
14464
14301
  auth: "admin"
14465
14302
  });
14466
- /**
14467
- * shm ring usage stats for a `frameSink: 'shm'` decoder session —
14468
- * exposed via `decoder.getShmStats` so downstream consumers can
14469
- * observe ring pressure (slot count, byte budget, hit/miss ratio).
14470
- */
14471
- var ShmRingStatsSchema = object({
14472
- sessionId: string(),
14473
- slotCount: number().int(),
14474
- slotByteLength: number().int(),
14475
- segmentBytes: number().int(),
14476
- budgetMb: number().int(),
14477
- framesWritten: number().int(),
14478
- getFrameHits: number().int(),
14479
- getFrameMisses: number().int()
14480
- });
14481
- method(object({ codec: string() }), boolean()), method(_void(), object({
14303
+ method(_void(), object({
14482
14304
  id: string(),
14483
14305
  name: string(),
14484
- isPullMode: boolean().optional(),
14485
- priority: number().optional(),
14486
14306
  hwaccel: string().optional(),
14487
14307
  probedBestHwaccel: string().optional()
14488
- })), method(DecoderSessionConfigSchema, object({
14489
- sessionId: string(),
14490
- nodeId: string()
14491
- })), method(object({ sessionId: string() }), _void()), method(object({
14492
- sessionId: string(),
14493
- packet: EncodedPacketSchema
14494
- }), _void()), method(object({
14495
- sessionId: string(),
14496
- url: string()
14497
- }), _void()), method(object({
14498
- sessionId: string(),
14499
- maxCount: number().default(1),
14500
- waitMs: number().optional()
14501
- }), array(DecodedFrameSchema)), method(object({
14502
- sessionId: string(),
14503
- maxCount: number().default(1),
14504
- waitMs: number().optional()
14505
- }), array(FrameHandleSchema)), method(object({ handle: FrameHandleSchema }), DecodedFrameSchema.nullable()), method(object({ sessionId: string() }), ShmRingStatsSchema.nullable()), method(object({
14506
- sessionId: string(),
14507
- config: DecoderSessionConfigSchema.partial()
14508
- }), _void()), method(object({ sessionId: string() }), DecoderStatsSchema), method(_void(), array(object({
14509
- sessionId: string(),
14510
- codec: string(),
14511
- outputFormat: string(),
14512
- createdAtMs: number()
14513
- })).readonly()), method(_void(), object({ backend: string() }), {
14308
+ })), method(_void(), object({ backend: string() }), {
14514
14309
  kind: "mutation",
14515
14310
  auth: "admin"
14516
14311
  });
@@ -21571,8 +21366,8 @@ Object.freeze(Object.fromEntries([{
21571
21366
  var CLUSTER_STEP_SETTING_FIELDS = [{
21572
21367
  stepId: "face-embedding",
21573
21368
  key: "minLandmarkFaceSize",
21574
- label: "Min face size for recognition (detection px)",
21575
- description: "Refuse to embed a face smaller than this IN THE DETECTION FRAME. Applies to every node — the enrolled gallery is one index, and two admission floors would fill it from two policies. 0 disables the gate.",
21369
+ label: "Min face size to embed (detection-frame px)",
21370
+ description: "Faces smaller than this in the small frame the detector sees are not embedded, so they can never be recognised. Below it the face cannot be aligned reliably and a bad embedding matches everyone. Same value on every node. 0 = no limit.",
21576
21371
  type: "slider",
21577
21372
  min: 0,
21578
21373
  max: 64,
@@ -23624,21 +23419,6 @@ var PipelineDefaultStepSchema = lazy(() => object({
23624
23419
  group: string().optional(),
23625
23420
  settings: record(string(), unknown()).optional()
23626
23421
  }));
23627
- var PipelineTemplateStepSchema = lazy(() => object({
23628
- addonId: string(),
23629
- enabled: boolean(),
23630
- modelId: string(),
23631
- children: array(PipelineTemplateStepSchema).readonly(),
23632
- settings: record(string(), unknown()).optional()
23633
- }));
23634
- var PipelineTemplateSchema$1 = object({
23635
- id: string(),
23636
- name: string(),
23637
- createdAt: string(),
23638
- updatedAt: string(),
23639
- engine: PipelineEngineChoiceSchema,
23640
- steps: array(PipelineTemplateStepSchema).readonly()
23641
- });
23642
23422
  var PipelineModelOptionSchema = object({
23643
23423
  id: string(),
23644
23424
  name: string(),
@@ -23850,15 +23630,7 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
23850
23630
  }), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({
23851
23631
  steps: array(PipelineStepInputSchema),
23852
23632
  deviceKey: string().optional()
23853
- }), PipelineValidationResultSchema), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
23854
- name: string(),
23855
- steps: array(PipelineTemplateStepSchema).readonly(),
23856
- engine: PipelineEngineChoiceSchema
23857
- }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({
23858
- id: string(),
23859
- name: string().optional(),
23860
- steps: array(PipelineTemplateStepSchema).readonly().optional()
23861
- }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({ id: string() }), _void(), { kind: "mutation" }), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
23633
+ }), PipelineValidationResultSchema), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
23862
23634
  addonId: string(),
23863
23635
  modelId: string(),
23864
23636
  format: ModelFormatSchema$2
@@ -25268,14 +25040,15 @@ var RunnerLocalLoadSchema = object({
25268
25040
  * Per-inference-device live load (multi-device C4). One entry per deviceKey
25269
25041
  * this runner currently has attached cameras on, so the orchestrator's second
25270
25042
  * `balance()` pass (over a node's devices) weights on real per-pool session
25271
- * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
25272
- * per device is 0 until the pool backlog gets a public accessor (follow-up).
25043
+ * counts. Empty on single-device / pre-multi-device runners. There is no
25044
+ * per-device queue depth: the inference pool's backlog has no accessor, and
25045
+ * a field that always read 0 was removed rather than shown as a measurement
25046
+ * (D762).
25273
25047
  */
25274
25048
  devices: array(object({
25275
25049
  deviceKey: string(),
25276
25050
  backend: string(),
25277
- attachedCameras: number(),
25278
- queueDepthTotal: number()
25051
+ attachedCameras: number()
25279
25052
  })).default([]),
25280
25053
  /** Hardware capability flags reported by this node. */
25281
25054
  hardware: object({
@@ -25489,14 +25262,6 @@ var CameraPipelineConfigSchema = object({
25489
25262
  settings: record(string(), unknown()).readonly().optional()
25490
25263
  }).nullable().optional()
25491
25264
  });
25492
- var PipelineTemplateSchema = object({
25493
- id: string(),
25494
- name: string(),
25495
- description: string().optional(),
25496
- config: CameraPipelineConfigSchema,
25497
- createdAt: string(),
25498
- updatedAt: string()
25499
- });
25500
25265
  var DeviceStepConfigSchema = object({
25501
25266
  modelId: string().optional(),
25502
25267
  settings: record(string(), unknown()).optional(),
@@ -25606,7 +25371,10 @@ var PipelineAssignmentSchema = object({
25606
25371
  });
25607
25372
  /**
25608
25373
  * Per-agent load summary surfaced to the load balancer + dashboards.
25609
- * Aggregated from each runner's `getLocalLoad` cap call.
25374
+ * Aggregated from each runner's `getLocalLoad` cap call. In the
25375
+ * `getAgentLoad` report, a single-device node's default-pool sessions (no
25376
+ * `deviceKey`) are credited to its one eligible device in `load.devices` —
25377
+ * the default pool IS that device (D745); the balancer's census stays raw.
25610
25378
  */
25611
25379
  var AgentLoadSummarySchema = object({
25612
25380
  nodeId: string(),
@@ -25695,26 +25463,27 @@ var CameraBrokerStatusSchema = object({
25695
25463
  width: number(),
25696
25464
  height: number(),
25697
25465
  subscribers: number(),
25698
- inFps: number(),
25699
- outFps: number()
25466
+ /**
25467
+ * Encoded packets/s the broker receives from the camera. null when the
25468
+ * broker's stats read failed — unknown, never 0 (a 0 here reads as a stall).
25469
+ * There is no output rate: decoding runs in the session decode workers, not
25470
+ * in the broker, so the broker's own decoded-frame rate is 0 on every live
25471
+ * camera and is not served (D758).
25472
+ */
25473
+ inFps: number().nullable()
25700
25474
  })).readonly(),
25701
25475
  webrtcSessions: number(),
25702
25476
  rtspRestream: boolean()
25703
25477
  });
25704
- /** Shared-memory ring statistics within the decoder block. */
25705
- var CameraDecoderShmSchema = object({
25706
- framesWritten: number(),
25707
- getFrameHits: number(),
25708
- getFrameMisses: number(),
25709
- budgetMb: number()
25710
- });
25711
- /** Decoder block — null when the decoder stage is unreachable or inactive. */
25712
- var CameraDecoderStatusSchema = object({
25713
- nodeId: string(),
25714
- formats: array(string()).readonly(),
25715
- sessionCount: number(),
25716
- shm: CameraDecoderShmSchema
25717
- });
25478
+ /**
25479
+ * Decoder block — WHERE decode runs (co-located with the detection node by
25480
+ * construction); null when the camera has no detection node. It carries no
25481
+ * frame counter: the shared-memory plane whose counters it used to serve is
25482
+ * gone, and those fields read 0 forever. Whether frames actually flow is read
25483
+ * from `detection.actualFps` (frames delivered to and processed by the
25484
+ * detector), never from a counter nothing feeds.
25485
+ */
25486
+ var CameraDecoderStatusSchema = object({ nodeId: string() });
25718
25487
  /** Motion block — null when motion detection is not active for this device. */
25719
25488
  var CameraMotionStatusSchema = object({
25720
25489
  enabled: boolean(),
@@ -26167,25 +25936,7 @@ method(object({
26167
25936
  }), method(_void(), DiskReconcileJobSchema, {
26168
25937
  kind: "mutation",
26169
25938
  auth: "admin"
26170
- }), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
26171
- name: string(),
26172
- description: string().optional(),
26173
- config: CameraPipelineConfigSchema
26174
- }), PipelineTemplateSchema, {
26175
- kind: "mutation",
26176
- auth: "admin"
26177
- }), method(object({
26178
- id: string(),
26179
- name: string().optional(),
26180
- description: string().optional(),
26181
- config: CameraPipelineConfigSchema.optional()
26182
- }), PipelineTemplateSchema, {
26183
- kind: "mutation",
26184
- auth: "admin"
26185
- }), method(object({ id: string() }), object({ success: literal(true) }), {
26186
- kind: "mutation",
26187
- auth: "admin"
26188
- });
25939
+ }), method(_void(), DiskReconcileJobSchema, { auth: "admin" });
26189
25940
  /**
26190
25941
  * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
26191
25942
  * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
@@ -28405,8 +28156,6 @@ var WebrtcStreamChoiceSchema = object({
28405
28156
  ]).nullable(),
28406
28157
  /** Input FPS from the camera (null for adaptive). */
28407
28158
  inputFps: number().nullable(),
28408
- /** Decoded FPS (null for adaptive). */
28409
- decodeFps: number().nullable(),
28410
28159
  /** Bitrate in kbps (null for adaptive). */
28411
28160
  bitrateKbps: number().nullable()
28412
28161
  });
@@ -28985,6 +28734,59 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
28985
28734
  var RestartAddonResultSchema = unknown();
28986
28735
  var InstallPackageResultSchema = unknown();
28987
28736
  var ReloadPackagesResultSchema = unknown();
28737
+ /**
28738
+ * What an uninstall did on ONE node.
28739
+ * - `uninstalled` — removed: runner(s) stopped, caps gone, code deleted.
28740
+ * - `not-installed` — the node did not have it (nothing to do).
28741
+ * - `refused` — protected / bootstrap package; nothing touched.
28742
+ * - `failed` — attempted and did not complete; `error` says why.
28743
+ * - `pending-reconnect` — the node is OFFLINE: its record was pruned so the
28744
+ * back-fill never returns the package, and the stale-addon sweep removes the
28745
+ * copy when the node registers again (only once the hub no longer has it).
28746
+ */
28747
+ var UninstallNodeOutcomeSchema = _enum([
28748
+ "uninstalled",
28749
+ "not-installed",
28750
+ "refused",
28751
+ "failed",
28752
+ "pending-reconnect"
28753
+ ]);
28754
+ var UninstallNodeResultSchema = object({
28755
+ nodeId: string(),
28756
+ outcome: UninstallNodeOutcomeSchema,
28757
+ error: string().optional(),
28758
+ addonIds: array(string()).readonly(),
28759
+ runnersStopped: array(string()).readonly(),
28760
+ warnings: array(string()).readonly()
28761
+ });
28762
+ var UninstallPackageResultSchema = object({
28763
+ /** Every targeted node ended `uninstalled`, `not-installed` or `pending-reconnect`. */
28764
+ success: boolean(),
28765
+ packageName: string(),
28766
+ results: array(UninstallNodeResultSchema).readonly()
28767
+ });
28768
+ /**
28769
+ * `unreachable` lists online agents whose status read FAILED — absence from
28770
+ * `installations` is only "not installed" for a node that answered (D315).
28771
+ */
28772
+ var PackageInstallationsSchema = object({
28773
+ installations: array(object({
28774
+ packageName: string(),
28775
+ nodeId: string(),
28776
+ nodeName: string(),
28777
+ isHub: boolean(),
28778
+ isOnline: boolean(),
28779
+ version: string().nullable(),
28780
+ addonIds: array(string()).readonly(),
28781
+ source: _enum(["live", "recorded"]),
28782
+ /** The hub installs this package too. `false` on an agent row = an orphan. */
28783
+ onHub: boolean()
28784
+ })).readonly(),
28785
+ unreachable: array(object({
28786
+ nodeId: string(),
28787
+ error: string()
28788
+ })).readonly()
28789
+ });
28988
28790
  var BulkUpdateItemStatusSchema = _enum([
28989
28791
  "queued",
28990
28792
  "updating",
@@ -29067,10 +28869,14 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
29067
28869
  }), method(object({ packageName: string() }), InstallPackageResultSchema, {
29068
28870
  kind: "mutation",
29069
28871
  auth: "admin"
29070
- }), method(_void(), boolean()), method(_void(), array(unknown()).readonly()), method(object({ packageName: string() }), unknown(), {
28872
+ }), method(_void(), boolean()), method(_void(), array(unknown()).readonly()), method(object({
28873
+ packageName: string().min(1),
28874
+ nodeId: string().optional(),
28875
+ everywhere: boolean().optional()
28876
+ }), UninstallPackageResultSchema, {
29071
28877
  kind: "mutation",
29072
28878
  auth: "admin"
29073
- }), method(_void(), ReloadPackagesResultSchema, {
28879
+ }), method(object({ packageName: string().optional() }), PackageInstallationsSchema, { auth: "admin" }), method(_void(), ReloadPackagesResultSchema, {
29074
28880
  kind: "mutation",
29075
28881
  auth: "admin"
29076
28882
  }), method(object({ query: string().optional() }), array(SearchResultSchema)), method(object({ nodeId: string().optional() }), array(PackageUpdateSchema).readonly(), { auth: "admin" }), method(object({
@@ -37359,6 +37165,12 @@ Object.freeze({
37359
37165
  addonId: null,
37360
37166
  access: "view"
37361
37167
  },
37168
+ "addons.listInstallations": {
37169
+ capName: "addons",
37170
+ capScope: "system",
37171
+ addonId: null,
37172
+ access: "view"
37173
+ },
37362
37174
  "addons.listJobs": {
37363
37175
  capName: "addons",
37364
37176
  capScope: "system",
@@ -38541,90 +38353,18 @@ Object.freeze({
38541
38353
  addonId: null,
38542
38354
  access: "create"
38543
38355
  },
38544
- "decoder.createSession": {
38545
- capName: "decoder",
38546
- capScope: "system",
38547
- addonId: null,
38548
- access: "view"
38549
- },
38550
- "decoder.destroySession": {
38551
- capName: "decoder",
38552
- capScope: "system",
38553
- addonId: null,
38554
- access: "delete"
38555
- },
38556
- "decoder.getFrame": {
38557
- capName: "decoder",
38558
- capScope: "system",
38559
- addonId: null,
38560
- access: "view"
38561
- },
38562
38356
  "decoder.getInfo": {
38563
38357
  capName: "decoder",
38564
38358
  capScope: "system",
38565
38359
  addonId: null,
38566
38360
  access: "view"
38567
38361
  },
38568
- "decoder.getShmStats": {
38569
- capName: "decoder",
38570
- capScope: "system",
38571
- addonId: null,
38572
- access: "view"
38573
- },
38574
- "decoder.getStats": {
38575
- capName: "decoder",
38576
- capScope: "system",
38577
- addonId: null,
38578
- access: "view"
38579
- },
38580
- "decoder.listActiveSessions": {
38581
- capName: "decoder",
38582
- capScope: "system",
38583
- addonId: null,
38584
- access: "view"
38585
- },
38586
- "decoder.openStream": {
38587
- capName: "decoder",
38588
- capScope: "system",
38589
- addonId: null,
38590
- access: "view"
38591
- },
38592
- "decoder.pullFrames": {
38593
- capName: "decoder",
38594
- capScope: "system",
38595
- addonId: null,
38596
- access: "view"
38597
- },
38598
- "decoder.pullHandles": {
38599
- capName: "decoder",
38600
- capScope: "system",
38601
- addonId: null,
38602
- access: "view"
38603
- },
38604
- "decoder.pushPacket": {
38605
- capName: "decoder",
38606
- capScope: "system",
38607
- addonId: null,
38608
- access: "view"
38609
- },
38610
38362
  "decoder.reprobeHwaccel": {
38611
38363
  capName: "decoder",
38612
38364
  capScope: "system",
38613
38365
  addonId: null,
38614
38366
  access: "create"
38615
38367
  },
38616
- "decoder.supportsCodec": {
38617
- capName: "decoder",
38618
- capScope: "system",
38619
- addonId: null,
38620
- access: "view"
38621
- },
38622
- "decoder.updateConfig": {
38623
- capName: "decoder",
38624
- capScope: "system",
38625
- addonId: null,
38626
- access: "view"
38627
- },
38628
38368
  "detectionPipeline.applyDeviceSettingsPatch": {
38629
38369
  capName: "detection-pipeline",
38630
38370
  capScope: "device",
@@ -41715,12 +41455,6 @@ Object.freeze({
41715
41455
  addonId: null,
41716
41456
  access: "delete"
41717
41457
  },
41718
- "pipelineExecutor.deleteTemplate": {
41719
- capName: "pipeline-executor",
41720
- capScope: "system",
41721
- addonId: null,
41722
- access: "delete"
41723
- },
41724
41458
  "pipelineExecutor.downloadModel": {
41725
41459
  capName: "pipeline-executor",
41726
41460
  capScope: "system",
@@ -41859,12 +41593,6 @@ Object.freeze({
41859
41593
  addonId: null,
41860
41594
  access: "view"
41861
41595
  },
41862
- "pipelineExecutor.listTemplates": {
41863
- capName: "pipeline-executor",
41864
- capScope: "system",
41865
- addonId: null,
41866
- access: "view"
41867
- },
41868
41596
  "pipelineExecutor.rearmInferenceDevice": {
41869
41597
  capName: "pipeline-executor",
41870
41598
  capScope: "system",
@@ -41889,12 +41617,6 @@ Object.freeze({
41889
41617
  addonId: null,
41890
41618
  access: "create"
41891
41619
  },
41892
- "pipelineExecutor.saveTemplate": {
41893
- capName: "pipeline-executor",
41894
- capScope: "system",
41895
- addonId: null,
41896
- access: "create"
41897
- },
41898
41620
  "pipelineExecutor.setVideoPipelineSteps": {
41899
41621
  capName: "pipeline-executor",
41900
41622
  capScope: "system",
@@ -41913,12 +41635,6 @@ Object.freeze({
41913
41635
  addonId: null,
41914
41636
  access: "create"
41915
41637
  },
41916
- "pipelineExecutor.updateTemplate": {
41917
- capName: "pipeline-executor",
41918
- capScope: "system",
41919
- addonId: null,
41920
- access: "create"
41921
- },
41922
41638
  "pipelineExecutor.validatePipeline": {
41923
41639
  capName: "pipeline-executor",
41924
41640
  capScope: "system",
@@ -41949,12 +41665,6 @@ Object.freeze({
41949
41665
  addonId: null,
41950
41666
  access: "create"
41951
41667
  },
41952
- "pipelineOrchestrator.deleteTemplate": {
41953
- capName: "pipeline-orchestrator",
41954
- capScope: "system",
41955
- addonId: null,
41956
- access: "delete"
41957
- },
41958
41668
  "pipelineOrchestrator.getAgentLoad": {
41959
41669
  capName: "pipeline-orchestrator",
41960
41670
  capScope: "system",
@@ -42105,12 +41815,6 @@ Object.freeze({
42105
41815
  addonId: null,
42106
41816
  access: "view"
42107
41817
  },
42108
- "pipelineOrchestrator.listTemplates": {
42109
- capName: "pipeline-orchestrator",
42110
- capScope: "system",
42111
- addonId: null,
42112
- access: "view"
42113
- },
42114
41818
  "pipelineOrchestrator.pauseForStorageMigration": {
42115
41819
  capName: "pipeline-orchestrator",
42116
41820
  capScope: "system",
@@ -42165,12 +41869,6 @@ Object.freeze({
42165
41869
  addonId: null,
42166
41870
  access: "create"
42167
41871
  },
42168
- "pipelineOrchestrator.saveTemplate": {
42169
- capName: "pipeline-orchestrator",
42170
- capScope: "system",
42171
- addonId: null,
42172
- access: "create"
42173
- },
42174
41872
  "pipelineOrchestrator.setAgentCapabilities": {
42175
41873
  capName: "pipeline-orchestrator",
42176
41874
  capScope: "system",
@@ -42279,12 +41977,6 @@ Object.freeze({
42279
41977
  addonId: null,
42280
41978
  access: "create"
42281
41979
  },
42282
- "pipelineOrchestrator.updateTemplate": {
42283
- capName: "pipeline-orchestrator",
42284
- capScope: "system",
42285
- addonId: null,
42286
- access: "create"
42287
- },
42288
41980
  "pipelineRunner.attachCamera": {
42289
41981
  capName: "pipeline-runner",
42290
41982
  capScope: "system",
@@ -43719,12 +43411,6 @@ Object.freeze({
43719
43411
  addonId: null,
43720
43412
  access: "view"
43721
43413
  },
43722
- "streamBroker.pullFrameHandles": {
43723
- capName: "stream-broker",
43724
- capScope: "system",
43725
- addonId: null,
43726
- access: "view"
43727
- },
43728
43414
  "streamBroker.regenerateRtspToken": {
43729
43415
  capName: "stream-broker",
43730
43416
  capScope: "system",
@@ -43791,12 +43477,6 @@ Object.freeze({
43791
43477
  addonId: null,
43792
43478
  access: "create"
43793
43479
  },
43794
- "streamBroker.subscribeFrames": {
43795
- capName: "stream-broker",
43796
- capScope: "system",
43797
- addonId: null,
43798
- access: "create"
43799
- },
43800
43480
  "streamBroker.unassignProfile": {
43801
43481
  capName: "stream-broker",
43802
43482
  capScope: "system",
@@ -43809,12 +43489,6 @@ Object.freeze({
43809
43489
  addonId: null,
43810
43490
  access: "create"
43811
43491
  },
43812
- "streamBroker.unsubscribeFrames": {
43813
- capName: "stream-broker",
43814
- capScope: "system",
43815
- addonId: null,
43816
- access: "create"
43817
- },
43818
43492
  "streamCatalog.getCatalog": {
43819
43493
  capName: "stream-catalog",
43820
43494
  capScope: "device",
@@ -46328,11 +46002,6 @@ Object.freeze({
46328
46002
  form: "single",
46329
46003
  optional: false
46330
46004
  }],
46331
- "decoder.createSession": [{
46332
- name: "deviceId",
46333
- form: "single",
46334
- optional: true
46335
- }],
46336
46005
  "detectionPipeline.applyDeviceSettingsPatch": [{
46337
46006
  name: "deviceId",
46338
46007
  form: "single",
@@ -50473,24 +50142,6 @@ var CAMERA_STREAM_CONTROLLER = {
50473
50142
  audioCodecs: ["AAC"]
50474
50143
  }]
50475
50144
  };
50476
- /**
50477
- * `Alexa.EndpointHealth` — REQUIRED on a sensor endpoint (alexa-endpointhealth;
50478
- * alexa-motionsensor: "Must also implement `Alexa.EndpointHealth`"), and every
50479
- * camera endpoint may carry `Alexa.MotionSensor`. Declared on every camera so
50480
- * Alexa can tell an offline camera from a broken one: `connectivity` comes
50481
- * from the `device-status` cap in ReportState and is pushed as a ChangeReport
50482
- * on `DeviceOnline` / `DeviceOffline` by the Event Gateway.
50483
- */
50484
- var ENDPOINT_HEALTH = {
50485
- type: "AlexaInterface",
50486
- interface: "Alexa.EndpointHealth",
50487
- version: "3",
50488
- properties: {
50489
- supported: [{ name: "connectivity" }],
50490
- proactivelyReported: true,
50491
- retrievable: true
50492
- }
50493
- };
50494
50145
  var DOORBELL_EVENT_SOURCE = {
50495
50146
  type: "AlexaInterface",
50496
50147
  interface: "Alexa.DoorbellEventSource",
@@ -50653,6 +50304,27 @@ var PTZ_TILT_MODE_CONTROLLER = ptzDirectionalModeController("ptz.tilt", "Tilt",
50653
50304
  label: "Down"
50654
50305
  }
50655
50306
  ]);
50307
+ /** Amazon (alexa-modecontroller): "You must include a minimum of two mode
50308
+ * objects." One preset made the camera's whole AddOrUpdateReport fail
50309
+ * schema validation, and Discover dropped the camera. */
50310
+ var MIN_MODE_CONTROLLER_MODES = 2;
50311
+ /**
50312
+ * Preset names as distinct mode values. Two presets with the same name (the
50313
+ * camera allows it: `Stanza`, `Stanza`; `CamStack Home` ×3) produced two
50314
+ * identical modes, and Alexa rejected the capability — "Invalid capability
50315
+ * received." — and with it every endpoint of the camera. First wins,
50316
+ * case-insensitively: the SetMode handler resolves a value to the first
50317
+ * preset of that name, which is the one kept.
50318
+ */
50319
+ function distinctPresetNames(names) {
50320
+ const seen = /* @__PURE__ */ new Set();
50321
+ return names.filter((name) => {
50322
+ const key = name.toLowerCase();
50323
+ if (seen.has(key)) return false;
50324
+ seen.add(key);
50325
+ return true;
50326
+ });
50327
+ }
50656
50328
  function ptzPresetModeController(presetNames) {
50657
50329
  return {
50658
50330
  type: "AlexaInterface",
@@ -50692,13 +50364,13 @@ function buildParentCameraEndpoint(input) {
50692
50364
  const capabilities = [
50693
50365
  ALEXA_BASE_CAPABILITY,
50694
50366
  CAMERA_STREAM_CONTROLLER,
50695
- rtcSessionController(input.hasIntercom),
50696
- ENDPOINT_HEALTH
50367
+ rtcSessionController(input.hasIntercom)
50697
50368
  ];
50698
50369
  if (input.isDoorbell) capabilities.push(DOORBELL_EVENT_SOURCE);
50699
50370
  if (input.hasMotion) capabilities.push(MOTION_SENSOR);
50700
50371
  if (input.hasBattery) capabilities.push(BATTERY_LEVEL_SENSOR);
50701
- if (input.ptzPresetNames.length > 0) capabilities.push(ptzPresetModeController(input.ptzPresetNames));
50372
+ const presetNames = distinctPresetNames(input.ptzPresetNames);
50373
+ if (presetNames.length >= MIN_MODE_CONTROLLER_MODES) capabilities.push(ptzPresetModeController(presetNames));
50702
50374
  if (input.hasPtz) {
50703
50375
  capabilities.push(PTZ_PAN_MODE_CONTROLLER);
50704
50376
  capabilities.push(PTZ_TILT_MODE_CONTROLLER);
@@ -50837,6 +50509,12 @@ function buildAlarmPanelEndpoint(input) {
50837
50509
  };
50838
50510
  }
50839
50511
  //#endregion
50512
+ //#region src/handlers/ptz-preset-recallable.ts
50513
+ var RECALLABLE_FUNCTIONS = new Set(["home"]);
50514
+ function isAlexaRecallablePreset(preset) {
50515
+ return preset.fn === null || RECALLABLE_FUNCTIONS.has(preset.fn);
50516
+ }
50517
+ //#endregion
50840
50518
  //#region src/handlers/discovery.ts
50841
50519
  var discoveryPayloadSchema = DiscoveryDirectiveSchema.shape.payload;
50842
50520
  /** Devices built at once in `handleDiscovery` — latency-bound tRPC reads, so
@@ -51024,7 +50702,7 @@ async function loadBoundDeviceInfo(device, api) {
51024
50702
  };
51025
50703
  }
51026
50704
  async function fetchPtzPresetNames(deviceId, api) {
51027
- return (await api.ptz.getPresets.query({ deviceId })).map((p) => p.name).filter((n) => n.length > 0);
50705
+ return (await api.ptz.getPresets.query({ deviceId })).filter(isAlexaRecallablePreset).map((p) => p.name).filter((n) => n.length > 0);
51028
50706
  }
51029
50707
  function classifyChildKind(deviceType, role) {
51030
50708
  if (role === "floodlight" || role === "spotlight" || role === "nightvision") return "light";
@@ -51424,8 +51102,8 @@ async function handleSetMode(header, endpointRaw, payloadRaw, deps, _callerIdent
51424
51102
  if (!mode.startsWith(PTZ_PRESET_MODE_PREFIX)) return errorEnvelope(header, "INVALID_DIRECTIVE", `mode '${mode}' is not a ptz.preset mode`);
51425
51103
  const presetName = mode.slice(11);
51426
51104
  if (presetName.length === 0) return errorEnvelope(header, "INVALID_DIRECTIVE", "preset name missing after prefix");
51427
- const preset = (await api.ptz.getPresets.query({ deviceId: parsedId.deviceId })).find((p) => p.name === presetName);
51428
- if (!preset) return errorEnvelope(header, "INVALID_DIRECTIVE", `preset '${presetName}' not found on device ${parsedId.deviceId}`);
51105
+ const preset = (await api.ptz.getPresets.query({ deviceId: parsedId.deviceId })).find((p) => p.name === presetName && isAlexaRecallablePreset(p));
51106
+ if (!preset) return errorEnvelope(header, "INVALID_DIRECTIVE", `preset '${presetName}' is not a recallable preset on device ${parsedId.deviceId}`);
51429
51107
  await api.ptz.goToPreset.mutate({
51430
51108
  deviceId: parsedId.deviceId,
51431
51109
  presetId: preset.id
@@ -51517,20 +51195,6 @@ async function collectCameraProperties(api, deviceId, properties, timeOfSample,
51517
51195
  timeOfSample,
51518
51196
  uncertaintyInMilliseconds: 0
51519
51197
  });
51520
- const online = boolField(await api.deviceStatus.getStatus.query({ deviceId }).catch((err) => {
51521
- logger.warn("alexa report-state: deviceStatus.getStatus failed — connectivity omitted", {
51522
- tags: { deviceId },
51523
- meta: { error: err instanceof Error ? err.message : String(err) }
51524
- });
51525
- return null;
51526
- }), "online");
51527
- if (online !== null) properties.push({
51528
- namespace: "Alexa.EndpointHealth",
51529
- name: "connectivity",
51530
- value: { value: online ? "OK" : "UNREACHABLE" },
51531
- timeOfSample,
51532
- uncertaintyInMilliseconds: 0
51533
- });
51534
51198
  const detected = boolField(await api.motion.getStatus.query({ deviceId }).catch((err) => {
51535
51199
  logger.info("alexa report-state: motion.getStatus skipped", { meta: {
51536
51200
  deviceId,
@@ -53487,7 +53151,7 @@ function buildAlexaOauthIntegration(publicHubUrl) {
53487
53151
  * client id and SECRET, on the theory that a per-account refresh token was the
53488
53152
  * real gate. The package is public on npm, so that secret was published with
53489
53153
  * every version and has to be rotated. Since D753 the secret lives only in the
53490
- * skill's Lambda (AWS Secrets Manager); the hub gets short-lived Event Gateway
53154
+ * skill's Lambda (SSM Parameter Store SecureString, D761); the hub gets short-lived Event Gateway
53491
53155
  * tokens from the Lambda's broker (`broker-client.ts`) with a per-link
53492
53156
  * credential. `scripts/check-no-client-secrets.ts` keeps it that way.
53493
53157
  *
@@ -54518,7 +54182,7 @@ var ExportAlexaAddon = class extends BaseAddon {
54518
54182
  content: [
54519
54183
  "Public skill (default): enable the \"CamStack\" skill in the Alexa app and link your account — no Amazon developer setup is needed. A single published skill + hosted Lambda route to this hub using its public HTTPS URL (baked into every JWT).",
54520
54184
  "The skill's Lambda keeps the Amazon credentials: this hub never holds an LWA client secret. It receives short-lived event tokens from the Lambda's token broker with a credential that is unique to this link.",
54521
- "Custom skill: switch \"Alexa skill\" below to Custom, enter your skill id, and deploy the skill Lambda from this addon's lambda-template in your own AWS account (your LWA credentials go into AWS Secrets Manager there, not here).",
54185
+ "Custom skill: switch \"Alexa skill\" below to Custom, enter your skill id, and deploy the skill Lambda from this addon's lambda-template in your own AWS account (your LWA credentials go into SSM Parameter Store there, not here).",
54522
54186
  "Either way this hub must be reachable from the public internet (Cloudflare Tunnel, Tailscale Funnel, …). The \"External address\" picker selects which detected endpoint the Lambda calls back on — press \"Refresh external addresses\" after setting up a new tunnel."
54523
54187
  ].join("\n")
54524
54188
  },
@@ -54585,7 +54249,7 @@ var ExportAlexaAddon = class extends BaseAddon {
54585
54249
  key: "__custom-skill-info",
54586
54250
  label: "Your skill Lambda",
54587
54251
  variant: "info",
54588
- content: "Proactive events (doorbell, motion, battery, device-list updates) are authorised by YOUR skill Lambda: it keeps the \"Send Alexa Events\" LWA client id/secret in AWS Secrets Manager and hands this hub short-lived tokens. Nothing to paste here — see the custom-skill setup guide."
54252
+ content: "Proactive events (doorbell, motion, battery, device-list updates) are authorised by YOUR skill Lambda: it keeps the \"Send Alexa Events\" LWA client id/secret in SSM Parameter Store and hands this hub short-lived tokens. Nothing to paste here — see the custom-skill setup guide."
54589
54253
  }] : [{
54590
54254
  type: "info",
54591
54255
  key: "__public-skill-info",