@camstack/addon-export-alexa 1.2.133 → 1.2.135
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/export-alexa.addon.js +236 -455
- package/dist/export-alexa.addon.mjs +237 -455
- package/lambda-template/README.md +57 -21
- package/lambda-template/aws.js +32 -16
- package/lambda-template/broker.js +3 -3
- package/lambda-template/debug-trace.js +100 -0
- package/lambda-template/index.js +112 -37
- package/lambda-template/secrets.js +24 -21
- package/lambda-template/util.js +54 -1
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as crypto$1 from "node:crypto";
|
|
2
|
-
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
//#region ../types/dist/event-category-B_0XEcLF.mjs
|
|
3
4
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4
5
|
EventCategory["SystemBoot"] = "system.boot";
|
|
5
6
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -500,15 +501,6 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
500
501
|
EventCategory["AgentWsDisconnected"] = "agent.ws.disconnected";
|
|
501
502
|
EventCategory["AgentBackupActivated"] = "agent.backup.activated";
|
|
502
503
|
EventCategory["OrchestrationSettingsUpdated"] = "orchestration.settings-updated";
|
|
503
|
-
/**
|
|
504
|
-
* Per-agent hwaccel preference changed (user override set, cleared, or
|
|
505
|
-
* re-probed). Observability event — decoders pull the current pref at
|
|
506
|
-
* `createSession`, so running sessions keep their current backend until
|
|
507
|
-
* they rotate naturally (camera add/remove, stream restart). Future
|
|
508
|
-
* work can wire a listener in stream-broker that force-rotates live
|
|
509
|
-
* sessions; for now this event powers logs + admin-UI toast feedback.
|
|
510
|
-
*/
|
|
511
|
-
EventCategory["PipelineAgentHwaccelChanged"] = "pipeline.agent-hwaccel-changed";
|
|
512
504
|
EventCategory["MotionAnalysis"] = "detection.motion-analysis";
|
|
513
505
|
/** All raw motion zones from CCL before minArea filter — for UI debug overlay. */
|
|
514
506
|
EventCategory["MotionZonesRaw"] = "detection.motion-zones-raw";
|
|
@@ -5379,7 +5371,7 @@ var ZodIssueCode = {
|
|
|
5379
5371
|
var ZodFirstPartyTypeKind;
|
|
5380
5372
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5381
5373
|
//#endregion
|
|
5382
|
-
//#region ../types/dist/sleep-
|
|
5374
|
+
//#region ../types/dist/sleep-BNeXenvr.mjs
|
|
5383
5375
|
/**
|
|
5384
5376
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5385
5377
|
* window to float samples (D455).
|
|
@@ -6745,32 +6737,12 @@ object({
|
|
|
6745
6737
|
audioCodec: string().optional(),
|
|
6746
6738
|
metadata: record(string(), unknown()).readonly().optional()
|
|
6747
6739
|
});
|
|
6748
|
-
var EncodedPacketSchema = object({
|
|
6749
|
-
type: _enum(["video", "audio"]),
|
|
6750
|
-
data: _instanceof(Uint8Array),
|
|
6751
|
-
pts: number(),
|
|
6752
|
-
dts: number(),
|
|
6753
|
-
keyframe: boolean(),
|
|
6754
|
-
codec: string()
|
|
6755
|
-
});
|
|
6756
|
-
var DecodedFrameSchema = object({
|
|
6757
|
-
data: _instanceof(Uint8Array),
|
|
6758
|
-
width: number(),
|
|
6759
|
-
height: number(),
|
|
6760
|
-
format: _enum([
|
|
6761
|
-
"jpeg",
|
|
6762
|
-
"rgb",
|
|
6763
|
-
"bgr",
|
|
6764
|
-
"yuv420",
|
|
6765
|
-
"gray"
|
|
6766
|
-
]),
|
|
6767
|
-
timestamp: number()
|
|
6768
|
-
});
|
|
6769
6740
|
/**
|
|
6770
6741
|
* Wire schema for `FrameHandle` — a zero-pixel, serialisable reference to a
|
|
6771
6742
|
* frame in a shared-memory ring (Phase 5 / D9). Mirrors the `FrameHandle` TS
|
|
6772
|
-
* interface in `interfaces/frame-handle.ts` field-for-field so a
|
|
6773
|
-
*
|
|
6743
|
+
* interface in `interfaces/frame-handle.ts` field-for-field so a cap method
|
|
6744
|
+
* (the runner's native-crop and pipeline-executor inputs) can carry handles
|
|
6745
|
+
* over tRPC / Moleculer.
|
|
6774
6746
|
*
|
|
6775
6747
|
* The `satisfies` assertion below pins this schema to the interface: if a
|
|
6776
6748
|
* field is added to / removed from `FrameHandle` without a matching schema
|
|
@@ -6796,50 +6768,6 @@ var FrameHandleSchema = object({
|
|
|
6796
6768
|
slotCount: number().int().positive()
|
|
6797
6769
|
});
|
|
6798
6770
|
/**
|
|
6799
|
-
* Pixel format a frame-handle subscriber can request from the broker
|
|
6800
|
-
* (Phase 5 / D9). The packed, raster subset of `FrameFormat` — the formats a
|
|
6801
|
-
* `frameSink: 'shm'` decoder session can write into a `FrameRing` slot.
|
|
6802
|
-
* `jpeg` is excluded: a variable-length stream is not a fixed-stride ring
|
|
6803
|
-
* payload, and no decoded-frame consumer requests it over the shm plane.
|
|
6804
|
-
*/
|
|
6805
|
-
var FrameHandleFormatSchema = _enum([
|
|
6806
|
-
"rgb",
|
|
6807
|
-
"bgr",
|
|
6808
|
-
"yuv420",
|
|
6809
|
-
"gray"
|
|
6810
|
-
]);
|
|
6811
|
-
/**
|
|
6812
|
-
* Input for `stream-broker.subscribeFrames` (Phase 5 / D9). A consumer asks the
|
|
6813
|
-
* broker for a `FrameHandle` stream of one `format`; the broker maintains one
|
|
6814
|
-
* shared-memory ring per `(brokerId, format)` actually requested, so a `gray`
|
|
6815
|
-
* subscriber (motion) and an `rgb` subscriber (detection) each read the format
|
|
6816
|
-
* they asked for with no broker-side conversion.
|
|
6817
|
-
*/
|
|
6818
|
-
var SubscribeFramesInputSchema = object({
|
|
6819
|
-
brokerId: string(),
|
|
6820
|
-
format: FrameHandleFormatSchema,
|
|
6821
|
-
/**
|
|
6822
|
-
* Optional reader-side cadence hint in frames per second. The broker does
|
|
6823
|
-
* NOT throttle — latest-wins ring reads drop frames implicitly for a slow
|
|
6824
|
-
* consumer. The value is echoed back in `SubscribeFramesResult.maxFps` so
|
|
6825
|
-
* the consumer can pace its own `pullFrameHandles` polling.
|
|
6826
|
-
*/
|
|
6827
|
-
maxFps: number().positive().optional(),
|
|
6828
|
-
/** Short caller-identity tag (`motion`, `detection`, …) for diagnostics. */
|
|
6829
|
-
tag: string().optional()
|
|
6830
|
-
});
|
|
6831
|
-
/**
|
|
6832
|
-
* Result of `stream-broker.subscribeFrames`. The consumer then polls
|
|
6833
|
-
* `pullFrameHandles({ subscriptionId, maxCount })` and feeds each returned
|
|
6834
|
-
* `FrameHandle` to a `FrameRingReader`.
|
|
6835
|
-
*/
|
|
6836
|
-
var SubscribeFramesResultSchema = object({
|
|
6837
|
-
/** Opaque id the consumer passes to `pullFrameHandles` / `unsubscribeFrames`. */
|
|
6838
|
-
subscriptionId: string(),
|
|
6839
|
-
/** Reader-side cadence hint (frames/s) — echoes `SubscribeFramesInput.maxFps`. */
|
|
6840
|
-
maxFps: number().nonnegative()
|
|
6841
|
-
});
|
|
6842
|
-
/**
|
|
6843
6771
|
* Wire schema for a decoded audio chunk (Phase 5 / D9). Mirrors the
|
|
6844
6772
|
* `DecodedAudioChunk` TS interface in `interfaces/stream-broker.ts`: PCM
|
|
6845
6773
|
* sample bytes plus the track parameters a consumer needs to interpret them.
|
|
@@ -6847,8 +6775,8 @@ var SubscribeFramesResultSchema = object({
|
|
|
6847
6775
|
* Audio chunks are tiny (a ~500ms AudioCodecSession window is a few KB of
|
|
6848
6776
|
* Float32 PCM), so unlike video frames they ship their bytes INLINE over
|
|
6849
6777
|
* tRPC / Moleculer — no shared-memory plane. `data` is typed `Uint8Array`
|
|
6850
|
-
* (the wire-serialisable supertype of `Buffer`)
|
|
6851
|
-
*
|
|
6778
|
+
* (the wire-serialisable supertype of `Buffer`); a `Buffer` is assignable to
|
|
6779
|
+
* it.
|
|
6852
6780
|
*/
|
|
6853
6781
|
var AudioChunkFormatSchema = _enum(AUDIO_CHUNK_FORMATS);
|
|
6854
6782
|
var DecodedAudioChunkSchema = object({
|
|
@@ -6866,11 +6794,10 @@ var DecodedAudioChunkSchema = object({
|
|
|
6866
6794
|
format: AudioChunkFormatSchema.optional()
|
|
6867
6795
|
});
|
|
6868
6796
|
/**
|
|
6869
|
-
* Input for `stream-broker.subscribeAudioChunks` (Phase 5 / D9).
|
|
6870
|
-
*
|
|
6871
|
-
*
|
|
6872
|
-
*
|
|
6873
|
-
* `pullAudioChunks`.
|
|
6797
|
+
* Input for `stream-broker.subscribeAudioChunks` (Phase 5 / D9). Audio ships
|
|
6798
|
+
* its bytes inline: a consumer asks the broker for the decoded audio-chunk
|
|
6799
|
+
* stream of one broker; the broker keeps a per-subscription bounded FIFO queue
|
|
6800
|
+
* the consumer drains via `pullAudioChunks`.
|
|
6874
6801
|
*/
|
|
6875
6802
|
var SubscribeAudioChunksInputSchema = object({
|
|
6876
6803
|
brokerId: string(),
|
|
@@ -6901,9 +6828,7 @@ var BrokerStatsSchema = object({
|
|
|
6901
6828
|
"stopped"
|
|
6902
6829
|
]),
|
|
6903
6830
|
inputFps: number(),
|
|
6904
|
-
decodeFps: number(),
|
|
6905
6831
|
encodedSubscribers: number(),
|
|
6906
|
-
decodedSubscribers: number(),
|
|
6907
6832
|
uptimeMs: number(),
|
|
6908
6833
|
bitrateKbps: number(),
|
|
6909
6834
|
idrIntervalMs: number(),
|
|
@@ -6916,14 +6841,6 @@ var BrokerStatsSchema = object({
|
|
|
6916
6841
|
preBufferMs: number(),
|
|
6917
6842
|
preBufferPackets: number(),
|
|
6918
6843
|
/**
|
|
6919
|
-
* Moleculer node id of the decoder provider currently servicing this
|
|
6920
|
-
* stream's decoded subscribers. `null` until the deferred decoder is
|
|
6921
|
-
* created (no decoded clients yet, or codec not detected). Surfaces the
|
|
6922
|
-
* runtime decoder placement so the UI can show "Decoder: <agent>" without
|
|
6923
|
-
* a separate cap call per broker.
|
|
6924
|
-
*/
|
|
6925
|
-
decoderNodeId: string().nullable(),
|
|
6926
|
-
/**
|
|
6927
6844
|
* Detected audio track parameters from the RTSP DESCRIBE / SDP. `null`
|
|
6928
6845
|
* when the stream has no audio track or the broker is in cold start.
|
|
6929
6846
|
* `supported = false` means the codec was detected but the local
|
|
@@ -10446,70 +10363,6 @@ var StorageLocationDeclarationSchema = object({
|
|
|
10446
10363
|
"backup"
|
|
10447
10364
|
]).optional()
|
|
10448
10365
|
});
|
|
10449
|
-
var DecoderStatsSchema = object({
|
|
10450
|
-
inputFps: number(),
|
|
10451
|
-
outputFps: number(),
|
|
10452
|
-
avgDecodeTimeMs: number(),
|
|
10453
|
-
droppedFrames: number(),
|
|
10454
|
-
/**
|
|
10455
|
-
* Pull-mode adaptive-fps telemetry (optional — only pull sessions run the
|
|
10456
|
-
* lag-driven controller; push sessions omit these). `lagMs` is the EWMA of
|
|
10457
|
-
* the decoder's real-time drift (rising = falling behind live); `adaptiveFps`
|
|
10458
|
-
* is the current lag-throttled emit rate (≤ `effectiveFps` ceiling).
|
|
10459
|
-
*/
|
|
10460
|
-
lagMs: number().optional(),
|
|
10461
|
-
effectiveFps: number().optional(),
|
|
10462
|
-
adaptiveFps: number().optional()
|
|
10463
|
-
});
|
|
10464
|
-
var DecoderSessionConfigSchema = object({
|
|
10465
|
-
codec: string(),
|
|
10466
|
-
maxFps: number().default(0),
|
|
10467
|
-
outputFormat: _enum([
|
|
10468
|
-
"jpeg",
|
|
10469
|
-
"rgb",
|
|
10470
|
-
"bgr",
|
|
10471
|
-
"yuv420",
|
|
10472
|
-
"gray"
|
|
10473
|
-
]).default("jpeg"),
|
|
10474
|
-
scale: number().default(1),
|
|
10475
|
-
width: number().optional(),
|
|
10476
|
-
height: number().optional(),
|
|
10477
|
-
/**
|
|
10478
|
-
* Identifier of the camera this decoder session serves. Optional
|
|
10479
|
-
* because the cap is generic (any caller could request decode), but
|
|
10480
|
-
* stream-broker passes it so decoder logs include `deviceId` for
|
|
10481
|
-
* per-camera filtering when diagnosing failures (e.g. node-av
|
|
10482
|
-
* sendPacket errors on a single hung camera).
|
|
10483
|
-
*/
|
|
10484
|
-
deviceId: number().int().nonnegative().optional(),
|
|
10485
|
-
/**
|
|
10486
|
-
* Free-form tag for log scoping. Stream-broker uses
|
|
10487
|
-
* `broker:<deviceId>/<profile>`. Decoder session logger surfaces it
|
|
10488
|
-
* on every line so `grep tag=broker:5/high` filters one camera
|
|
10489
|
-
* profile cleanly.
|
|
10490
|
-
*/
|
|
10491
|
-
tag: string().optional(),
|
|
10492
|
-
/**
|
|
10493
|
-
* Where the session delivers decoded frames (Phase 5 / D9):
|
|
10494
|
-
*
|
|
10495
|
-
* - `'callback'` (default) — the legacy pixel path: decoded frames are
|
|
10496
|
-
* buffered as `DecodedFrame`s and drained via `pullFrames`.
|
|
10497
|
-
* - `'shm'` — the shared-memory frame plane: decoded frames are written
|
|
10498
|
-
* into an OS shared-memory ring and drained as zero-pixel
|
|
10499
|
-
* `FrameHandle`s via `pullHandles`. A session is one mode or the
|
|
10500
|
-
* other — `pullFrames` returns nothing for an `'shm'` session and
|
|
10501
|
-
* `pullHandles` returns nothing for a `'callback'` session.
|
|
10502
|
-
*/
|
|
10503
|
-
frameSink: _enum(["callback", "shm"]).default("callback"),
|
|
10504
|
-
/**
|
|
10505
|
-
* Per-camera decoder DEBUG facility. When `true`, a pull-mode session emits
|
|
10506
|
-
* a throttled (~1Hz) structured `decoder debug` line (effective/adaptive fps,
|
|
10507
|
-
* real-time lag, dropped-frame delta, avg decode time, hwaccel). Mirrors the
|
|
10508
|
-
* stream-broker's `streamingDebug` gate — off by default so production logs
|
|
10509
|
-
* stay quiet and the emit path pays zero per-frame cost when disabled.
|
|
10510
|
-
*/
|
|
10511
|
-
debug: boolean().optional()
|
|
10512
|
-
});
|
|
10513
10366
|
/**
|
|
10514
10367
|
* Per-component log CHANNELS — the gate a hot path consults, and the registry
|
|
10515
10368
|
* an addon declares its channels in.
|
|
@@ -12770,13 +12623,6 @@ var BrokerRtspClientSchema = object({
|
|
|
12770
12623
|
lastRtpAt: number(),
|
|
12771
12624
|
bytesSent: number()
|
|
12772
12625
|
});
|
|
12773
|
-
var BrokerDecodedClientSchema = object({
|
|
12774
|
-
tag: string(),
|
|
12775
|
-
subscribedAt: number(),
|
|
12776
|
-
maxFps: number(),
|
|
12777
|
-
framesDelivered: number(),
|
|
12778
|
-
framesDropped: number()
|
|
12779
|
-
});
|
|
12780
12626
|
var BrokerAudioClientSchema = object({
|
|
12781
12627
|
tag: string(),
|
|
12782
12628
|
subscribedAt: number(),
|
|
@@ -12868,7 +12714,6 @@ var BrokerEncodedClientSchema = object({
|
|
|
12868
12714
|
});
|
|
12869
12715
|
var BrokerClientsSchema = object({
|
|
12870
12716
|
rtsp: array(BrokerRtspClientSchema).readonly(),
|
|
12871
|
-
decoded: array(BrokerDecodedClientSchema).readonly(),
|
|
12872
12717
|
audio: array(BrokerAudioClientSchema).readonly(),
|
|
12873
12718
|
encoded: array(BrokerEncodedClientSchema).readonly(),
|
|
12874
12719
|
pipeClients: number(),
|
|
@@ -13237,11 +13082,7 @@ method(object({
|
|
|
13237
13082
|
auth: "admin"
|
|
13238
13083
|
}), method(object({ brokerId: string() }), BrokerClientsSchema), method(object({
|
|
13239
13084
|
brokerId: string(),
|
|
13240
|
-
channel: _enum([
|
|
13241
|
-
"rtsp",
|
|
13242
|
-
"decoded",
|
|
13243
|
-
"audio"
|
|
13244
|
-
]),
|
|
13085
|
+
channel: _enum(["rtsp", "audio"]),
|
|
13245
13086
|
handle: string()
|
|
13246
13087
|
}), object({ killed: boolean() }), {
|
|
13247
13088
|
kind: "mutation",
|
|
@@ -13276,10 +13117,7 @@ method(object({
|
|
|
13276
13117
|
}), method(SubscribeAudioChunksInputSchema, SubscribeAudioChunksResultSchema, { kind: "mutation" }), method(object({
|
|
13277
13118
|
subscriptionId: string(),
|
|
13278
13119
|
maxCount: number().int().positive().default(8)
|
|
13279
|
-
}), array(DecodedAudioChunkSchema).readonly()), method(object({ subscriptionId: string() }), object({ released: boolean() }), { kind: "mutation" }), method(
|
|
13280
|
-
subscriptionId: string(),
|
|
13281
|
-
maxCount: number().int().positive().default(4)
|
|
13282
|
-
}), array(FrameHandleSchema).readonly()), method(object({ subscriptionId: string() }), object({ released: boolean() }), { kind: "mutation" }), method(object({
|
|
13120
|
+
}), array(DecodedAudioChunkSchema).readonly()), method(object({ subscriptionId: string() }), object({ released: boolean() }), { kind: "mutation" }), method(object({
|
|
13283
13121
|
brokerId: string(),
|
|
13284
13122
|
seconds: number().min(0).max(30)
|
|
13285
13123
|
}), _void(), {
|
|
@@ -14436,54 +14274,12 @@ id: union([string(), number()]) }), {
|
|
|
14436
14274
|
kind: "mutation",
|
|
14437
14275
|
auth: "admin"
|
|
14438
14276
|
});
|
|
14439
|
-
|
|
14440
|
-
* shm ring usage stats for a `frameSink: 'shm'` decoder session —
|
|
14441
|
-
* exposed via `decoder.getShmStats` so downstream consumers can
|
|
14442
|
-
* observe ring pressure (slot count, byte budget, hit/miss ratio).
|
|
14443
|
-
*/
|
|
14444
|
-
var ShmRingStatsSchema = object({
|
|
14445
|
-
sessionId: string(),
|
|
14446
|
-
slotCount: number().int(),
|
|
14447
|
-
slotByteLength: number().int(),
|
|
14448
|
-
segmentBytes: number().int(),
|
|
14449
|
-
budgetMb: number().int(),
|
|
14450
|
-
framesWritten: number().int(),
|
|
14451
|
-
getFrameHits: number().int(),
|
|
14452
|
-
getFrameMisses: number().int()
|
|
14453
|
-
});
|
|
14454
|
-
method(object({ codec: string() }), boolean()), method(_void(), object({
|
|
14277
|
+
method(_void(), object({
|
|
14455
14278
|
id: string(),
|
|
14456
14279
|
name: string(),
|
|
14457
|
-
isPullMode: boolean().optional(),
|
|
14458
|
-
priority: number().optional(),
|
|
14459
14280
|
hwaccel: string().optional(),
|
|
14460
14281
|
probedBestHwaccel: string().optional()
|
|
14461
|
-
})), method(
|
|
14462
|
-
sessionId: string(),
|
|
14463
|
-
nodeId: string()
|
|
14464
|
-
})), method(object({ sessionId: string() }), _void()), method(object({
|
|
14465
|
-
sessionId: string(),
|
|
14466
|
-
packet: EncodedPacketSchema
|
|
14467
|
-
}), _void()), method(object({
|
|
14468
|
-
sessionId: string(),
|
|
14469
|
-
url: string()
|
|
14470
|
-
}), _void()), method(object({
|
|
14471
|
-
sessionId: string(),
|
|
14472
|
-
maxCount: number().default(1),
|
|
14473
|
-
waitMs: number().optional()
|
|
14474
|
-
}), array(DecodedFrameSchema)), method(object({
|
|
14475
|
-
sessionId: string(),
|
|
14476
|
-
maxCount: number().default(1),
|
|
14477
|
-
waitMs: number().optional()
|
|
14478
|
-
}), array(FrameHandleSchema)), method(object({ handle: FrameHandleSchema }), DecodedFrameSchema.nullable()), method(object({ sessionId: string() }), ShmRingStatsSchema.nullable()), method(object({
|
|
14479
|
-
sessionId: string(),
|
|
14480
|
-
config: DecoderSessionConfigSchema.partial()
|
|
14481
|
-
}), _void()), method(object({ sessionId: string() }), DecoderStatsSchema), method(_void(), array(object({
|
|
14482
|
-
sessionId: string(),
|
|
14483
|
-
codec: string(),
|
|
14484
|
-
outputFormat: string(),
|
|
14485
|
-
createdAtMs: number()
|
|
14486
|
-
})).readonly()), method(_void(), object({ backend: string() }), {
|
|
14282
|
+
})), method(_void(), object({ backend: string() }), {
|
|
14487
14283
|
kind: "mutation",
|
|
14488
14284
|
auth: "admin"
|
|
14489
14285
|
});
|
|
@@ -21544,8 +21340,8 @@ Object.freeze(Object.fromEntries([{
|
|
|
21544
21340
|
var CLUSTER_STEP_SETTING_FIELDS = [{
|
|
21545
21341
|
stepId: "face-embedding",
|
|
21546
21342
|
key: "minLandmarkFaceSize",
|
|
21547
|
-
label: "Min face size
|
|
21548
|
-
description: "
|
|
21343
|
+
label: "Min face size to embed (detection-frame px)",
|
|
21344
|
+
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.",
|
|
21549
21345
|
type: "slider",
|
|
21550
21346
|
min: 0,
|
|
21551
21347
|
max: 64,
|
|
@@ -23597,21 +23393,6 @@ var PipelineDefaultStepSchema = lazy(() => object({
|
|
|
23597
23393
|
group: string().optional(),
|
|
23598
23394
|
settings: record(string(), unknown()).optional()
|
|
23599
23395
|
}));
|
|
23600
|
-
var PipelineTemplateStepSchema = lazy(() => object({
|
|
23601
|
-
addonId: string(),
|
|
23602
|
-
enabled: boolean(),
|
|
23603
|
-
modelId: string(),
|
|
23604
|
-
children: array(PipelineTemplateStepSchema).readonly(),
|
|
23605
|
-
settings: record(string(), unknown()).optional()
|
|
23606
|
-
}));
|
|
23607
|
-
var PipelineTemplateSchema$1 = object({
|
|
23608
|
-
id: string(),
|
|
23609
|
-
name: string(),
|
|
23610
|
-
createdAt: string(),
|
|
23611
|
-
updatedAt: string(),
|
|
23612
|
-
engine: PipelineEngineChoiceSchema,
|
|
23613
|
-
steps: array(PipelineTemplateStepSchema).readonly()
|
|
23614
|
-
});
|
|
23615
23396
|
var PipelineModelOptionSchema = object({
|
|
23616
23397
|
id: string(),
|
|
23617
23398
|
name: string(),
|
|
@@ -23823,15 +23604,7 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
23823
23604
|
}), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({
|
|
23824
23605
|
steps: array(PipelineStepInputSchema),
|
|
23825
23606
|
deviceKey: string().optional()
|
|
23826
|
-
}), PipelineValidationResultSchema), method(_void(),
|
|
23827
|
-
name: string(),
|
|
23828
|
-
steps: array(PipelineTemplateStepSchema).readonly(),
|
|
23829
|
-
engine: PipelineEngineChoiceSchema
|
|
23830
|
-
}), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({
|
|
23831
|
-
id: string(),
|
|
23832
|
-
name: string().optional(),
|
|
23833
|
-
steps: array(PipelineTemplateStepSchema).readonly().optional()
|
|
23834
|
-
}), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({ id: string() }), _void(), { kind: "mutation" }), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
|
|
23607
|
+
}), PipelineValidationResultSchema), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
|
|
23835
23608
|
addonId: string(),
|
|
23836
23609
|
modelId: string(),
|
|
23837
23610
|
format: ModelFormatSchema$2
|
|
@@ -25241,14 +25014,15 @@ var RunnerLocalLoadSchema = object({
|
|
|
25241
25014
|
* Per-inference-device live load (multi-device C4). One entry per deviceKey
|
|
25242
25015
|
* this runner currently has attached cameras on, so the orchestrator's second
|
|
25243
25016
|
* `balance()` pass (over a node's devices) weights on real per-pool session
|
|
25244
|
-
* counts. Empty on single-device / pre-multi-device runners.
|
|
25245
|
-
* per
|
|
25017
|
+
* counts. Empty on single-device / pre-multi-device runners. There is no
|
|
25018
|
+
* per-device queue depth: the inference pool's backlog has no accessor, and
|
|
25019
|
+
* a field that always read 0 was removed rather than shown as a measurement
|
|
25020
|
+
* (D762).
|
|
25246
25021
|
*/
|
|
25247
25022
|
devices: array(object({
|
|
25248
25023
|
deviceKey: string(),
|
|
25249
25024
|
backend: string(),
|
|
25250
|
-
attachedCameras: number()
|
|
25251
|
-
queueDepthTotal: number()
|
|
25025
|
+
attachedCameras: number()
|
|
25252
25026
|
})).default([]),
|
|
25253
25027
|
/** Hardware capability flags reported by this node. */
|
|
25254
25028
|
hardware: object({
|
|
@@ -25462,14 +25236,6 @@ var CameraPipelineConfigSchema = object({
|
|
|
25462
25236
|
settings: record(string(), unknown()).readonly().optional()
|
|
25463
25237
|
}).nullable().optional()
|
|
25464
25238
|
});
|
|
25465
|
-
var PipelineTemplateSchema = object({
|
|
25466
|
-
id: string(),
|
|
25467
|
-
name: string(),
|
|
25468
|
-
description: string().optional(),
|
|
25469
|
-
config: CameraPipelineConfigSchema,
|
|
25470
|
-
createdAt: string(),
|
|
25471
|
-
updatedAt: string()
|
|
25472
|
-
});
|
|
25473
25239
|
var DeviceStepConfigSchema = object({
|
|
25474
25240
|
modelId: string().optional(),
|
|
25475
25241
|
settings: record(string(), unknown()).optional(),
|
|
@@ -25579,7 +25345,10 @@ var PipelineAssignmentSchema = object({
|
|
|
25579
25345
|
});
|
|
25580
25346
|
/**
|
|
25581
25347
|
* Per-agent load summary surfaced to the load balancer + dashboards.
|
|
25582
|
-
* Aggregated from each runner's `getLocalLoad` cap call.
|
|
25348
|
+
* Aggregated from each runner's `getLocalLoad` cap call. In the
|
|
25349
|
+
* `getAgentLoad` report, a single-device node's default-pool sessions (no
|
|
25350
|
+
* `deviceKey`) are credited to its one eligible device in `load.devices` —
|
|
25351
|
+
* the default pool IS that device (D745); the balancer's census stays raw.
|
|
25583
25352
|
*/
|
|
25584
25353
|
var AgentLoadSummarySchema = object({
|
|
25585
25354
|
nodeId: string(),
|
|
@@ -25668,26 +25437,27 @@ var CameraBrokerStatusSchema = object({
|
|
|
25668
25437
|
width: number(),
|
|
25669
25438
|
height: number(),
|
|
25670
25439
|
subscribers: number(),
|
|
25671
|
-
|
|
25672
|
-
|
|
25440
|
+
/**
|
|
25441
|
+
* Encoded packets/s the broker receives from the camera. null when the
|
|
25442
|
+
* broker's stats read failed — unknown, never 0 (a 0 here reads as a stall).
|
|
25443
|
+
* There is no output rate: decoding runs in the session decode workers, not
|
|
25444
|
+
* in the broker, so the broker's own decoded-frame rate is 0 on every live
|
|
25445
|
+
* camera and is not served (D758).
|
|
25446
|
+
*/
|
|
25447
|
+
inFps: number().nullable()
|
|
25673
25448
|
})).readonly(),
|
|
25674
25449
|
webrtcSessions: number(),
|
|
25675
25450
|
rtspRestream: boolean()
|
|
25676
25451
|
});
|
|
25677
|
-
/**
|
|
25678
|
-
|
|
25679
|
-
|
|
25680
|
-
|
|
25681
|
-
|
|
25682
|
-
|
|
25683
|
-
|
|
25684
|
-
|
|
25685
|
-
var CameraDecoderStatusSchema = object({
|
|
25686
|
-
nodeId: string(),
|
|
25687
|
-
formats: array(string()).readonly(),
|
|
25688
|
-
sessionCount: number(),
|
|
25689
|
-
shm: CameraDecoderShmSchema
|
|
25690
|
-
});
|
|
25452
|
+
/**
|
|
25453
|
+
* Decoder block — WHERE decode runs (co-located with the detection node by
|
|
25454
|
+
* construction); null when the camera has no detection node. It carries no
|
|
25455
|
+
* frame counter: the shared-memory plane whose counters it used to serve is
|
|
25456
|
+
* gone, and those fields read 0 forever. Whether frames actually flow is read
|
|
25457
|
+
* from `detection.actualFps` (frames delivered to and processed by the
|
|
25458
|
+
* detector), never from a counter nothing feeds.
|
|
25459
|
+
*/
|
|
25460
|
+
var CameraDecoderStatusSchema = object({ nodeId: string() });
|
|
25691
25461
|
/** Motion block — null when motion detection is not active for this device. */
|
|
25692
25462
|
var CameraMotionStatusSchema = object({
|
|
25693
25463
|
enabled: boolean(),
|
|
@@ -26140,25 +25910,7 @@ method(object({
|
|
|
26140
25910
|
}), method(_void(), DiskReconcileJobSchema, {
|
|
26141
25911
|
kind: "mutation",
|
|
26142
25912
|
auth: "admin"
|
|
26143
|
-
}), method(_void(), DiskReconcileJobSchema, { auth: "admin" })
|
|
26144
|
-
name: string(),
|
|
26145
|
-
description: string().optional(),
|
|
26146
|
-
config: CameraPipelineConfigSchema
|
|
26147
|
-
}), PipelineTemplateSchema, {
|
|
26148
|
-
kind: "mutation",
|
|
26149
|
-
auth: "admin"
|
|
26150
|
-
}), method(object({
|
|
26151
|
-
id: string(),
|
|
26152
|
-
name: string().optional(),
|
|
26153
|
-
description: string().optional(),
|
|
26154
|
-
config: CameraPipelineConfigSchema.optional()
|
|
26155
|
-
}), PipelineTemplateSchema, {
|
|
26156
|
-
kind: "mutation",
|
|
26157
|
-
auth: "admin"
|
|
26158
|
-
}), method(object({ id: string() }), object({ success: literal(true) }), {
|
|
26159
|
-
kind: "mutation",
|
|
26160
|
-
auth: "admin"
|
|
26161
|
-
});
|
|
25913
|
+
}), method(_void(), DiskReconcileJobSchema, { auth: "admin" });
|
|
26162
25914
|
/**
|
|
26163
25915
|
* Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
|
|
26164
25916
|
* `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
|
|
@@ -28378,8 +28130,6 @@ var WebrtcStreamChoiceSchema = object({
|
|
|
28378
28130
|
]).nullable(),
|
|
28379
28131
|
/** Input FPS from the camera (null for adaptive). */
|
|
28380
28132
|
inputFps: number().nullable(),
|
|
28381
|
-
/** Decoded FPS (null for adaptive). */
|
|
28382
|
-
decodeFps: number().nullable(),
|
|
28383
28133
|
/** Bitrate in kbps (null for adaptive). */
|
|
28384
28134
|
bitrateKbps: number().nullable()
|
|
28385
28135
|
});
|
|
@@ -38514,90 +38264,18 @@ Object.freeze({
|
|
|
38514
38264
|
addonId: null,
|
|
38515
38265
|
access: "create"
|
|
38516
38266
|
},
|
|
38517
|
-
"decoder.createSession": {
|
|
38518
|
-
capName: "decoder",
|
|
38519
|
-
capScope: "system",
|
|
38520
|
-
addonId: null,
|
|
38521
|
-
access: "view"
|
|
38522
|
-
},
|
|
38523
|
-
"decoder.destroySession": {
|
|
38524
|
-
capName: "decoder",
|
|
38525
|
-
capScope: "system",
|
|
38526
|
-
addonId: null,
|
|
38527
|
-
access: "delete"
|
|
38528
|
-
},
|
|
38529
|
-
"decoder.getFrame": {
|
|
38530
|
-
capName: "decoder",
|
|
38531
|
-
capScope: "system",
|
|
38532
|
-
addonId: null,
|
|
38533
|
-
access: "view"
|
|
38534
|
-
},
|
|
38535
38267
|
"decoder.getInfo": {
|
|
38536
38268
|
capName: "decoder",
|
|
38537
38269
|
capScope: "system",
|
|
38538
38270
|
addonId: null,
|
|
38539
38271
|
access: "view"
|
|
38540
38272
|
},
|
|
38541
|
-
"decoder.getShmStats": {
|
|
38542
|
-
capName: "decoder",
|
|
38543
|
-
capScope: "system",
|
|
38544
|
-
addonId: null,
|
|
38545
|
-
access: "view"
|
|
38546
|
-
},
|
|
38547
|
-
"decoder.getStats": {
|
|
38548
|
-
capName: "decoder",
|
|
38549
|
-
capScope: "system",
|
|
38550
|
-
addonId: null,
|
|
38551
|
-
access: "view"
|
|
38552
|
-
},
|
|
38553
|
-
"decoder.listActiveSessions": {
|
|
38554
|
-
capName: "decoder",
|
|
38555
|
-
capScope: "system",
|
|
38556
|
-
addonId: null,
|
|
38557
|
-
access: "view"
|
|
38558
|
-
},
|
|
38559
|
-
"decoder.openStream": {
|
|
38560
|
-
capName: "decoder",
|
|
38561
|
-
capScope: "system",
|
|
38562
|
-
addonId: null,
|
|
38563
|
-
access: "view"
|
|
38564
|
-
},
|
|
38565
|
-
"decoder.pullFrames": {
|
|
38566
|
-
capName: "decoder",
|
|
38567
|
-
capScope: "system",
|
|
38568
|
-
addonId: null,
|
|
38569
|
-
access: "view"
|
|
38570
|
-
},
|
|
38571
|
-
"decoder.pullHandles": {
|
|
38572
|
-
capName: "decoder",
|
|
38573
|
-
capScope: "system",
|
|
38574
|
-
addonId: null,
|
|
38575
|
-
access: "view"
|
|
38576
|
-
},
|
|
38577
|
-
"decoder.pushPacket": {
|
|
38578
|
-
capName: "decoder",
|
|
38579
|
-
capScope: "system",
|
|
38580
|
-
addonId: null,
|
|
38581
|
-
access: "view"
|
|
38582
|
-
},
|
|
38583
38273
|
"decoder.reprobeHwaccel": {
|
|
38584
38274
|
capName: "decoder",
|
|
38585
38275
|
capScope: "system",
|
|
38586
38276
|
addonId: null,
|
|
38587
38277
|
access: "create"
|
|
38588
38278
|
},
|
|
38589
|
-
"decoder.supportsCodec": {
|
|
38590
|
-
capName: "decoder",
|
|
38591
|
-
capScope: "system",
|
|
38592
|
-
addonId: null,
|
|
38593
|
-
access: "view"
|
|
38594
|
-
},
|
|
38595
|
-
"decoder.updateConfig": {
|
|
38596
|
-
capName: "decoder",
|
|
38597
|
-
capScope: "system",
|
|
38598
|
-
addonId: null,
|
|
38599
|
-
access: "view"
|
|
38600
|
-
},
|
|
38601
38279
|
"detectionPipeline.applyDeviceSettingsPatch": {
|
|
38602
38280
|
capName: "detection-pipeline",
|
|
38603
38281
|
capScope: "device",
|
|
@@ -41688,12 +41366,6 @@ Object.freeze({
|
|
|
41688
41366
|
addonId: null,
|
|
41689
41367
|
access: "delete"
|
|
41690
41368
|
},
|
|
41691
|
-
"pipelineExecutor.deleteTemplate": {
|
|
41692
|
-
capName: "pipeline-executor",
|
|
41693
|
-
capScope: "system",
|
|
41694
|
-
addonId: null,
|
|
41695
|
-
access: "delete"
|
|
41696
|
-
},
|
|
41697
41369
|
"pipelineExecutor.downloadModel": {
|
|
41698
41370
|
capName: "pipeline-executor",
|
|
41699
41371
|
capScope: "system",
|
|
@@ -41832,12 +41504,6 @@ Object.freeze({
|
|
|
41832
41504
|
addonId: null,
|
|
41833
41505
|
access: "view"
|
|
41834
41506
|
},
|
|
41835
|
-
"pipelineExecutor.listTemplates": {
|
|
41836
|
-
capName: "pipeline-executor",
|
|
41837
|
-
capScope: "system",
|
|
41838
|
-
addonId: null,
|
|
41839
|
-
access: "view"
|
|
41840
|
-
},
|
|
41841
41507
|
"pipelineExecutor.rearmInferenceDevice": {
|
|
41842
41508
|
capName: "pipeline-executor",
|
|
41843
41509
|
capScope: "system",
|
|
@@ -41862,12 +41528,6 @@ Object.freeze({
|
|
|
41862
41528
|
addonId: null,
|
|
41863
41529
|
access: "create"
|
|
41864
41530
|
},
|
|
41865
|
-
"pipelineExecutor.saveTemplate": {
|
|
41866
|
-
capName: "pipeline-executor",
|
|
41867
|
-
capScope: "system",
|
|
41868
|
-
addonId: null,
|
|
41869
|
-
access: "create"
|
|
41870
|
-
},
|
|
41871
41531
|
"pipelineExecutor.setVideoPipelineSteps": {
|
|
41872
41532
|
capName: "pipeline-executor",
|
|
41873
41533
|
capScope: "system",
|
|
@@ -41886,12 +41546,6 @@ Object.freeze({
|
|
|
41886
41546
|
addonId: null,
|
|
41887
41547
|
access: "create"
|
|
41888
41548
|
},
|
|
41889
|
-
"pipelineExecutor.updateTemplate": {
|
|
41890
|
-
capName: "pipeline-executor",
|
|
41891
|
-
capScope: "system",
|
|
41892
|
-
addonId: null,
|
|
41893
|
-
access: "create"
|
|
41894
|
-
},
|
|
41895
41549
|
"pipelineExecutor.validatePipeline": {
|
|
41896
41550
|
capName: "pipeline-executor",
|
|
41897
41551
|
capScope: "system",
|
|
@@ -41922,12 +41576,6 @@ Object.freeze({
|
|
|
41922
41576
|
addonId: null,
|
|
41923
41577
|
access: "create"
|
|
41924
41578
|
},
|
|
41925
|
-
"pipelineOrchestrator.deleteTemplate": {
|
|
41926
|
-
capName: "pipeline-orchestrator",
|
|
41927
|
-
capScope: "system",
|
|
41928
|
-
addonId: null,
|
|
41929
|
-
access: "delete"
|
|
41930
|
-
},
|
|
41931
41579
|
"pipelineOrchestrator.getAgentLoad": {
|
|
41932
41580
|
capName: "pipeline-orchestrator",
|
|
41933
41581
|
capScope: "system",
|
|
@@ -42078,12 +41726,6 @@ Object.freeze({
|
|
|
42078
41726
|
addonId: null,
|
|
42079
41727
|
access: "view"
|
|
42080
41728
|
},
|
|
42081
|
-
"pipelineOrchestrator.listTemplates": {
|
|
42082
|
-
capName: "pipeline-orchestrator",
|
|
42083
|
-
capScope: "system",
|
|
42084
|
-
addonId: null,
|
|
42085
|
-
access: "view"
|
|
42086
|
-
},
|
|
42087
41729
|
"pipelineOrchestrator.pauseForStorageMigration": {
|
|
42088
41730
|
capName: "pipeline-orchestrator",
|
|
42089
41731
|
capScope: "system",
|
|
@@ -42138,12 +41780,6 @@ Object.freeze({
|
|
|
42138
41780
|
addonId: null,
|
|
42139
41781
|
access: "create"
|
|
42140
41782
|
},
|
|
42141
|
-
"pipelineOrchestrator.saveTemplate": {
|
|
42142
|
-
capName: "pipeline-orchestrator",
|
|
42143
|
-
capScope: "system",
|
|
42144
|
-
addonId: null,
|
|
42145
|
-
access: "create"
|
|
42146
|
-
},
|
|
42147
41783
|
"pipelineOrchestrator.setAgentCapabilities": {
|
|
42148
41784
|
capName: "pipeline-orchestrator",
|
|
42149
41785
|
capScope: "system",
|
|
@@ -42252,12 +41888,6 @@ Object.freeze({
|
|
|
42252
41888
|
addonId: null,
|
|
42253
41889
|
access: "create"
|
|
42254
41890
|
},
|
|
42255
|
-
"pipelineOrchestrator.updateTemplate": {
|
|
42256
|
-
capName: "pipeline-orchestrator",
|
|
42257
|
-
capScope: "system",
|
|
42258
|
-
addonId: null,
|
|
42259
|
-
access: "create"
|
|
42260
|
-
},
|
|
42261
41891
|
"pipelineRunner.attachCamera": {
|
|
42262
41892
|
capName: "pipeline-runner",
|
|
42263
41893
|
capScope: "system",
|
|
@@ -43692,12 +43322,6 @@ Object.freeze({
|
|
|
43692
43322
|
addonId: null,
|
|
43693
43323
|
access: "view"
|
|
43694
43324
|
},
|
|
43695
|
-
"streamBroker.pullFrameHandles": {
|
|
43696
|
-
capName: "stream-broker",
|
|
43697
|
-
capScope: "system",
|
|
43698
|
-
addonId: null,
|
|
43699
|
-
access: "view"
|
|
43700
|
-
},
|
|
43701
43325
|
"streamBroker.regenerateRtspToken": {
|
|
43702
43326
|
capName: "stream-broker",
|
|
43703
43327
|
capScope: "system",
|
|
@@ -43764,12 +43388,6 @@ Object.freeze({
|
|
|
43764
43388
|
addonId: null,
|
|
43765
43389
|
access: "create"
|
|
43766
43390
|
},
|
|
43767
|
-
"streamBroker.subscribeFrames": {
|
|
43768
|
-
capName: "stream-broker",
|
|
43769
|
-
capScope: "system",
|
|
43770
|
-
addonId: null,
|
|
43771
|
-
access: "create"
|
|
43772
|
-
},
|
|
43773
43391
|
"streamBroker.unassignProfile": {
|
|
43774
43392
|
capName: "stream-broker",
|
|
43775
43393
|
capScope: "system",
|
|
@@ -43782,12 +43400,6 @@ Object.freeze({
|
|
|
43782
43400
|
addonId: null,
|
|
43783
43401
|
access: "create"
|
|
43784
43402
|
},
|
|
43785
|
-
"streamBroker.unsubscribeFrames": {
|
|
43786
|
-
capName: "stream-broker",
|
|
43787
|
-
capScope: "system",
|
|
43788
|
-
addonId: null,
|
|
43789
|
-
access: "create"
|
|
43790
|
-
},
|
|
43791
43403
|
"streamCatalog.getCatalog": {
|
|
43792
43404
|
capName: "stream-catalog",
|
|
43793
43405
|
capScope: "device",
|
|
@@ -46301,11 +45913,6 @@ Object.freeze({
|
|
|
46301
45913
|
form: "single",
|
|
46302
45914
|
optional: false
|
|
46303
45915
|
}],
|
|
46304
|
-
"decoder.createSession": [{
|
|
46305
|
-
name: "deviceId",
|
|
46306
|
-
form: "single",
|
|
46307
|
-
optional: true
|
|
46308
|
-
}],
|
|
46309
45916
|
"detectionPipeline.applyDeviceSettingsPatch": [{
|
|
46310
45917
|
name: "deviceId",
|
|
46311
45918
|
form: "single",
|
|
@@ -49817,6 +49424,9 @@ function parseNonNegativeInt(s) {
|
|
|
49817
49424
|
/**
|
|
49818
49425
|
* Shared Alexa `Alexa/ErrorResponse` envelope builder.
|
|
49819
49426
|
*
|
|
49427
|
+
* Certification checks the shape: `correlationToken` (set here) and
|
|
49428
|
+
* `endpoint` (set by `withErrorEndpoint` in the router) are both required.
|
|
49429
|
+
*
|
|
49820
49430
|
* Lives in its own module so every handler (and the directive router)
|
|
49821
49431
|
* can emit its own namespace-appropriate error without circular imports.
|
|
49822
49432
|
*/
|
|
@@ -49825,8 +49435,10 @@ object({ event: object({
|
|
|
49825
49435
|
namespace: literal("Alexa"),
|
|
49826
49436
|
name: literal("ErrorResponse"),
|
|
49827
49437
|
messageId: string(),
|
|
49438
|
+
correlationToken: string().optional(),
|
|
49828
49439
|
payloadVersion: literal("3")
|
|
49829
49440
|
}),
|
|
49441
|
+
endpoint: object({ endpointId: string() }).optional(),
|
|
49830
49442
|
payload: object({
|
|
49831
49443
|
type: string(),
|
|
49832
49444
|
message: string()
|
|
@@ -49838,6 +49450,7 @@ function errorEnvelope(header, type, message) {
|
|
|
49838
49450
|
namespace: "Alexa",
|
|
49839
49451
|
name: "ErrorResponse",
|
|
49840
49452
|
messageId: header.messageId,
|
|
49453
|
+
...header.correlationToken !== void 0 ? { correlationToken: header.correlationToken } : {},
|
|
49841
49454
|
payloadVersion: "3"
|
|
49842
49455
|
},
|
|
49843
49456
|
payload: {
|
|
@@ -50198,18 +49811,43 @@ async function handleSessionDisconnected(header, endpointRaw, payloadRaw, deps,
|
|
|
50198
49811
|
},
|
|
50199
49812
|
meta: { endpointId: endpoint.endpointId }
|
|
50200
49813
|
});
|
|
49814
|
+
return sessionEvent("SessionDisconnected", header, endpoint.endpointId, parsed.data.payload.sessionId);
|
|
49815
|
+
}
|
|
49816
|
+
/**
|
|
49817
|
+
* The response to `SessionConnected` / `SessionDisconnected` is the event of
|
|
49818
|
+
* the same name with the session id (alexa-rtcsessioncontroller) — not a
|
|
49819
|
+
* generic `Alexa.Response`.
|
|
49820
|
+
*/
|
|
49821
|
+
function sessionEvent(name, header, endpointId, sessionId) {
|
|
50201
49822
|
return { event: {
|
|
50202
49823
|
header: {
|
|
50203
|
-
namespace: "Alexa",
|
|
50204
|
-
name
|
|
49824
|
+
namespace: "Alexa.RTCSessionController",
|
|
49825
|
+
name,
|
|
50205
49826
|
messageId: header.messageId,
|
|
50206
49827
|
correlationToken: header.correlationToken,
|
|
50207
49828
|
payloadVersion: "3"
|
|
50208
49829
|
},
|
|
50209
|
-
endpoint: { endpointId
|
|
50210
|
-
payload: {}
|
|
49830
|
+
endpoint: { endpointId },
|
|
49831
|
+
payload: { sessionId }
|
|
50211
49832
|
} };
|
|
50212
49833
|
}
|
|
49834
|
+
/**
|
|
49835
|
+
* `SessionConnected` — Alexa reports the media path is up. Nothing to do
|
|
49836
|
+
* hub-side (the session already exists from the offer); acknowledge it. Amazon
|
|
49837
|
+
* notes it is not always sent, so nothing may depend on it.
|
|
49838
|
+
*/
|
|
49839
|
+
function handleSessionConnected(header, endpointRaw, payloadRaw, deps) {
|
|
49840
|
+
const parsed = SessionDisconnectedDirectiveSchema.safeParse({
|
|
49841
|
+
endpoint: endpointRaw,
|
|
49842
|
+
payload: payloadRaw ?? {}
|
|
49843
|
+
});
|
|
49844
|
+
if (!parsed.success) return errorEnvelope(header, "INVALID_DIRECTIVE", `payload validation failed: ${parsed.error.message}`);
|
|
49845
|
+
deps.logger.info("alexa rtc-session: SessionConnected", {
|
|
49846
|
+
tags: { sessionId: parsed.data.payload.sessionId },
|
|
49847
|
+
meta: { endpointId: parsed.data.endpoint.endpointId }
|
|
49848
|
+
});
|
|
49849
|
+
return sessionEvent("SessionConnected", header, parsed.data.endpoint.endpointId, parsed.data.payload.sessionId);
|
|
49850
|
+
}
|
|
50213
49851
|
//#endregion
|
|
50214
49852
|
//#region src/broker-client.ts
|
|
50215
49853
|
/**
|
|
@@ -50577,6 +50215,27 @@ var PTZ_TILT_MODE_CONTROLLER = ptzDirectionalModeController("ptz.tilt", "Tilt",
|
|
|
50577
50215
|
label: "Down"
|
|
50578
50216
|
}
|
|
50579
50217
|
]);
|
|
50218
|
+
/** Amazon (alexa-modecontroller): "You must include a minimum of two mode
|
|
50219
|
+
* objects." One preset made the camera's whole AddOrUpdateReport fail
|
|
50220
|
+
* schema validation, and Discover dropped the camera. */
|
|
50221
|
+
var MIN_MODE_CONTROLLER_MODES = 2;
|
|
50222
|
+
/**
|
|
50223
|
+
* Preset names as distinct mode values. Two presets with the same name (the
|
|
50224
|
+
* camera allows it: `Stanza`, `Stanza`; `CamStack Home` ×3) produced two
|
|
50225
|
+
* identical modes, and Alexa rejected the capability — "Invalid capability
|
|
50226
|
+
* received." — and with it every endpoint of the camera. First wins,
|
|
50227
|
+
* case-insensitively: the SetMode handler resolves a value to the first
|
|
50228
|
+
* preset of that name, which is the one kept.
|
|
50229
|
+
*/
|
|
50230
|
+
function distinctPresetNames(names) {
|
|
50231
|
+
const seen = /* @__PURE__ */ new Set();
|
|
50232
|
+
return names.filter((name) => {
|
|
50233
|
+
const key = name.toLowerCase();
|
|
50234
|
+
if (seen.has(key)) return false;
|
|
50235
|
+
seen.add(key);
|
|
50236
|
+
return true;
|
|
50237
|
+
});
|
|
50238
|
+
}
|
|
50580
50239
|
function ptzPresetModeController(presetNames) {
|
|
50581
50240
|
return {
|
|
50582
50241
|
type: "AlexaInterface",
|
|
@@ -50621,7 +50280,8 @@ function buildParentCameraEndpoint(input) {
|
|
|
50621
50280
|
if (input.isDoorbell) capabilities.push(DOORBELL_EVENT_SOURCE);
|
|
50622
50281
|
if (input.hasMotion) capabilities.push(MOTION_SENSOR);
|
|
50623
50282
|
if (input.hasBattery) capabilities.push(BATTERY_LEVEL_SENSOR);
|
|
50624
|
-
|
|
50283
|
+
const presetNames = distinctPresetNames(input.ptzPresetNames);
|
|
50284
|
+
if (presetNames.length >= MIN_MODE_CONTROLLER_MODES) capabilities.push(ptzPresetModeController(presetNames));
|
|
50625
50285
|
if (input.hasPtz) {
|
|
50626
50286
|
capabilities.push(PTZ_PAN_MODE_CONTROLLER);
|
|
50627
50287
|
capabilities.push(PTZ_TILT_MODE_CONTROLLER);
|
|
@@ -50760,6 +50420,12 @@ function buildAlarmPanelEndpoint(input) {
|
|
|
50760
50420
|
};
|
|
50761
50421
|
}
|
|
50762
50422
|
//#endregion
|
|
50423
|
+
//#region src/handlers/ptz-preset-recallable.ts
|
|
50424
|
+
var RECALLABLE_FUNCTIONS = new Set(["home"]);
|
|
50425
|
+
function isAlexaRecallablePreset(preset) {
|
|
50426
|
+
return preset.fn === null || RECALLABLE_FUNCTIONS.has(preset.fn);
|
|
50427
|
+
}
|
|
50428
|
+
//#endregion
|
|
50763
50429
|
//#region src/handlers/discovery.ts
|
|
50764
50430
|
var discoveryPayloadSchema = DiscoveryDirectiveSchema.shape.payload;
|
|
50765
50431
|
/** Devices built at once in `handleDiscovery` — latency-bound tRPC reads, so
|
|
@@ -50947,7 +50613,7 @@ async function loadBoundDeviceInfo(device, api) {
|
|
|
50947
50613
|
};
|
|
50948
50614
|
}
|
|
50949
50615
|
async function fetchPtzPresetNames(deviceId, api) {
|
|
50950
|
-
return (await api.ptz.getPresets.query({ deviceId })).map((p) => p.name).filter((n) => n.length > 0);
|
|
50616
|
+
return (await api.ptz.getPresets.query({ deviceId })).filter(isAlexaRecallablePreset).map((p) => p.name).filter((n) => n.length > 0);
|
|
50951
50617
|
}
|
|
50952
50618
|
function classifyChildKind(deviceType, role) {
|
|
50953
50619
|
if (role === "floodlight" || role === "spotlight" || role === "nightvision") return "light";
|
|
@@ -51347,8 +51013,8 @@ async function handleSetMode(header, endpointRaw, payloadRaw, deps, _callerIdent
|
|
|
51347
51013
|
if (!mode.startsWith(PTZ_PRESET_MODE_PREFIX)) return errorEnvelope(header, "INVALID_DIRECTIVE", `mode '${mode}' is not a ptz.preset mode`);
|
|
51348
51014
|
const presetName = mode.slice(11);
|
|
51349
51015
|
if (presetName.length === 0) return errorEnvelope(header, "INVALID_DIRECTIVE", "preset name missing after prefix");
|
|
51350
|
-
const preset = (await api.ptz.getPresets.query({ deviceId: parsedId.deviceId })).find((p) => p.name === presetName);
|
|
51351
|
-
if (!preset) return errorEnvelope(header, "INVALID_DIRECTIVE", `preset '${presetName}' not
|
|
51016
|
+
const preset = (await api.ptz.getPresets.query({ deviceId: parsedId.deviceId })).find((p) => p.name === presetName && isAlexaRecallablePreset(p));
|
|
51017
|
+
if (!preset) return errorEnvelope(header, "INVALID_DIRECTIVE", `preset '${presetName}' is not a recallable preset on device ${parsedId.deviceId}`);
|
|
51352
51018
|
await api.ptz.goToPreset.mutate({
|
|
51353
51019
|
deviceId: parsedId.deviceId,
|
|
51354
51020
|
presetId: preset.id
|
|
@@ -51822,7 +51488,37 @@ function assertDeviceScope(scopes) {
|
|
|
51822
51488
|
function requireDeviceScope(scopes, access) {
|
|
51823
51489
|
if (!scopesAllowDeviceCap(scopes, access)) throw new Error(`directive token not authorized for device ${access}`);
|
|
51824
51490
|
}
|
|
51491
|
+
/**
|
|
51492
|
+
* Give an `Alexa.ErrorResponse` the directive's endpoint when it lacks one.
|
|
51493
|
+
* Amazon requires `endpoint` on every ErrorResponse to an endpoint directive
|
|
51494
|
+
* (alexa-errorresponse); the handlers build their errors from the header
|
|
51495
|
+
* alone, so the router — which holds the endpoint for every path — adds it.
|
|
51496
|
+
*/
|
|
51497
|
+
function withErrorEndpoint(body, endpoint) {
|
|
51498
|
+
const endpointId = typeof endpoint === "object" && endpoint !== null && typeof endpoint.endpointId === "string" ? endpoint.endpointId : null;
|
|
51499
|
+
if (endpointId === null || typeof body !== "object" || body === null) return body;
|
|
51500
|
+
const event = body.event;
|
|
51501
|
+
if (typeof event !== "object" || event === null) return body;
|
|
51502
|
+
const ev = event;
|
|
51503
|
+
if (ev.header?.namespace !== "Alexa" || ev.header?.name !== "ErrorResponse") return body;
|
|
51504
|
+
if (ev.endpoint !== void 0) return body;
|
|
51505
|
+
return {
|
|
51506
|
+
...body,
|
|
51507
|
+
event: {
|
|
51508
|
+
...ev,
|
|
51509
|
+
endpoint: { endpointId }
|
|
51510
|
+
}
|
|
51511
|
+
};
|
|
51512
|
+
}
|
|
51825
51513
|
async function dispatchDirective(rawBody, deps, callerIdentity) {
|
|
51514
|
+
const result = await dispatchParsedDirective(rawBody, deps, callerIdentity);
|
|
51515
|
+
const endpoint = rawBody?.directive?.endpoint;
|
|
51516
|
+
return {
|
|
51517
|
+
status: result.status,
|
|
51518
|
+
body: withErrorEndpoint(result.body, endpoint)
|
|
51519
|
+
};
|
|
51520
|
+
}
|
|
51521
|
+
async function dispatchParsedDirective(rawBody, deps, callerIdentity) {
|
|
51826
51522
|
const parsed = DirectiveRequestSchema.safeParse(rawBody);
|
|
51827
51523
|
if (!parsed.success) return {
|
|
51828
51524
|
status: 400,
|
|
@@ -51844,6 +51540,7 @@ async function dispatchDirective(rawBody, deps, callerIdentity) {
|
|
|
51844
51540
|
"Alexa.Discovery/Discover": "view",
|
|
51845
51541
|
"Alexa.CameraStreamController/InitializeCameraStreams": "view",
|
|
51846
51542
|
"Alexa.RTCSessionController/InitiateSessionWithOffer": "view",
|
|
51543
|
+
"Alexa.RTCSessionController/SessionConnected": "view",
|
|
51847
51544
|
"Alexa.RTCSessionController/SessionDisconnected": "view",
|
|
51848
51545
|
"Alexa.PowerController/TurnOn": "create",
|
|
51849
51546
|
"Alexa.PowerController/TurnOff": "create",
|
|
@@ -51878,6 +51575,10 @@ async function dispatchDirective(rawBody, deps, callerIdentity) {
|
|
|
51878
51575
|
status: 200,
|
|
51879
51576
|
body: await handleInitiateSessionWithOffer(header, endpoint, payload, deps.rtcSession, callerIdentity)
|
|
51880
51577
|
};
|
|
51578
|
+
if (key === "Alexa.RTCSessionController/SessionConnected") return {
|
|
51579
|
+
status: 200,
|
|
51580
|
+
body: handleSessionConnected(header, endpoint, payload, deps.rtcSession)
|
|
51581
|
+
};
|
|
51881
51582
|
if (key === "Alexa.RTCSessionController/SessionDisconnected") return {
|
|
51882
51583
|
status: 200,
|
|
51883
51584
|
body: await handleSessionDisconnected(header, endpoint, payload, deps.rtcSession, callerIdentity)
|
|
@@ -52384,10 +52085,12 @@ var DEFAULT_MOTION_CLEAR_HOLD_MS = 5e3;
|
|
|
52384
52085
|
var MotionClearHold = class {
|
|
52385
52086
|
entries = /* @__PURE__ */ new Map();
|
|
52386
52087
|
holdMs;
|
|
52088
|
+
minDetectedMs;
|
|
52387
52089
|
setTimer;
|
|
52388
52090
|
clearTimer;
|
|
52389
52091
|
constructor(options) {
|
|
52390
52092
|
this.holdMs = options.holdMs;
|
|
52093
|
+
this.minDetectedMs = options.minDetectedMs ?? 3e4;
|
|
52391
52094
|
this.setTimer = options.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
52392
52095
|
this.clearTimer = options.clearTimer ?? ((handle) => clearTimeout(handle));
|
|
52393
52096
|
}
|
|
@@ -52405,13 +52108,15 @@ var MotionClearHold = class {
|
|
|
52405
52108
|
if (entry.timer !== null) this.clearTimer(entry.timer);
|
|
52406
52109
|
this.entries.set(key, {
|
|
52407
52110
|
state: "detected",
|
|
52408
|
-
timer: null
|
|
52111
|
+
timer: null,
|
|
52112
|
+
detectedAt: entry.detectedAt
|
|
52409
52113
|
});
|
|
52410
52114
|
return false;
|
|
52411
52115
|
}
|
|
52412
52116
|
this.entries.set(key, {
|
|
52413
52117
|
state: "detected",
|
|
52414
|
-
timer: null
|
|
52118
|
+
timer: null,
|
|
52119
|
+
detectedAt: Date.now()
|
|
52415
52120
|
});
|
|
52416
52121
|
return true;
|
|
52417
52122
|
}
|
|
@@ -52432,16 +52137,21 @@ var MotionClearHold = class {
|
|
|
52432
52137
|
onCleared(key, release) {
|
|
52433
52138
|
const entry = this.entries.get(key);
|
|
52434
52139
|
if (entry !== void 0 && entry.state !== "detected") return;
|
|
52140
|
+
const detectedAt = entry?.detectedAt ?? null;
|
|
52141
|
+
const sinceDetected = detectedAt === null ? 0 : Date.now() - detectedAt;
|
|
52142
|
+
const delay = Math.max(this.holdMs, this.minDetectedMs - sinceDetected);
|
|
52435
52143
|
const timer = this.setTimer(() => {
|
|
52436
52144
|
this.entries.set(key, {
|
|
52437
52145
|
state: "idle",
|
|
52438
|
-
timer: null
|
|
52146
|
+
timer: null,
|
|
52147
|
+
detectedAt: null
|
|
52439
52148
|
});
|
|
52440
52149
|
release();
|
|
52441
|
-
},
|
|
52150
|
+
}, delay);
|
|
52442
52151
|
this.entries.set(key, {
|
|
52443
52152
|
state: "holding",
|
|
52444
|
-
timer
|
|
52153
|
+
timer,
|
|
52154
|
+
detectedAt
|
|
52445
52155
|
});
|
|
52446
52156
|
}
|
|
52447
52157
|
/** What Alexa currently believes for this key — for logs and assertions. */
|
|
@@ -52495,6 +52205,9 @@ var REFRESH_PROACTIVE_WINDOW_MS = 300 * 1e3;
|
|
|
52495
52205
|
var REFRESH_SCAN_INTERVAL_MS = 60 * 1e3;
|
|
52496
52206
|
/** After a failed legacy-grant migration, wait this long before trying again. */
|
|
52497
52207
|
var MIGRATION_RETRY_MS = 300 * 1e3;
|
|
52208
|
+
/** The interface a camera endpoint must declare for a connectivity
|
|
52209
|
+
* ChangeReport to mean anything to the Alexa cloud. */
|
|
52210
|
+
var ENDPOINT_HEALTH_INTERFACE = "Alexa.EndpointHealth";
|
|
52498
52211
|
/** The interface an endpoint must advertise for a `DoorbellPress` to survive
|
|
52499
52212
|
* the Alexa cloud. */
|
|
52500
52213
|
var DOORBELL_INTERFACE = "Alexa.DoorbellEventSource";
|
|
@@ -52526,6 +52239,10 @@ var LwaEventGateway = class {
|
|
|
52526
52239
|
unsubscribe = null;
|
|
52527
52240
|
unsubscribeMotion = null;
|
|
52528
52241
|
unsubscribeBattery = null;
|
|
52242
|
+
unsubscribeConnectivity = [];
|
|
52243
|
+
/** Last connectivity reported per `userId|endpointId` — a camera that
|
|
52244
|
+
* re-announces the same state must not become a ChangeReport stream. */
|
|
52245
|
+
lastConnectivity = /* @__PURE__ */ new Map();
|
|
52529
52246
|
refreshTimer = null;
|
|
52530
52247
|
lastSentAt = /* @__PURE__ */ new Map();
|
|
52531
52248
|
/**
|
|
@@ -52574,6 +52291,11 @@ var LwaEventGateway = class {
|
|
|
52574
52291
|
this.deps.logger.warn("alexa lwa-gateway: battery handler crashed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
52575
52292
|
});
|
|
52576
52293
|
});
|
|
52294
|
+
this.unsubscribeConnectivity = [[EventCategory.DeviceOnline, "OK"], [EventCategory.DeviceOffline, "UNREACHABLE"]].map(([category, connectivity]) => this.deps.ctx.eventBus.subscribe({ category }, (event) => {
|
|
52295
|
+
this.onConnectivityChanged(event.data, connectivity).catch((err) => {
|
|
52296
|
+
this.deps.logger.warn("alexa lwa-gateway: connectivity handler crashed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
52297
|
+
});
|
|
52298
|
+
}));
|
|
52577
52299
|
this.refreshTimer = setInterval(() => {
|
|
52578
52300
|
this.refreshExpiringTokens().catch((err) => {
|
|
52579
52301
|
this.deps.logger.warn("alexa lwa-gateway: token refresh scan crashed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
@@ -52588,6 +52310,9 @@ var LwaEventGateway = class {
|
|
|
52588
52310
|
this.unsubscribeMotion = null;
|
|
52589
52311
|
if (this.unsubscribeBattery) this.unsubscribeBattery();
|
|
52590
52312
|
this.unsubscribeBattery = null;
|
|
52313
|
+
for (const off of this.unsubscribeConnectivity) off();
|
|
52314
|
+
this.unsubscribeConnectivity = [];
|
|
52315
|
+
this.lastConnectivity.clear();
|
|
52591
52316
|
if (this.refreshTimer) clearInterval(this.refreshTimer);
|
|
52592
52317
|
this.refreshTimer = null;
|
|
52593
52318
|
this.lastSentAt.clear();
|
|
@@ -52792,6 +52517,33 @@ var LwaEventGateway = class {
|
|
|
52792
52517
|
}
|
|
52793
52518
|
}
|
|
52794
52519
|
/**
|
|
52520
|
+
* A camera went offline or came back. Amazon requires the `connectivity`
|
|
52521
|
+
* ChangeReport within 3 s of the change (alexa-endpointhealth). Deduped per
|
|
52522
|
+
* account and endpoint, and skipped for an endpoint Alexa is known to hold
|
|
52523
|
+
* WITHOUT `Alexa.EndpointHealth` — the cloud would drop it (D76).
|
|
52524
|
+
*/
|
|
52525
|
+
async onConnectivityChanged(data, connectivity) {
|
|
52526
|
+
if (typeof data?.deviceId !== "number") return;
|
|
52527
|
+
if (!this.deps.listExposed().some((e) => e.deviceId === data.deviceId)) return;
|
|
52528
|
+
const signature = this.deps.getExportedSignature(data.deviceId);
|
|
52529
|
+
if (signature !== null && !signature.includes(ENDPOINT_HEALTH_INTERFACE)) return;
|
|
52530
|
+
const linked = this.deps.listLinkedAccounts();
|
|
52531
|
+
if (linked.length === 0) return;
|
|
52532
|
+
if (!this.hasProactiveCredentials()) return;
|
|
52533
|
+
const endpointId = String(data.deviceId);
|
|
52534
|
+
for (const account of linked) {
|
|
52535
|
+
const key = `${account.userId}|${endpointId}`;
|
|
52536
|
+
if (this.lastConnectivity.get(key) === connectivity) continue;
|
|
52537
|
+
this.lastConnectivity.set(key, connectivity);
|
|
52538
|
+
await this.postEventBody(account.userId, endpointId, (accessToken) => buildConnectivityChangeReportPayload({
|
|
52539
|
+
endpointId,
|
|
52540
|
+
connectivity,
|
|
52541
|
+
timestamp: Date.now(),
|
|
52542
|
+
accessToken
|
|
52543
|
+
}), `connectivity ${connectivity}`);
|
|
52544
|
+
}
|
|
52545
|
+
}
|
|
52546
|
+
/**
|
|
52795
52547
|
* Send a one-shot `DeleteReport` for arbitrary endpoint ids per linked
|
|
52796
52548
|
* account. Used to clean up stale entries Alexa still shows from prior
|
|
52797
52549
|
* syncs (different addon version, different account, or an aborted
|
|
@@ -53112,6 +52864,36 @@ function buildBatteryChangeReportPayload(opts) {
|
|
|
53112
52864
|
context: { properties: [] }
|
|
53113
52865
|
};
|
|
53114
52866
|
}
|
|
52867
|
+
function buildConnectivityChangeReportPayload(opts) {
|
|
52868
|
+
return {
|
|
52869
|
+
event: {
|
|
52870
|
+
header: {
|
|
52871
|
+
namespace: "Alexa",
|
|
52872
|
+
name: "ChangeReport",
|
|
52873
|
+
messageId: randomUUID(),
|
|
52874
|
+
payloadVersion: "3"
|
|
52875
|
+
},
|
|
52876
|
+
endpoint: {
|
|
52877
|
+
scope: {
|
|
52878
|
+
type: "BearerToken",
|
|
52879
|
+
token: opts.accessToken
|
|
52880
|
+
},
|
|
52881
|
+
endpointId: opts.endpointId
|
|
52882
|
+
},
|
|
52883
|
+
payload: { change: {
|
|
52884
|
+
cause: { type: "PERIODIC_POLL" },
|
|
52885
|
+
properties: [{
|
|
52886
|
+
namespace: "Alexa.EndpointHealth",
|
|
52887
|
+
name: "connectivity",
|
|
52888
|
+
value: { value: opts.connectivity },
|
|
52889
|
+
timeOfSample: new Date(opts.timestamp).toISOString(),
|
|
52890
|
+
uncertaintyInMilliseconds: 0
|
|
52891
|
+
}]
|
|
52892
|
+
} }
|
|
52893
|
+
},
|
|
52894
|
+
context: { properties: [] }
|
|
52895
|
+
};
|
|
52896
|
+
}
|
|
53115
52897
|
function buildSmartVisionObjectDetectionEvent(opts) {
|
|
53116
52898
|
const timeOfSample = new Date(opts.timestamp).toISOString();
|
|
53117
52899
|
return { event: {
|
|
@@ -53280,7 +53062,7 @@ function buildAlexaOauthIntegration(publicHubUrl) {
|
|
|
53280
53062
|
* client id and SECRET, on the theory that a per-account refresh token was the
|
|
53281
53063
|
* real gate. The package is public on npm, so that secret was published with
|
|
53282
53064
|
* every version and has to be rotated. Since D753 the secret lives only in the
|
|
53283
|
-
* skill's Lambda (
|
|
53065
|
+
* skill's Lambda (SSM Parameter Store SecureString, D761); the hub gets short-lived Event Gateway
|
|
53284
53066
|
* tokens from the Lambda's broker (`broker-client.ts`) with a per-link
|
|
53285
53067
|
* credential. `scripts/check-no-client-secrets.ts` keeps it that way.
|
|
53286
53068
|
*
|
|
@@ -54311,7 +54093,7 @@ var ExportAlexaAddon = class extends BaseAddon {
|
|
|
54311
54093
|
content: [
|
|
54312
54094
|
"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).",
|
|
54313
54095
|
"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.",
|
|
54314
|
-
"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
|
|
54096
|
+
"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).",
|
|
54315
54097
|
"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."
|
|
54316
54098
|
].join("\n")
|
|
54317
54099
|
},
|
|
@@ -54378,7 +54160,7 @@ var ExportAlexaAddon = class extends BaseAddon {
|
|
|
54378
54160
|
key: "__custom-skill-info",
|
|
54379
54161
|
label: "Your skill Lambda",
|
|
54380
54162
|
variant: "info",
|
|
54381
|
-
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
|
|
54163
|
+
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."
|
|
54382
54164
|
}] : [{
|
|
54383
54165
|
type: "info",
|
|
54384
54166
|
key: "__public-skill-info",
|