@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
|
@@ -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-
|
|
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-
|
|
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
|
|
6800
|
-
*
|
|
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`)
|
|
6878
|
-
*
|
|
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).
|
|
6897
|
-
*
|
|
6898
|
-
*
|
|
6899
|
-
*
|
|
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(
|
|
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(
|
|
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
|
|
21575
|
-
description: "
|
|
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(),
|
|
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.
|
|
25272
|
-
* per
|
|
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
|
-
|
|
25699
|
-
|
|
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
|
-
/**
|
|
25705
|
-
|
|
25706
|
-
|
|
25707
|
-
|
|
25708
|
-
|
|
25709
|
-
|
|
25710
|
-
|
|
25711
|
-
|
|
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" })
|
|
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
|
});
|
|
@@ -38541,90 +38290,18 @@ Object.freeze({
|
|
|
38541
38290
|
addonId: null,
|
|
38542
38291
|
access: "create"
|
|
38543
38292
|
},
|
|
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
38293
|
"decoder.getInfo": {
|
|
38563
38294
|
capName: "decoder",
|
|
38564
38295
|
capScope: "system",
|
|
38565
38296
|
addonId: null,
|
|
38566
38297
|
access: "view"
|
|
38567
38298
|
},
|
|
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
38299
|
"decoder.reprobeHwaccel": {
|
|
38611
38300
|
capName: "decoder",
|
|
38612
38301
|
capScope: "system",
|
|
38613
38302
|
addonId: null,
|
|
38614
38303
|
access: "create"
|
|
38615
38304
|
},
|
|
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
38305
|
"detectionPipeline.applyDeviceSettingsPatch": {
|
|
38629
38306
|
capName: "detection-pipeline",
|
|
38630
38307
|
capScope: "device",
|
|
@@ -41715,12 +41392,6 @@ Object.freeze({
|
|
|
41715
41392
|
addonId: null,
|
|
41716
41393
|
access: "delete"
|
|
41717
41394
|
},
|
|
41718
|
-
"pipelineExecutor.deleteTemplate": {
|
|
41719
|
-
capName: "pipeline-executor",
|
|
41720
|
-
capScope: "system",
|
|
41721
|
-
addonId: null,
|
|
41722
|
-
access: "delete"
|
|
41723
|
-
},
|
|
41724
41395
|
"pipelineExecutor.downloadModel": {
|
|
41725
41396
|
capName: "pipeline-executor",
|
|
41726
41397
|
capScope: "system",
|
|
@@ -41859,12 +41530,6 @@ Object.freeze({
|
|
|
41859
41530
|
addonId: null,
|
|
41860
41531
|
access: "view"
|
|
41861
41532
|
},
|
|
41862
|
-
"pipelineExecutor.listTemplates": {
|
|
41863
|
-
capName: "pipeline-executor",
|
|
41864
|
-
capScope: "system",
|
|
41865
|
-
addonId: null,
|
|
41866
|
-
access: "view"
|
|
41867
|
-
},
|
|
41868
41533
|
"pipelineExecutor.rearmInferenceDevice": {
|
|
41869
41534
|
capName: "pipeline-executor",
|
|
41870
41535
|
capScope: "system",
|
|
@@ -41889,12 +41554,6 @@ Object.freeze({
|
|
|
41889
41554
|
addonId: null,
|
|
41890
41555
|
access: "create"
|
|
41891
41556
|
},
|
|
41892
|
-
"pipelineExecutor.saveTemplate": {
|
|
41893
|
-
capName: "pipeline-executor",
|
|
41894
|
-
capScope: "system",
|
|
41895
|
-
addonId: null,
|
|
41896
|
-
access: "create"
|
|
41897
|
-
},
|
|
41898
41557
|
"pipelineExecutor.setVideoPipelineSteps": {
|
|
41899
41558
|
capName: "pipeline-executor",
|
|
41900
41559
|
capScope: "system",
|
|
@@ -41913,12 +41572,6 @@ Object.freeze({
|
|
|
41913
41572
|
addonId: null,
|
|
41914
41573
|
access: "create"
|
|
41915
41574
|
},
|
|
41916
|
-
"pipelineExecutor.updateTemplate": {
|
|
41917
|
-
capName: "pipeline-executor",
|
|
41918
|
-
capScope: "system",
|
|
41919
|
-
addonId: null,
|
|
41920
|
-
access: "create"
|
|
41921
|
-
},
|
|
41922
41575
|
"pipelineExecutor.validatePipeline": {
|
|
41923
41576
|
capName: "pipeline-executor",
|
|
41924
41577
|
capScope: "system",
|
|
@@ -41949,12 +41602,6 @@ Object.freeze({
|
|
|
41949
41602
|
addonId: null,
|
|
41950
41603
|
access: "create"
|
|
41951
41604
|
},
|
|
41952
|
-
"pipelineOrchestrator.deleteTemplate": {
|
|
41953
|
-
capName: "pipeline-orchestrator",
|
|
41954
|
-
capScope: "system",
|
|
41955
|
-
addonId: null,
|
|
41956
|
-
access: "delete"
|
|
41957
|
-
},
|
|
41958
41605
|
"pipelineOrchestrator.getAgentLoad": {
|
|
41959
41606
|
capName: "pipeline-orchestrator",
|
|
41960
41607
|
capScope: "system",
|
|
@@ -42105,12 +41752,6 @@ Object.freeze({
|
|
|
42105
41752
|
addonId: null,
|
|
42106
41753
|
access: "view"
|
|
42107
41754
|
},
|
|
42108
|
-
"pipelineOrchestrator.listTemplates": {
|
|
42109
|
-
capName: "pipeline-orchestrator",
|
|
42110
|
-
capScope: "system",
|
|
42111
|
-
addonId: null,
|
|
42112
|
-
access: "view"
|
|
42113
|
-
},
|
|
42114
41755
|
"pipelineOrchestrator.pauseForStorageMigration": {
|
|
42115
41756
|
capName: "pipeline-orchestrator",
|
|
42116
41757
|
capScope: "system",
|
|
@@ -42165,12 +41806,6 @@ Object.freeze({
|
|
|
42165
41806
|
addonId: null,
|
|
42166
41807
|
access: "create"
|
|
42167
41808
|
},
|
|
42168
|
-
"pipelineOrchestrator.saveTemplate": {
|
|
42169
|
-
capName: "pipeline-orchestrator",
|
|
42170
|
-
capScope: "system",
|
|
42171
|
-
addonId: null,
|
|
42172
|
-
access: "create"
|
|
42173
|
-
},
|
|
42174
41809
|
"pipelineOrchestrator.setAgentCapabilities": {
|
|
42175
41810
|
capName: "pipeline-orchestrator",
|
|
42176
41811
|
capScope: "system",
|
|
@@ -42279,12 +41914,6 @@ Object.freeze({
|
|
|
42279
41914
|
addonId: null,
|
|
42280
41915
|
access: "create"
|
|
42281
41916
|
},
|
|
42282
|
-
"pipelineOrchestrator.updateTemplate": {
|
|
42283
|
-
capName: "pipeline-orchestrator",
|
|
42284
|
-
capScope: "system",
|
|
42285
|
-
addonId: null,
|
|
42286
|
-
access: "create"
|
|
42287
|
-
},
|
|
42288
41917
|
"pipelineRunner.attachCamera": {
|
|
42289
41918
|
capName: "pipeline-runner",
|
|
42290
41919
|
capScope: "system",
|
|
@@ -43719,12 +43348,6 @@ Object.freeze({
|
|
|
43719
43348
|
addonId: null,
|
|
43720
43349
|
access: "view"
|
|
43721
43350
|
},
|
|
43722
|
-
"streamBroker.pullFrameHandles": {
|
|
43723
|
-
capName: "stream-broker",
|
|
43724
|
-
capScope: "system",
|
|
43725
|
-
addonId: null,
|
|
43726
|
-
access: "view"
|
|
43727
|
-
},
|
|
43728
43351
|
"streamBroker.regenerateRtspToken": {
|
|
43729
43352
|
capName: "stream-broker",
|
|
43730
43353
|
capScope: "system",
|
|
@@ -43791,12 +43414,6 @@ Object.freeze({
|
|
|
43791
43414
|
addonId: null,
|
|
43792
43415
|
access: "create"
|
|
43793
43416
|
},
|
|
43794
|
-
"streamBroker.subscribeFrames": {
|
|
43795
|
-
capName: "stream-broker",
|
|
43796
|
-
capScope: "system",
|
|
43797
|
-
addonId: null,
|
|
43798
|
-
access: "create"
|
|
43799
|
-
},
|
|
43800
43417
|
"streamBroker.unassignProfile": {
|
|
43801
43418
|
capName: "stream-broker",
|
|
43802
43419
|
capScope: "system",
|
|
@@ -43809,12 +43426,6 @@ Object.freeze({
|
|
|
43809
43426
|
addonId: null,
|
|
43810
43427
|
access: "create"
|
|
43811
43428
|
},
|
|
43812
|
-
"streamBroker.unsubscribeFrames": {
|
|
43813
|
-
capName: "stream-broker",
|
|
43814
|
-
capScope: "system",
|
|
43815
|
-
addonId: null,
|
|
43816
|
-
access: "create"
|
|
43817
|
-
},
|
|
43818
43429
|
"streamCatalog.getCatalog": {
|
|
43819
43430
|
capName: "stream-catalog",
|
|
43820
43431
|
capScope: "device",
|
|
@@ -46328,11 +45939,6 @@ Object.freeze({
|
|
|
46328
45939
|
form: "single",
|
|
46329
45940
|
optional: false
|
|
46330
45941
|
}],
|
|
46331
|
-
"decoder.createSession": [{
|
|
46332
|
-
name: "deviceId",
|
|
46333
|
-
form: "single",
|
|
46334
|
-
optional: true
|
|
46335
|
-
}],
|
|
46336
45942
|
"detectionPipeline.applyDeviceSettingsPatch": [{
|
|
46337
45943
|
name: "deviceId",
|
|
46338
45944
|
form: "single",
|
|
@@ -49844,6 +49450,9 @@ function parseNonNegativeInt(s) {
|
|
|
49844
49450
|
/**
|
|
49845
49451
|
* Shared Alexa `Alexa/ErrorResponse` envelope builder.
|
|
49846
49452
|
*
|
|
49453
|
+
* Certification checks the shape: `correlationToken` (set here) and
|
|
49454
|
+
* `endpoint` (set by `withErrorEndpoint` in the router) are both required.
|
|
49455
|
+
*
|
|
49847
49456
|
* Lives in its own module so every handler (and the directive router)
|
|
49848
49457
|
* can emit its own namespace-appropriate error without circular imports.
|
|
49849
49458
|
*/
|
|
@@ -49852,8 +49461,10 @@ object({ event: object({
|
|
|
49852
49461
|
namespace: literal("Alexa"),
|
|
49853
49462
|
name: literal("ErrorResponse"),
|
|
49854
49463
|
messageId: string(),
|
|
49464
|
+
correlationToken: string().optional(),
|
|
49855
49465
|
payloadVersion: literal("3")
|
|
49856
49466
|
}),
|
|
49467
|
+
endpoint: object({ endpointId: string() }).optional(),
|
|
49857
49468
|
payload: object({
|
|
49858
49469
|
type: string(),
|
|
49859
49470
|
message: string()
|
|
@@ -49865,6 +49476,7 @@ function errorEnvelope(header, type, message) {
|
|
|
49865
49476
|
namespace: "Alexa",
|
|
49866
49477
|
name: "ErrorResponse",
|
|
49867
49478
|
messageId: header.messageId,
|
|
49479
|
+
...header.correlationToken !== void 0 ? { correlationToken: header.correlationToken } : {},
|
|
49868
49480
|
payloadVersion: "3"
|
|
49869
49481
|
},
|
|
49870
49482
|
payload: {
|
|
@@ -50225,18 +49837,43 @@ async function handleSessionDisconnected(header, endpointRaw, payloadRaw, deps,
|
|
|
50225
49837
|
},
|
|
50226
49838
|
meta: { endpointId: endpoint.endpointId }
|
|
50227
49839
|
});
|
|
49840
|
+
return sessionEvent("SessionDisconnected", header, endpoint.endpointId, parsed.data.payload.sessionId);
|
|
49841
|
+
}
|
|
49842
|
+
/**
|
|
49843
|
+
* The response to `SessionConnected` / `SessionDisconnected` is the event of
|
|
49844
|
+
* the same name with the session id (alexa-rtcsessioncontroller) — not a
|
|
49845
|
+
* generic `Alexa.Response`.
|
|
49846
|
+
*/
|
|
49847
|
+
function sessionEvent(name, header, endpointId, sessionId) {
|
|
50228
49848
|
return { event: {
|
|
50229
49849
|
header: {
|
|
50230
|
-
namespace: "Alexa",
|
|
50231
|
-
name
|
|
49850
|
+
namespace: "Alexa.RTCSessionController",
|
|
49851
|
+
name,
|
|
50232
49852
|
messageId: header.messageId,
|
|
50233
49853
|
correlationToken: header.correlationToken,
|
|
50234
49854
|
payloadVersion: "3"
|
|
50235
49855
|
},
|
|
50236
|
-
endpoint: { endpointId
|
|
50237
|
-
payload: {}
|
|
49856
|
+
endpoint: { endpointId },
|
|
49857
|
+
payload: { sessionId }
|
|
50238
49858
|
} };
|
|
50239
49859
|
}
|
|
49860
|
+
/**
|
|
49861
|
+
* `SessionConnected` — Alexa reports the media path is up. Nothing to do
|
|
49862
|
+
* hub-side (the session already exists from the offer); acknowledge it. Amazon
|
|
49863
|
+
* notes it is not always sent, so nothing may depend on it.
|
|
49864
|
+
*/
|
|
49865
|
+
function handleSessionConnected(header, endpointRaw, payloadRaw, deps) {
|
|
49866
|
+
const parsed = SessionDisconnectedDirectiveSchema.safeParse({
|
|
49867
|
+
endpoint: endpointRaw,
|
|
49868
|
+
payload: payloadRaw ?? {}
|
|
49869
|
+
});
|
|
49870
|
+
if (!parsed.success) return errorEnvelope(header, "INVALID_DIRECTIVE", `payload validation failed: ${parsed.error.message}`);
|
|
49871
|
+
deps.logger.info("alexa rtc-session: SessionConnected", {
|
|
49872
|
+
tags: { sessionId: parsed.data.payload.sessionId },
|
|
49873
|
+
meta: { endpointId: parsed.data.endpoint.endpointId }
|
|
49874
|
+
});
|
|
49875
|
+
return sessionEvent("SessionConnected", header, parsed.data.endpoint.endpointId, parsed.data.payload.sessionId);
|
|
49876
|
+
}
|
|
50240
49877
|
//#endregion
|
|
50241
49878
|
//#region src/broker-client.ts
|
|
50242
49879
|
/**
|
|
@@ -50604,6 +50241,27 @@ var PTZ_TILT_MODE_CONTROLLER = ptzDirectionalModeController("ptz.tilt", "Tilt",
|
|
|
50604
50241
|
label: "Down"
|
|
50605
50242
|
}
|
|
50606
50243
|
]);
|
|
50244
|
+
/** Amazon (alexa-modecontroller): "You must include a minimum of two mode
|
|
50245
|
+
* objects." One preset made the camera's whole AddOrUpdateReport fail
|
|
50246
|
+
* schema validation, and Discover dropped the camera. */
|
|
50247
|
+
var MIN_MODE_CONTROLLER_MODES = 2;
|
|
50248
|
+
/**
|
|
50249
|
+
* Preset names as distinct mode values. Two presets with the same name (the
|
|
50250
|
+
* camera allows it: `Stanza`, `Stanza`; `CamStack Home` ×3) produced two
|
|
50251
|
+
* identical modes, and Alexa rejected the capability — "Invalid capability
|
|
50252
|
+
* received." — and with it every endpoint of the camera. First wins,
|
|
50253
|
+
* case-insensitively: the SetMode handler resolves a value to the first
|
|
50254
|
+
* preset of that name, which is the one kept.
|
|
50255
|
+
*/
|
|
50256
|
+
function distinctPresetNames(names) {
|
|
50257
|
+
const seen = /* @__PURE__ */ new Set();
|
|
50258
|
+
return names.filter((name) => {
|
|
50259
|
+
const key = name.toLowerCase();
|
|
50260
|
+
if (seen.has(key)) return false;
|
|
50261
|
+
seen.add(key);
|
|
50262
|
+
return true;
|
|
50263
|
+
});
|
|
50264
|
+
}
|
|
50607
50265
|
function ptzPresetModeController(presetNames) {
|
|
50608
50266
|
return {
|
|
50609
50267
|
type: "AlexaInterface",
|
|
@@ -50648,7 +50306,8 @@ function buildParentCameraEndpoint(input) {
|
|
|
50648
50306
|
if (input.isDoorbell) capabilities.push(DOORBELL_EVENT_SOURCE);
|
|
50649
50307
|
if (input.hasMotion) capabilities.push(MOTION_SENSOR);
|
|
50650
50308
|
if (input.hasBattery) capabilities.push(BATTERY_LEVEL_SENSOR);
|
|
50651
|
-
|
|
50309
|
+
const presetNames = distinctPresetNames(input.ptzPresetNames);
|
|
50310
|
+
if (presetNames.length >= MIN_MODE_CONTROLLER_MODES) capabilities.push(ptzPresetModeController(presetNames));
|
|
50652
50311
|
if (input.hasPtz) {
|
|
50653
50312
|
capabilities.push(PTZ_PAN_MODE_CONTROLLER);
|
|
50654
50313
|
capabilities.push(PTZ_TILT_MODE_CONTROLLER);
|
|
@@ -50787,6 +50446,12 @@ function buildAlarmPanelEndpoint(input) {
|
|
|
50787
50446
|
};
|
|
50788
50447
|
}
|
|
50789
50448
|
//#endregion
|
|
50449
|
+
//#region src/handlers/ptz-preset-recallable.ts
|
|
50450
|
+
var RECALLABLE_FUNCTIONS = new Set(["home"]);
|
|
50451
|
+
function isAlexaRecallablePreset(preset) {
|
|
50452
|
+
return preset.fn === null || RECALLABLE_FUNCTIONS.has(preset.fn);
|
|
50453
|
+
}
|
|
50454
|
+
//#endregion
|
|
50790
50455
|
//#region src/handlers/discovery.ts
|
|
50791
50456
|
var discoveryPayloadSchema = DiscoveryDirectiveSchema.shape.payload;
|
|
50792
50457
|
/** Devices built at once in `handleDiscovery` — latency-bound tRPC reads, so
|
|
@@ -50974,7 +50639,7 @@ async function loadBoundDeviceInfo(device, api) {
|
|
|
50974
50639
|
};
|
|
50975
50640
|
}
|
|
50976
50641
|
async function fetchPtzPresetNames(deviceId, api) {
|
|
50977
|
-
return (await api.ptz.getPresets.query({ deviceId })).map((p) => p.name).filter((n) => n.length > 0);
|
|
50642
|
+
return (await api.ptz.getPresets.query({ deviceId })).filter(isAlexaRecallablePreset).map((p) => p.name).filter((n) => n.length > 0);
|
|
50978
50643
|
}
|
|
50979
50644
|
function classifyChildKind(deviceType, role) {
|
|
50980
50645
|
if (role === "floodlight" || role === "spotlight" || role === "nightvision") return "light";
|
|
@@ -51374,8 +51039,8 @@ async function handleSetMode(header, endpointRaw, payloadRaw, deps, _callerIdent
|
|
|
51374
51039
|
if (!mode.startsWith(PTZ_PRESET_MODE_PREFIX)) return errorEnvelope(header, "INVALID_DIRECTIVE", `mode '${mode}' is not a ptz.preset mode`);
|
|
51375
51040
|
const presetName = mode.slice(11);
|
|
51376
51041
|
if (presetName.length === 0) return errorEnvelope(header, "INVALID_DIRECTIVE", "preset name missing after prefix");
|
|
51377
|
-
const preset = (await api.ptz.getPresets.query({ deviceId: parsedId.deviceId })).find((p) => p.name === presetName);
|
|
51378
|
-
if (!preset) return errorEnvelope(header, "INVALID_DIRECTIVE", `preset '${presetName}' not
|
|
51042
|
+
const preset = (await api.ptz.getPresets.query({ deviceId: parsedId.deviceId })).find((p) => p.name === presetName && isAlexaRecallablePreset(p));
|
|
51043
|
+
if (!preset) return errorEnvelope(header, "INVALID_DIRECTIVE", `preset '${presetName}' is not a recallable preset on device ${parsedId.deviceId}`);
|
|
51379
51044
|
await api.ptz.goToPreset.mutate({
|
|
51380
51045
|
deviceId: parsedId.deviceId,
|
|
51381
51046
|
presetId: preset.id
|
|
@@ -51849,7 +51514,37 @@ function assertDeviceScope(scopes) {
|
|
|
51849
51514
|
function requireDeviceScope(scopes, access) {
|
|
51850
51515
|
if (!scopesAllowDeviceCap(scopes, access)) throw new Error(`directive token not authorized for device ${access}`);
|
|
51851
51516
|
}
|
|
51517
|
+
/**
|
|
51518
|
+
* Give an `Alexa.ErrorResponse` the directive's endpoint when it lacks one.
|
|
51519
|
+
* Amazon requires `endpoint` on every ErrorResponse to an endpoint directive
|
|
51520
|
+
* (alexa-errorresponse); the handlers build their errors from the header
|
|
51521
|
+
* alone, so the router — which holds the endpoint for every path — adds it.
|
|
51522
|
+
*/
|
|
51523
|
+
function withErrorEndpoint(body, endpoint) {
|
|
51524
|
+
const endpointId = typeof endpoint === "object" && endpoint !== null && typeof endpoint.endpointId === "string" ? endpoint.endpointId : null;
|
|
51525
|
+
if (endpointId === null || typeof body !== "object" || body === null) return body;
|
|
51526
|
+
const event = body.event;
|
|
51527
|
+
if (typeof event !== "object" || event === null) return body;
|
|
51528
|
+
const ev = event;
|
|
51529
|
+
if (ev.header?.namespace !== "Alexa" || ev.header?.name !== "ErrorResponse") return body;
|
|
51530
|
+
if (ev.endpoint !== void 0) return body;
|
|
51531
|
+
return {
|
|
51532
|
+
...body,
|
|
51533
|
+
event: {
|
|
51534
|
+
...ev,
|
|
51535
|
+
endpoint: { endpointId }
|
|
51536
|
+
}
|
|
51537
|
+
};
|
|
51538
|
+
}
|
|
51852
51539
|
async function dispatchDirective(rawBody, deps, callerIdentity) {
|
|
51540
|
+
const result = await dispatchParsedDirective(rawBody, deps, callerIdentity);
|
|
51541
|
+
const endpoint = rawBody?.directive?.endpoint;
|
|
51542
|
+
return {
|
|
51543
|
+
status: result.status,
|
|
51544
|
+
body: withErrorEndpoint(result.body, endpoint)
|
|
51545
|
+
};
|
|
51546
|
+
}
|
|
51547
|
+
async function dispatchParsedDirective(rawBody, deps, callerIdentity) {
|
|
51853
51548
|
const parsed = DirectiveRequestSchema.safeParse(rawBody);
|
|
51854
51549
|
if (!parsed.success) return {
|
|
51855
51550
|
status: 400,
|
|
@@ -51871,6 +51566,7 @@ async function dispatchDirective(rawBody, deps, callerIdentity) {
|
|
|
51871
51566
|
"Alexa.Discovery/Discover": "view",
|
|
51872
51567
|
"Alexa.CameraStreamController/InitializeCameraStreams": "view",
|
|
51873
51568
|
"Alexa.RTCSessionController/InitiateSessionWithOffer": "view",
|
|
51569
|
+
"Alexa.RTCSessionController/SessionConnected": "view",
|
|
51874
51570
|
"Alexa.RTCSessionController/SessionDisconnected": "view",
|
|
51875
51571
|
"Alexa.PowerController/TurnOn": "create",
|
|
51876
51572
|
"Alexa.PowerController/TurnOff": "create",
|
|
@@ -51905,6 +51601,10 @@ async function dispatchDirective(rawBody, deps, callerIdentity) {
|
|
|
51905
51601
|
status: 200,
|
|
51906
51602
|
body: await handleInitiateSessionWithOffer(header, endpoint, payload, deps.rtcSession, callerIdentity)
|
|
51907
51603
|
};
|
|
51604
|
+
if (key === "Alexa.RTCSessionController/SessionConnected") return {
|
|
51605
|
+
status: 200,
|
|
51606
|
+
body: handleSessionConnected(header, endpoint, payload, deps.rtcSession)
|
|
51607
|
+
};
|
|
51908
51608
|
if (key === "Alexa.RTCSessionController/SessionDisconnected") return {
|
|
51909
51609
|
status: 200,
|
|
51910
51610
|
body: await handleSessionDisconnected(header, endpoint, payload, deps.rtcSession, callerIdentity)
|
|
@@ -52411,10 +52111,12 @@ var DEFAULT_MOTION_CLEAR_HOLD_MS = 5e3;
|
|
|
52411
52111
|
var MotionClearHold = class {
|
|
52412
52112
|
entries = /* @__PURE__ */ new Map();
|
|
52413
52113
|
holdMs;
|
|
52114
|
+
minDetectedMs;
|
|
52414
52115
|
setTimer;
|
|
52415
52116
|
clearTimer;
|
|
52416
52117
|
constructor(options) {
|
|
52417
52118
|
this.holdMs = options.holdMs;
|
|
52119
|
+
this.minDetectedMs = options.minDetectedMs ?? 3e4;
|
|
52418
52120
|
this.setTimer = options.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
52419
52121
|
this.clearTimer = options.clearTimer ?? ((handle) => clearTimeout(handle));
|
|
52420
52122
|
}
|
|
@@ -52432,13 +52134,15 @@ var MotionClearHold = class {
|
|
|
52432
52134
|
if (entry.timer !== null) this.clearTimer(entry.timer);
|
|
52433
52135
|
this.entries.set(key, {
|
|
52434
52136
|
state: "detected",
|
|
52435
|
-
timer: null
|
|
52137
|
+
timer: null,
|
|
52138
|
+
detectedAt: entry.detectedAt
|
|
52436
52139
|
});
|
|
52437
52140
|
return false;
|
|
52438
52141
|
}
|
|
52439
52142
|
this.entries.set(key, {
|
|
52440
52143
|
state: "detected",
|
|
52441
|
-
timer: null
|
|
52144
|
+
timer: null,
|
|
52145
|
+
detectedAt: Date.now()
|
|
52442
52146
|
});
|
|
52443
52147
|
return true;
|
|
52444
52148
|
}
|
|
@@ -52459,16 +52163,21 @@ var MotionClearHold = class {
|
|
|
52459
52163
|
onCleared(key, release) {
|
|
52460
52164
|
const entry = this.entries.get(key);
|
|
52461
52165
|
if (entry !== void 0 && entry.state !== "detected") return;
|
|
52166
|
+
const detectedAt = entry?.detectedAt ?? null;
|
|
52167
|
+
const sinceDetected = detectedAt === null ? 0 : Date.now() - detectedAt;
|
|
52168
|
+
const delay = Math.max(this.holdMs, this.minDetectedMs - sinceDetected);
|
|
52462
52169
|
const timer = this.setTimer(() => {
|
|
52463
52170
|
this.entries.set(key, {
|
|
52464
52171
|
state: "idle",
|
|
52465
|
-
timer: null
|
|
52172
|
+
timer: null,
|
|
52173
|
+
detectedAt: null
|
|
52466
52174
|
});
|
|
52467
52175
|
release();
|
|
52468
|
-
},
|
|
52176
|
+
}, delay);
|
|
52469
52177
|
this.entries.set(key, {
|
|
52470
52178
|
state: "holding",
|
|
52471
|
-
timer
|
|
52179
|
+
timer,
|
|
52180
|
+
detectedAt
|
|
52472
52181
|
});
|
|
52473
52182
|
}
|
|
52474
52183
|
/** What Alexa currently believes for this key — for logs and assertions. */
|
|
@@ -52522,6 +52231,9 @@ var REFRESH_PROACTIVE_WINDOW_MS = 300 * 1e3;
|
|
|
52522
52231
|
var REFRESH_SCAN_INTERVAL_MS = 60 * 1e3;
|
|
52523
52232
|
/** After a failed legacy-grant migration, wait this long before trying again. */
|
|
52524
52233
|
var MIGRATION_RETRY_MS = 300 * 1e3;
|
|
52234
|
+
/** The interface a camera endpoint must declare for a connectivity
|
|
52235
|
+
* ChangeReport to mean anything to the Alexa cloud. */
|
|
52236
|
+
var ENDPOINT_HEALTH_INTERFACE = "Alexa.EndpointHealth";
|
|
52525
52237
|
/** The interface an endpoint must advertise for a `DoorbellPress` to survive
|
|
52526
52238
|
* the Alexa cloud. */
|
|
52527
52239
|
var DOORBELL_INTERFACE = "Alexa.DoorbellEventSource";
|
|
@@ -52553,6 +52265,10 @@ var LwaEventGateway = class {
|
|
|
52553
52265
|
unsubscribe = null;
|
|
52554
52266
|
unsubscribeMotion = null;
|
|
52555
52267
|
unsubscribeBattery = null;
|
|
52268
|
+
unsubscribeConnectivity = [];
|
|
52269
|
+
/** Last connectivity reported per `userId|endpointId` — a camera that
|
|
52270
|
+
* re-announces the same state must not become a ChangeReport stream. */
|
|
52271
|
+
lastConnectivity = /* @__PURE__ */ new Map();
|
|
52556
52272
|
refreshTimer = null;
|
|
52557
52273
|
lastSentAt = /* @__PURE__ */ new Map();
|
|
52558
52274
|
/**
|
|
@@ -52601,6 +52317,11 @@ var LwaEventGateway = class {
|
|
|
52601
52317
|
this.deps.logger.warn("alexa lwa-gateway: battery handler crashed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
52602
52318
|
});
|
|
52603
52319
|
});
|
|
52320
|
+
this.unsubscribeConnectivity = [[EventCategory.DeviceOnline, "OK"], [EventCategory.DeviceOffline, "UNREACHABLE"]].map(([category, connectivity]) => this.deps.ctx.eventBus.subscribe({ category }, (event) => {
|
|
52321
|
+
this.onConnectivityChanged(event.data, connectivity).catch((err) => {
|
|
52322
|
+
this.deps.logger.warn("alexa lwa-gateway: connectivity handler crashed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
52323
|
+
});
|
|
52324
|
+
}));
|
|
52604
52325
|
this.refreshTimer = setInterval(() => {
|
|
52605
52326
|
this.refreshExpiringTokens().catch((err) => {
|
|
52606
52327
|
this.deps.logger.warn("alexa lwa-gateway: token refresh scan crashed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
@@ -52615,6 +52336,9 @@ var LwaEventGateway = class {
|
|
|
52615
52336
|
this.unsubscribeMotion = null;
|
|
52616
52337
|
if (this.unsubscribeBattery) this.unsubscribeBattery();
|
|
52617
52338
|
this.unsubscribeBattery = null;
|
|
52339
|
+
for (const off of this.unsubscribeConnectivity) off();
|
|
52340
|
+
this.unsubscribeConnectivity = [];
|
|
52341
|
+
this.lastConnectivity.clear();
|
|
52618
52342
|
if (this.refreshTimer) clearInterval(this.refreshTimer);
|
|
52619
52343
|
this.refreshTimer = null;
|
|
52620
52344
|
this.lastSentAt.clear();
|
|
@@ -52819,6 +52543,33 @@ var LwaEventGateway = class {
|
|
|
52819
52543
|
}
|
|
52820
52544
|
}
|
|
52821
52545
|
/**
|
|
52546
|
+
* A camera went offline or came back. Amazon requires the `connectivity`
|
|
52547
|
+
* ChangeReport within 3 s of the change (alexa-endpointhealth). Deduped per
|
|
52548
|
+
* account and endpoint, and skipped for an endpoint Alexa is known to hold
|
|
52549
|
+
* WITHOUT `Alexa.EndpointHealth` — the cloud would drop it (D76).
|
|
52550
|
+
*/
|
|
52551
|
+
async onConnectivityChanged(data, connectivity) {
|
|
52552
|
+
if (typeof data?.deviceId !== "number") return;
|
|
52553
|
+
if (!this.deps.listExposed().some((e) => e.deviceId === data.deviceId)) return;
|
|
52554
|
+
const signature = this.deps.getExportedSignature(data.deviceId);
|
|
52555
|
+
if (signature !== null && !signature.includes(ENDPOINT_HEALTH_INTERFACE)) return;
|
|
52556
|
+
const linked = this.deps.listLinkedAccounts();
|
|
52557
|
+
if (linked.length === 0) return;
|
|
52558
|
+
if (!this.hasProactiveCredentials()) return;
|
|
52559
|
+
const endpointId = String(data.deviceId);
|
|
52560
|
+
for (const account of linked) {
|
|
52561
|
+
const key = `${account.userId}|${endpointId}`;
|
|
52562
|
+
if (this.lastConnectivity.get(key) === connectivity) continue;
|
|
52563
|
+
this.lastConnectivity.set(key, connectivity);
|
|
52564
|
+
await this.postEventBody(account.userId, endpointId, (accessToken) => buildConnectivityChangeReportPayload({
|
|
52565
|
+
endpointId,
|
|
52566
|
+
connectivity,
|
|
52567
|
+
timestamp: Date.now(),
|
|
52568
|
+
accessToken
|
|
52569
|
+
}), `connectivity ${connectivity}`);
|
|
52570
|
+
}
|
|
52571
|
+
}
|
|
52572
|
+
/**
|
|
52822
52573
|
* Send a one-shot `DeleteReport` for arbitrary endpoint ids per linked
|
|
52823
52574
|
* account. Used to clean up stale entries Alexa still shows from prior
|
|
52824
52575
|
* syncs (different addon version, different account, or an aborted
|
|
@@ -53139,6 +52890,36 @@ function buildBatteryChangeReportPayload(opts) {
|
|
|
53139
52890
|
context: { properties: [] }
|
|
53140
52891
|
};
|
|
53141
52892
|
}
|
|
52893
|
+
function buildConnectivityChangeReportPayload(opts) {
|
|
52894
|
+
return {
|
|
52895
|
+
event: {
|
|
52896
|
+
header: {
|
|
52897
|
+
namespace: "Alexa",
|
|
52898
|
+
name: "ChangeReport",
|
|
52899
|
+
messageId: (0, node_crypto.randomUUID)(),
|
|
52900
|
+
payloadVersion: "3"
|
|
52901
|
+
},
|
|
52902
|
+
endpoint: {
|
|
52903
|
+
scope: {
|
|
52904
|
+
type: "BearerToken",
|
|
52905
|
+
token: opts.accessToken
|
|
52906
|
+
},
|
|
52907
|
+
endpointId: opts.endpointId
|
|
52908
|
+
},
|
|
52909
|
+
payload: { change: {
|
|
52910
|
+
cause: { type: "PERIODIC_POLL" },
|
|
52911
|
+
properties: [{
|
|
52912
|
+
namespace: "Alexa.EndpointHealth",
|
|
52913
|
+
name: "connectivity",
|
|
52914
|
+
value: { value: opts.connectivity },
|
|
52915
|
+
timeOfSample: new Date(opts.timestamp).toISOString(),
|
|
52916
|
+
uncertaintyInMilliseconds: 0
|
|
52917
|
+
}]
|
|
52918
|
+
} }
|
|
52919
|
+
},
|
|
52920
|
+
context: { properties: [] }
|
|
52921
|
+
};
|
|
52922
|
+
}
|
|
53142
52923
|
function buildSmartVisionObjectDetectionEvent(opts) {
|
|
53143
52924
|
const timeOfSample = new Date(opts.timestamp).toISOString();
|
|
53144
52925
|
return { event: {
|
|
@@ -53307,7 +53088,7 @@ function buildAlexaOauthIntegration(publicHubUrl) {
|
|
|
53307
53088
|
* client id and SECRET, on the theory that a per-account refresh token was the
|
|
53308
53089
|
* real gate. The package is public on npm, so that secret was published with
|
|
53309
53090
|
* every version and has to be rotated. Since D753 the secret lives only in the
|
|
53310
|
-
* skill's Lambda (
|
|
53091
|
+
* skill's Lambda (SSM Parameter Store SecureString, D761); the hub gets short-lived Event Gateway
|
|
53311
53092
|
* tokens from the Lambda's broker (`broker-client.ts`) with a per-link
|
|
53312
53093
|
* credential. `scripts/check-no-client-secrets.ts` keeps it that way.
|
|
53313
53094
|
*
|
|
@@ -54338,7 +54119,7 @@ var ExportAlexaAddon = class extends BaseAddon {
|
|
|
54338
54119
|
content: [
|
|
54339
54120
|
"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).",
|
|
54340
54121
|
"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.",
|
|
54341
|
-
"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
|
|
54122
|
+
"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).",
|
|
54342
54123
|
"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."
|
|
54343
54124
|
].join("\n")
|
|
54344
54125
|
},
|
|
@@ -54405,7 +54186,7 @@ var ExportAlexaAddon = class extends BaseAddon {
|
|
|
54405
54186
|
key: "__custom-skill-info",
|
|
54406
54187
|
label: "Your skill Lambda",
|
|
54407
54188
|
variant: "info",
|
|
54408
|
-
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
|
|
54189
|
+
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."
|
|
54409
54190
|
}] : [{
|
|
54410
54191
|
type: "info",
|
|
54411
54192
|
key: "__public-skill-info",
|