@camstack/addon-export-alexa 1.2.39 → 1.2.40

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.
@@ -7636,6 +7636,111 @@ var CameraSwitchGroupSchema = object({
7636
7636
  fetchedAt: number()
7637
7637
  });
7638
7638
  /**
7639
+ * Per-component log CHANNELS — the gate a hot path consults, and the registry
7640
+ * an addon declares its channels in.
7641
+ *
7642
+ * ## Two axes, deliberately separated
7643
+ *
7644
+ * - **DECLARATION** — which channels exist. Only the addon knows:
7645
+ * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
7646
+ * baichuan/handshake. A hand-wired central list rots at the first addition,
7647
+ * and rots silently. So a channel is declared where it is consulted, and the
7648
+ * `log-channels` capability enumerates the declarations.
7649
+ * - **VALUE** — at which level, for which scope, until when. That stays ONE
7650
+ * thing: the logging settings document on the `system` cap. Two authorities
7651
+ * over the values is the exact defect
7652
+ * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
7653
+ * remove; re-introducing it from the cure side would be grotesque.
7654
+ *
7655
+ * Nothing in this file reads a clock, an env var or a store. The registry is
7656
+ * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
7657
+ * the hot path with a value somebody actually read, and by
7658
+ * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
7659
+ * never reaches here, so it can neither disarm an armed channel nor arm a
7660
+ * disarmed one (D49).
7661
+ *
7662
+ * ## The canonical call shape
7663
+ *
7664
+ * ```ts
7665
+ * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
7666
+ * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
7667
+ * }
7668
+ * ```
7669
+ *
7670
+ * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
7671
+ * read. Disarmed, a call site costs one load and one branch, and the `extras`
7672
+ * object literal is never constructed because it lives inside the branch. It
7673
+ * is the same shape already proven in production at `stream-broker.ts:1650`,
7674
+ * and the same discipline `LoggingGate.allowsDestination` uses for the
7675
+ * destination floor (measured at 1.93 ns/call when off).
7676
+ *
7677
+ * ## Why a channel emits at `info`
7678
+ *
7679
+ * `loki-logging.addon.ts` pins the destination default at `info` and
7680
+ * `loki-destination.ts` drops everything below it, so a line emitted at
7681
+ * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
7682
+ * minutes. A diagnostic that cannot be read an hour later is worse than no
7683
+ * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
7684
+ * emits at the channel's declared level, whose schema floor is `info`.
7685
+ */
7686
+ /**
7687
+ * The level a channel writes at once armed.
7688
+ *
7689
+ * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
7690
+ * not leave the process for Loki, and the whole point of arming a channel is
7691
+ * to read it later.
7692
+ */
7693
+ var LogChannelLevelSchema = _enum([
7694
+ "info",
7695
+ "warn",
7696
+ "error"
7697
+ ]);
7698
+ /**
7699
+ * What an addon declares about one channel. No value, no state — a
7700
+ * declaration is inert.
7701
+ */
7702
+ var LogChannelDescriptorSchema = object({
7703
+ /**
7704
+ * Dotted `area.thing`, unique across the workspace. `area` is conventionally
7705
+ * the addon's short name so an operator reading a channel list can tell who
7706
+ * owns it without a second lookup.
7707
+ */
7708
+ name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
7709
+ /** One sentence: what the operator will SEE after arming it. */
7710
+ description: string().min(1),
7711
+ /** The level its lines are emitted at. Never below `info`. */
7712
+ defaultLevel: LogChannelLevelSchema,
7713
+ /**
7714
+ * Whether this channel can be narrowed to a camera.
7715
+ *
7716
+ * `true` is a PROMISE with two halves, and both must hold: the gate is
7717
+ * consulted with the numeric device id, AND every line the channel admits
7718
+ * carries `tags: { deviceId }` with that same numeric id. The second half is
7719
+ * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
7720
+ * keeps `deviceId` out of the stream labels for cardinality, so the tag in
7721
+ * the body is the only way to filter.
7722
+ *
7723
+ * A channel whose lines carry the device only in `meta` (or not at all) is
7724
+ * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
7725
+ * the operator narrows to one camera, sees nothing, and concludes the code
7726
+ * path was never taken.
7727
+ */
7728
+ perDevice: boolean()
7729
+ });
7730
+ /**
7731
+ * An armed window over one channel, as the document hands it to a mirror.
7732
+ *
7733
+ * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
7734
+ * expires by itself, which is the one failure a boolean cannot avoid.
7735
+ */
7736
+ var LogChannelWindowSchema = object({
7737
+ channel: string().min(1),
7738
+ /** Epoch ms the window closes at. */
7739
+ armedUntilMs: number(),
7740
+ /** `null` = every camera. A non-empty list narrows to those numeric ids. */
7741
+ deviceIds: array(number().int()).readonly().nullable()
7742
+ });
7743
+ /**
7639
7744
  * Ops-log — the durable, append-only operations audit shared by the
7640
7745
  * recordings and events management surfaces.
7641
7746
  *
@@ -11256,6 +11361,35 @@ var MutationFilterSchema = object({
11256
11361
  whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11257
11362
  whereNot: record(string(), unknown()).optional()
11258
11363
  });
11364
+ /**
11365
+ * One scalar an {@link settingsStoreCapability.methods.aggregate} call asks for.
11366
+ *
11367
+ * `as` names the slot in the result, so the SAME column may be asked twice with
11368
+ * two operations (`MIN(startMs)` and `MAX(startMs)` in one round trip) — which
11369
+ * a `Record<column, op>` shape could not express.
11370
+ */
11371
+ var AggregateFieldSchema = object({
11372
+ /** Result key. */
11373
+ as: string().min(1),
11374
+ /** Column to aggregate. Must be a real column of a declared collection. */
11375
+ field: string().min(1),
11376
+ op: _enum([
11377
+ "sum",
11378
+ "min",
11379
+ "max"
11380
+ ])
11381
+ });
11382
+ /**
11383
+ * `COUNT(*)` plus one number per requested field.
11384
+ *
11385
+ * `null` means NO ROW MATCHED, never zero: a `SUM` over an empty set and a sum
11386
+ * that really is 0 are different facts, and an accounting caller that renders
11387
+ * "0 bytes, oldest = 0" for "nothing here" reports a lie about a disk.
11388
+ */
11389
+ var AggregateResultSchema = object({
11390
+ count: number().int(),
11391
+ values: record(string(), number().nullable())
11392
+ });
11259
11393
  /** A single stored record: `{ id, data }`. */
11260
11394
  var SettingsRecordSchema = object({
11261
11395
  id: string(),
@@ -11340,6 +11474,11 @@ method(object({
11340
11474
  collection: string(),
11341
11475
  filter: QueryFilterSchema.optional()
11342
11476
  }), number()), method(object({
11477
+ namespace: string().optional(),
11478
+ collection: string(),
11479
+ fields: array(AggregateFieldSchema).readonly(),
11480
+ filter: QueryFilterSchema.optional()
11481
+ }), AggregateResultSchema), method(object({
11343
11482
  namespace: string().optional(),
11344
11483
  collection: string(),
11345
11484
  field: string(),
@@ -11456,6 +11595,11 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11456
11595
  collection: string(),
11457
11596
  filter: QueryFilterSchema.optional()
11458
11597
  }), number(), { auth: "admin" }), method(object({
11598
+ namespace: string().optional(),
11599
+ collection: string(),
11600
+ fields: array(AggregateFieldSchema).readonly(),
11601
+ filter: QueryFilterSchema.optional()
11602
+ }), AggregateResultSchema, { auth: "admin" }), method(object({
11459
11603
  namespace: string().optional(),
11460
11604
  collection: string(),
11461
11605
  field: string(),
@@ -12042,24 +12186,6 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
12042
12186
  kind: "mutation",
12043
12187
  auth: "admin"
12044
12188
  });
12045
- /**
12046
- * Device Manager capability — hub-side singleton that unifies device persistence,
12047
- * live registry access, and all management operations into a single tRPC surface.
12048
- *
12049
- * Replaces:
12050
- * - `device-persistence` capability (persistence methods absorbed here)
12051
- * - `device-management.router.ts` (deleted in Phase 2)
12052
- * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
12053
- *
12054
- * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
12055
- * fork into separate processes but never run on remote cluster agents. Therefore:
12056
- * - No nodeId routing needed — this is a pure hub singleton.
12057
- * - The hub's DeviceRegistry is the single source of truth for all live devices.
12058
- * - No shadow registry or cross-node aggregation required.
12059
- *
12060
- * Forked workers register devices back to the hub via `ctx.devices`
12061
- * (DeviceManagerApi → ctx.api.deviceManager.registerDevice), same as today.
12062
- */
12063
12189
  /** One child-placement directive on a container's `childLayout`. Structurally
12064
12190
  * identical to `ChildLayoutEntry` in `device-management.ts` — the cap wire
12065
12191
  * shape for the same field. The child is identified by its re-sync-stable
@@ -12428,7 +12554,7 @@ method(object({
12428
12554
  * it answers today and the caller filters as it already does.
12429
12555
  */
12430
12556
  deviceIds: array(number()).optional()
12431
- }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12557
+ }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ parentDeviceIds: array(number()).max(256) }), record(string(), array(DeviceInfoSchema))), method(object({ deviceId: number() }), object({
12432
12558
  mode: LinkedDevicesModeSchema,
12433
12559
  devices: array(LinkedDeviceSchema)
12434
12560
  })), method(object({ deviceIds: array(number()) }), array(LinkedDevicesForDeviceSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
@@ -13152,6 +13278,59 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
13152
13278
  kind: "mutation",
13153
13279
  auth: "admin"
13154
13280
  });
13281
+ /**
13282
+ * `log-channels` — the capability an addon DECLARES its diagnostic channels
13283
+ * through. It stores nothing.
13284
+ *
13285
+ * ## Why a capability at all, and why this shape
13286
+ *
13287
+ * Which channels exist is knowledge only the addon has: `stream-broker` knows
13288
+ * webrtc/ICE/RTP, `provider-reolink` knows baichuan/handshake. A central list
13289
+ * maintained by hand rots at the first addition and rots INVISIBLY — nothing
13290
+ * fails, an operator just never sees the channel somebody added. So the list
13291
+ * is assembled from declarations at runtime.
13292
+ *
13293
+ * The shape is copied from `log-destination.cap.ts`, which already does
13294
+ * exactly this job: `mode: 'collection'`, `internal: true`,
13295
+ * `mount: { kind: 'skip' }` — no tRPC route, no generated hooks — while
13296
+ * `addons.listCapabilityProviders` still enumerates it, and the hub's
13297
+ * `CapabilityRegistry` still holds an RPC proxy per provider so a forked
13298
+ * runner's declarations reach hub-main over the transport that already exists.
13299
+ * No new UDS message, no second registry.
13300
+ *
13301
+ * ## What it deliberately does NOT own
13302
+ *
13303
+ * The VALUES — which channel is armed, for which cameras, until when — live in
13304
+ * ONE place: the logging settings document on the `system` cap
13305
+ * (`getLoggingSettings` / `setLoggingSettings`). Two authorities over the same
13306
+ * value is the defect the plan behind this work exists to remove, and
13307
+ * `setRequestCensus` was retired (D245) rather than allowed to be a second
13308
+ * one. {@link logChannelsCapability} therefore has no getter for a level, no
13309
+ * setter for a window and no persistence of any kind.
13310
+ *
13311
+ * ## Why `apply` is here even so
13312
+ *
13313
+ * The gate lives in the addon's PROCESS; the document lives in hub-main. Some
13314
+ * seam has to carry the value from the authority to the mirror, and a channel
13315
+ * that cannot be reached is precisely the dead knob this whole slice exists to
13316
+ * make impossible (D62 — `audioThresholdDbfs`, the HA entities with no source).
13317
+ * `apply` is that seam and nothing more: it writes an in-memory mirror, it
13318
+ * persists nothing, it is never the source of a value, and it is called only
13319
+ * with a set the hub actually read (D49 — a read that fails does not call it
13320
+ * at all, so no channel is silently disarmed by a bad read).
13321
+ */
13322
+ /** What `apply` reports back — enough to log, not enough to be a second state. */
13323
+ var LogChannelApplyResultSchema = object({
13324
+ /** How many declared channels are armed in this process after the call. */
13325
+ armed: number().int().min(0),
13326
+ /**
13327
+ * Names the document armed that this process does not declare. Reported
13328
+ * rather than swallowed: a name here is either a typo or an addon that has
13329
+ * not booted, and both deserve a line instead of silence.
13330
+ */
13331
+ unknown: array(string()).readonly()
13332
+ });
13333
+ method(_void(), array(LogChannelDescriptorSchema).readonly()), method(object({ windows: array(LogChannelWindowSchema).readonly() }), LogChannelApplyResultSchema, { kind: "mutation" });
13155
13334
  var LogLevelSchema = _enum([
13156
13335
  "debug",
13157
13336
  "info",
@@ -26585,10 +26764,11 @@ var DiagnosticIdSchema = _enum(["request-census"]);
26585
26764
  * The layers of the level hierarchy, general → specific. The most specific
26586
26765
  * layer that carries an explicit value wins.
26587
26766
  *
26588
- * `component` is DECLARED and not yet resolvable: the per-component channels
26589
- * are a later slice of the same plan, and a `levelSource` enum that has to
26590
- * grow later would force every consumer of this document to change with it.
26591
- * Nothing returns `component` today.
26767
+ * `component` became RESOLVABLE on 2026-08-27: a component is a declared log
26768
+ * CHANNEL (`stream-broker.webrtc`, `provider-reolink.baichuan`), named by
26769
+ * `scopeComponent`. It was declared-but-dark in the first slice precisely so
26770
+ * that turning it on would not force every consumer of this document to widen
26771
+ * a `levelSource` enum — which is what has now not happened.
26592
26772
  */
26593
26773
  var LoggingScopeKindSchema = _enum([
26594
26774
  "cluster",
@@ -26615,6 +26795,14 @@ var LoggingLevelLayerSchema = object({
26615
26795
  scope: LoggingScopeKindSchema,
26616
26796
  /** The node this layer speaks for; `null` on the cluster layer. */
26617
26797
  nodeId: string().nullable(),
26798
+ /**
26799
+ * The declared channel this layer speaks for; `null` on every layer but
26800
+ * `component`. Never folded into `nodeId`: a component level is CLUSTER-WIDE
26801
+ * by design — the convention this repo settled on is one orchestrator-wide
26802
+ * setting, never per node (D52) — so a component layer that carried a node
26803
+ * would invite a per-node copy of a value that has no per-node meaning.
26804
+ */
26805
+ component: string().nullable(),
26618
26806
  /** Explicitly set here, or `null` when this layer inherits. */
26619
26807
  level: LogLevelSchema$1.nullable()
26620
26808
  });
@@ -26656,6 +26844,49 @@ var DiagnosticWindowPatchSchema = object({
26656
26844
  reportEveryMs: number().int().positive().optional()
26657
26845
  });
26658
26846
  /**
26847
+ * A channel ARMED, as the document reports it.
26848
+ *
26849
+ * `armMs` is not echoed back: what an operator needs to see is the deadline
26850
+ * and the time left, because a diagnostic left running is itself an incident
26851
+ * and "armed for 10 minutes" said an hour ago is not an answer.
26852
+ */
26853
+ var LogChannelWindowStateSchema = object({
26854
+ channel: string(),
26855
+ armed: boolean(),
26856
+ /** Epoch ms the window closes at. 0 when disarmed. */
26857
+ armedUntilMs: number(),
26858
+ /** Ms left before it expires on its own. 0 when disarmed. */
26859
+ remainingMs: number(),
26860
+ /**
26861
+ * The cameras it is narrowed to, or `null` for every camera.
26862
+ *
26863
+ * A channel declared `perDevice: false` can only ever report `null` here:
26864
+ * its lines do not carry `tags: { deviceId }`, so narrowing them would
26865
+ * produce a filter that silently matches nothing. The server REFUSES such a
26866
+ * patch rather than quietly widening it — ignoring the request would teach
26867
+ * the operator that per-camera filtering works on that channel when it does
26868
+ * not.
26869
+ */
26870
+ deviceIds: array(number().int()).readonly().nullable()
26871
+ });
26872
+ /**
26873
+ * `armMs: 0` DISARMS. Same grammar as {@link DiagnosticWindowPatchSchema}, and
26874
+ * for the same reason: a channel is a window with a deadline, never a switch.
26875
+ */
26876
+ var LogChannelWindowPatchSchema = object({
26877
+ channel: string().min(1),
26878
+ armMs: number().int().min(0),
26879
+ /**
26880
+ * Narrow to these numeric device ids. Absent or `null` = every camera.
26881
+ *
26882
+ * Numeric because the repo's own rule makes it possible: every log line
26883
+ * about a device carries `tags: { deviceId }` with the numeric id. That rule
26884
+ * was paid for with a 22% thumbnail gap and a 3-hour media blackout both
26885
+ * diagnosed by hand, and this is the first thing that collects on it.
26886
+ */
26887
+ deviceIds: array(number().int()).readonly().nullable().optional()
26888
+ });
26889
+ /**
26659
26890
  * A PATCH, and patches MERGE.
26660
26891
  *
26661
26892
  * A field absent from the patch is left exactly as it was — arming a
@@ -26674,7 +26905,14 @@ var LoggingSettingsPatchSchema = object({
26674
26905
  * Only the diagnostics NAMED here change. An armed window that is not listed
26675
26906
  * keeps running — a patch is never a full replacement.
26676
26907
  */
26677
- diagnostics: array(DiagnosticWindowPatchSchema).readonly().optional()
26908
+ diagnostics: array(DiagnosticWindowPatchSchema).readonly().optional(),
26909
+ /**
26910
+ * Only the channels NAMED here change. An armed channel that is not listed
26911
+ * keeps running — same rule as `diagnostics`, because a patch that silently
26912
+ * disarmed the channels it did not mention would make the Levels page and
26913
+ * the Diagnostics page fight over the same value.
26914
+ */
26915
+ channels: array(LogChannelWindowPatchSchema).readonly().optional()
26678
26916
  });
26679
26917
  /**
26680
26918
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -26687,9 +26925,22 @@ var LoggingSettingsPatchSchema = object({
26687
26925
  * authority over the whole hierarchy and answers for every layer, so the
26688
26926
  * layer selector needs a name the transport does not already own.
26689
26927
  */
26690
- var GetLoggingSettingsInputSchema = object({ scopeNodeId: string().optional() });
26928
+ var GetLoggingSettingsInputSchema = object({
26929
+ scopeNodeId: string().optional(),
26930
+ /**
26931
+ * The declared CHANNEL this document is addressed at, when the caller wants
26932
+ * the `component` layer. Absent = the node/cluster hierarchy only.
26933
+ *
26934
+ * Naming it separately rather than overloading `scopeNodeId` keeps the two
26935
+ * axes from collapsing: a component level is cluster-wide, a node level is
26936
+ * not, and one selector for both would make "which of these two did I just
26937
+ * set" unanswerable — the exact ambiguity `explicit` exists to remove.
26938
+ */
26939
+ scopeComponent: string().optional()
26940
+ });
26691
26941
  var SetLoggingSettingsInputSchema = object({
26692
26942
  scopeNodeId: string().optional(),
26943
+ scopeComponent: string().optional(),
26693
26944
  patch: LoggingSettingsPatchSchema
26694
26945
  });
26695
26946
  /**
@@ -26704,9 +26955,20 @@ var SetLoggingSettingsInputSchema = object({
26704
26955
  var LoggingSettingsStateSchema = object({
26705
26956
  /** The layer this document was read at. `null` = the cluster layer. */
26706
26957
  scopeNodeId: string().nullable(),
26958
+ /** The channel this document was read at. `null` = no component layer. */
26959
+ scopeComponent: string().nullable(),
26707
26960
  effective: LoggingEffectiveSchema,
26708
26961
  explicit: LoggingExplicitSchema,
26709
26962
  activeWindows: array(DiagnosticWindowSchema).readonly(),
26963
+ /**
26964
+ * Every channel the cluster's addons DECLARE, gathered from the
26965
+ * `log-channels` providers. Not stored anywhere: assembled per read, so a
26966
+ * channel added by a redeployed addon appears without anybody editing a
26967
+ * list, and a channel whose addon is gone stops being offered.
26968
+ */
26969
+ channels: array(LogChannelDescriptorSchema).readonly(),
26970
+ /** The channels ARMED right now, each with its deadline. */
26971
+ activeChannels: array(LogChannelWindowStateSchema).readonly(),
26710
26972
  persisted: boolean()
26711
26973
  });
26712
26974
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
@@ -28283,6 +28545,12 @@ Object.freeze({
28283
28545
  addonId: null,
28284
28546
  access: "view"
28285
28547
  },
28548
+ "dataStoreProvider.aggregate": {
28549
+ capName: "data-store-provider",
28550
+ capScope: "system",
28551
+ addonId: null,
28552
+ access: "view"
28553
+ },
28286
28554
  "dataStoreProvider.count": {
28287
28555
  capName: "data-store-provider",
28288
28556
  capScope: "system",
@@ -28697,6 +28965,12 @@ Object.freeze({
28697
28965
  addonId: null,
28698
28966
  access: "view"
28699
28967
  },
28968
+ "deviceManager.getChildrenBatch": {
28969
+ capName: "device-manager",
28970
+ capScope: "system",
28971
+ addonId: null,
28972
+ access: "view"
28973
+ },
28700
28974
  "deviceManager.getConfigSchema": {
28701
28975
  capName: "device-manager",
28702
28976
  capScope: "system",
@@ -29747,6 +30021,18 @@ Object.freeze({
29747
30021
  addonId: null,
29748
30022
  access: "create"
29749
30023
  },
30024
+ "logChannels.apply": {
30025
+ capName: "log-channels",
30026
+ capScope: "system",
30027
+ addonId: null,
30028
+ access: "create"
30029
+ },
30030
+ "logChannels.list": {
30031
+ capName: "log-channels",
30032
+ capScope: "system",
30033
+ addonId: null,
30034
+ access: "view"
30035
+ },
29750
30036
  "logDestination.query": {
29751
30037
  capName: "log-destination",
29752
30038
  capScope: "system",
@@ -31901,6 +32187,12 @@ Object.freeze({
31901
32187
  addonId: null,
31902
32188
  access: "create"
31903
32189
  },
32190
+ "settingsStore.aggregate": {
32191
+ capName: "settings-store",
32192
+ capScope: "system",
32193
+ addonId: null,
32194
+ access: "view"
32195
+ },
31904
32196
  "settingsStore.count": {
31905
32197
  capName: "settings-store",
31906
32198
  capScope: "system",
@@ -33480,6 +33772,11 @@ Object.freeze({
33480
33772
  form: "single",
33481
33773
  optional: false
33482
33774
  }],
33775
+ "deviceManager.getChildrenBatch": [{
33776
+ name: "parentDeviceIds",
33777
+ form: "array",
33778
+ optional: false
33779
+ }],
33483
33780
  "deviceManager.getConfigSchema": [{
33484
33781
  name: "deviceId",
33485
33782
  form: "single",
@@ -7609,6 +7609,111 @@ var CameraSwitchGroupSchema = object({
7609
7609
  fetchedAt: number()
7610
7610
  });
7611
7611
  /**
7612
+ * Per-component log CHANNELS — the gate a hot path consults, and the registry
7613
+ * an addon declares its channels in.
7614
+ *
7615
+ * ## Two axes, deliberately separated
7616
+ *
7617
+ * - **DECLARATION** — which channels exist. Only the addon knows:
7618
+ * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
7619
+ * baichuan/handshake. A hand-wired central list rots at the first addition,
7620
+ * and rots silently. So a channel is declared where it is consulted, and the
7621
+ * `log-channels` capability enumerates the declarations.
7622
+ * - **VALUE** — at which level, for which scope, until when. That stays ONE
7623
+ * thing: the logging settings document on the `system` cap. Two authorities
7624
+ * over the values is the exact defect
7625
+ * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
7626
+ * remove; re-introducing it from the cure side would be grotesque.
7627
+ *
7628
+ * Nothing in this file reads a clock, an env var or a store. The registry is
7629
+ * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
7630
+ * the hot path with a value somebody actually read, and by
7631
+ * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
7632
+ * never reaches here, so it can neither disarm an armed channel nor arm a
7633
+ * disarmed one (D49).
7634
+ *
7635
+ * ## The canonical call shape
7636
+ *
7637
+ * ```ts
7638
+ * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
7639
+ * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
7640
+ * }
7641
+ * ```
7642
+ *
7643
+ * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
7644
+ * read. Disarmed, a call site costs one load and one branch, and the `extras`
7645
+ * object literal is never constructed because it lives inside the branch. It
7646
+ * is the same shape already proven in production at `stream-broker.ts:1650`,
7647
+ * and the same discipline `LoggingGate.allowsDestination` uses for the
7648
+ * destination floor (measured at 1.93 ns/call when off).
7649
+ *
7650
+ * ## Why a channel emits at `info`
7651
+ *
7652
+ * `loki-logging.addon.ts` pins the destination default at `info` and
7653
+ * `loki-destination.ts` drops everything below it, so a line emitted at
7654
+ * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
7655
+ * minutes. A diagnostic that cannot be read an hour later is worse than no
7656
+ * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
7657
+ * emits at the channel's declared level, whose schema floor is `info`.
7658
+ */
7659
+ /**
7660
+ * The level a channel writes at once armed.
7661
+ *
7662
+ * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
7663
+ * not leave the process for Loki, and the whole point of arming a channel is
7664
+ * to read it later.
7665
+ */
7666
+ var LogChannelLevelSchema = _enum([
7667
+ "info",
7668
+ "warn",
7669
+ "error"
7670
+ ]);
7671
+ /**
7672
+ * What an addon declares about one channel. No value, no state — a
7673
+ * declaration is inert.
7674
+ */
7675
+ var LogChannelDescriptorSchema = object({
7676
+ /**
7677
+ * Dotted `area.thing`, unique across the workspace. `area` is conventionally
7678
+ * the addon's short name so an operator reading a channel list can tell who
7679
+ * owns it without a second lookup.
7680
+ */
7681
+ name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
7682
+ /** One sentence: what the operator will SEE after arming it. */
7683
+ description: string().min(1),
7684
+ /** The level its lines are emitted at. Never below `info`. */
7685
+ defaultLevel: LogChannelLevelSchema,
7686
+ /**
7687
+ * Whether this channel can be narrowed to a camera.
7688
+ *
7689
+ * `true` is a PROMISE with two halves, and both must hold: the gate is
7690
+ * consulted with the numeric device id, AND every line the channel admits
7691
+ * carries `tags: { deviceId }` with that same numeric id. The second half is
7692
+ * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
7693
+ * keeps `deviceId` out of the stream labels for cardinality, so the tag in
7694
+ * the body is the only way to filter.
7695
+ *
7696
+ * A channel whose lines carry the device only in `meta` (or not at all) is
7697
+ * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
7698
+ * the operator narrows to one camera, sees nothing, and concludes the code
7699
+ * path was never taken.
7700
+ */
7701
+ perDevice: boolean()
7702
+ });
7703
+ /**
7704
+ * An armed window over one channel, as the document hands it to a mirror.
7705
+ *
7706
+ * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
7707
+ * expires by itself, which is the one failure a boolean cannot avoid.
7708
+ */
7709
+ var LogChannelWindowSchema = object({
7710
+ channel: string().min(1),
7711
+ /** Epoch ms the window closes at. */
7712
+ armedUntilMs: number(),
7713
+ /** `null` = every camera. A non-empty list narrows to those numeric ids. */
7714
+ deviceIds: array(number().int()).readonly().nullable()
7715
+ });
7716
+ /**
7612
7717
  * Ops-log — the durable, append-only operations audit shared by the
7613
7718
  * recordings and events management surfaces.
7614
7719
  *
@@ -11229,6 +11334,35 @@ var MutationFilterSchema = object({
11229
11334
  whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11230
11335
  whereNot: record(string(), unknown()).optional()
11231
11336
  });
11337
+ /**
11338
+ * One scalar an {@link settingsStoreCapability.methods.aggregate} call asks for.
11339
+ *
11340
+ * `as` names the slot in the result, so the SAME column may be asked twice with
11341
+ * two operations (`MIN(startMs)` and `MAX(startMs)` in one round trip) — which
11342
+ * a `Record<column, op>` shape could not express.
11343
+ */
11344
+ var AggregateFieldSchema = object({
11345
+ /** Result key. */
11346
+ as: string().min(1),
11347
+ /** Column to aggregate. Must be a real column of a declared collection. */
11348
+ field: string().min(1),
11349
+ op: _enum([
11350
+ "sum",
11351
+ "min",
11352
+ "max"
11353
+ ])
11354
+ });
11355
+ /**
11356
+ * `COUNT(*)` plus one number per requested field.
11357
+ *
11358
+ * `null` means NO ROW MATCHED, never zero: a `SUM` over an empty set and a sum
11359
+ * that really is 0 are different facts, and an accounting caller that renders
11360
+ * "0 bytes, oldest = 0" for "nothing here" reports a lie about a disk.
11361
+ */
11362
+ var AggregateResultSchema = object({
11363
+ count: number().int(),
11364
+ values: record(string(), number().nullable())
11365
+ });
11232
11366
  /** A single stored record: `{ id, data }`. */
11233
11367
  var SettingsRecordSchema = object({
11234
11368
  id: string(),
@@ -11313,6 +11447,11 @@ method(object({
11313
11447
  collection: string(),
11314
11448
  filter: QueryFilterSchema.optional()
11315
11449
  }), number()), method(object({
11450
+ namespace: string().optional(),
11451
+ collection: string(),
11452
+ fields: array(AggregateFieldSchema).readonly(),
11453
+ filter: QueryFilterSchema.optional()
11454
+ }), AggregateResultSchema), method(object({
11316
11455
  namespace: string().optional(),
11317
11456
  collection: string(),
11318
11457
  field: string(),
@@ -11429,6 +11568,11 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11429
11568
  collection: string(),
11430
11569
  filter: QueryFilterSchema.optional()
11431
11570
  }), number(), { auth: "admin" }), method(object({
11571
+ namespace: string().optional(),
11572
+ collection: string(),
11573
+ fields: array(AggregateFieldSchema).readonly(),
11574
+ filter: QueryFilterSchema.optional()
11575
+ }), AggregateResultSchema, { auth: "admin" }), method(object({
11432
11576
  namespace: string().optional(),
11433
11577
  collection: string(),
11434
11578
  field: string(),
@@ -12015,24 +12159,6 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
12015
12159
  kind: "mutation",
12016
12160
  auth: "admin"
12017
12161
  });
12018
- /**
12019
- * Device Manager capability — hub-side singleton that unifies device persistence,
12020
- * live registry access, and all management operations into a single tRPC surface.
12021
- *
12022
- * Replaces:
12023
- * - `device-persistence` capability (persistence methods absorbed here)
12024
- * - `device-management.router.ts` (deleted in Phase 2)
12025
- * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
12026
- *
12027
- * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
12028
- * fork into separate processes but never run on remote cluster agents. Therefore:
12029
- * - No nodeId routing needed — this is a pure hub singleton.
12030
- * - The hub's DeviceRegistry is the single source of truth for all live devices.
12031
- * - No shadow registry or cross-node aggregation required.
12032
- *
12033
- * Forked workers register devices back to the hub via `ctx.devices`
12034
- * (DeviceManagerApi → ctx.api.deviceManager.registerDevice), same as today.
12035
- */
12036
12162
  /** One child-placement directive on a container's `childLayout`. Structurally
12037
12163
  * identical to `ChildLayoutEntry` in `device-management.ts` — the cap wire
12038
12164
  * shape for the same field. The child is identified by its re-sync-stable
@@ -12401,7 +12527,7 @@ method(object({
12401
12527
  * it answers today and the caller filters as it already does.
12402
12528
  */
12403
12529
  deviceIds: array(number()).optional()
12404
- }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12530
+ }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ parentDeviceIds: array(number()).max(256) }), record(string(), array(DeviceInfoSchema))), method(object({ deviceId: number() }), object({
12405
12531
  mode: LinkedDevicesModeSchema,
12406
12532
  devices: array(LinkedDeviceSchema)
12407
12533
  })), method(object({ deviceIds: array(number()) }), array(LinkedDevicesForDeviceSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
@@ -13125,6 +13251,59 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
13125
13251
  kind: "mutation",
13126
13252
  auth: "admin"
13127
13253
  });
13254
+ /**
13255
+ * `log-channels` — the capability an addon DECLARES its diagnostic channels
13256
+ * through. It stores nothing.
13257
+ *
13258
+ * ## Why a capability at all, and why this shape
13259
+ *
13260
+ * Which channels exist is knowledge only the addon has: `stream-broker` knows
13261
+ * webrtc/ICE/RTP, `provider-reolink` knows baichuan/handshake. A central list
13262
+ * maintained by hand rots at the first addition and rots INVISIBLY — nothing
13263
+ * fails, an operator just never sees the channel somebody added. So the list
13264
+ * is assembled from declarations at runtime.
13265
+ *
13266
+ * The shape is copied from `log-destination.cap.ts`, which already does
13267
+ * exactly this job: `mode: 'collection'`, `internal: true`,
13268
+ * `mount: { kind: 'skip' }` — no tRPC route, no generated hooks — while
13269
+ * `addons.listCapabilityProviders` still enumerates it, and the hub's
13270
+ * `CapabilityRegistry` still holds an RPC proxy per provider so a forked
13271
+ * runner's declarations reach hub-main over the transport that already exists.
13272
+ * No new UDS message, no second registry.
13273
+ *
13274
+ * ## What it deliberately does NOT own
13275
+ *
13276
+ * The VALUES — which channel is armed, for which cameras, until when — live in
13277
+ * ONE place: the logging settings document on the `system` cap
13278
+ * (`getLoggingSettings` / `setLoggingSettings`). Two authorities over the same
13279
+ * value is the defect the plan behind this work exists to remove, and
13280
+ * `setRequestCensus` was retired (D245) rather than allowed to be a second
13281
+ * one. {@link logChannelsCapability} therefore has no getter for a level, no
13282
+ * setter for a window and no persistence of any kind.
13283
+ *
13284
+ * ## Why `apply` is here even so
13285
+ *
13286
+ * The gate lives in the addon's PROCESS; the document lives in hub-main. Some
13287
+ * seam has to carry the value from the authority to the mirror, and a channel
13288
+ * that cannot be reached is precisely the dead knob this whole slice exists to
13289
+ * make impossible (D62 — `audioThresholdDbfs`, the HA entities with no source).
13290
+ * `apply` is that seam and nothing more: it writes an in-memory mirror, it
13291
+ * persists nothing, it is never the source of a value, and it is called only
13292
+ * with a set the hub actually read (D49 — a read that fails does not call it
13293
+ * at all, so no channel is silently disarmed by a bad read).
13294
+ */
13295
+ /** What `apply` reports back — enough to log, not enough to be a second state. */
13296
+ var LogChannelApplyResultSchema = object({
13297
+ /** How many declared channels are armed in this process after the call. */
13298
+ armed: number().int().min(0),
13299
+ /**
13300
+ * Names the document armed that this process does not declare. Reported
13301
+ * rather than swallowed: a name here is either a typo or an addon that has
13302
+ * not booted, and both deserve a line instead of silence.
13303
+ */
13304
+ unknown: array(string()).readonly()
13305
+ });
13306
+ method(_void(), array(LogChannelDescriptorSchema).readonly()), method(object({ windows: array(LogChannelWindowSchema).readonly() }), LogChannelApplyResultSchema, { kind: "mutation" });
13128
13307
  var LogLevelSchema = _enum([
13129
13308
  "debug",
13130
13309
  "info",
@@ -26558,10 +26737,11 @@ var DiagnosticIdSchema = _enum(["request-census"]);
26558
26737
  * The layers of the level hierarchy, general → specific. The most specific
26559
26738
  * layer that carries an explicit value wins.
26560
26739
  *
26561
- * `component` is DECLARED and not yet resolvable: the per-component channels
26562
- * are a later slice of the same plan, and a `levelSource` enum that has to
26563
- * grow later would force every consumer of this document to change with it.
26564
- * Nothing returns `component` today.
26740
+ * `component` became RESOLVABLE on 2026-08-27: a component is a declared log
26741
+ * CHANNEL (`stream-broker.webrtc`, `provider-reolink.baichuan`), named by
26742
+ * `scopeComponent`. It was declared-but-dark in the first slice precisely so
26743
+ * that turning it on would not force every consumer of this document to widen
26744
+ * a `levelSource` enum — which is what has now not happened.
26565
26745
  */
26566
26746
  var LoggingScopeKindSchema = _enum([
26567
26747
  "cluster",
@@ -26588,6 +26768,14 @@ var LoggingLevelLayerSchema = object({
26588
26768
  scope: LoggingScopeKindSchema,
26589
26769
  /** The node this layer speaks for; `null` on the cluster layer. */
26590
26770
  nodeId: string().nullable(),
26771
+ /**
26772
+ * The declared channel this layer speaks for; `null` on every layer but
26773
+ * `component`. Never folded into `nodeId`: a component level is CLUSTER-WIDE
26774
+ * by design — the convention this repo settled on is one orchestrator-wide
26775
+ * setting, never per node (D52) — so a component layer that carried a node
26776
+ * would invite a per-node copy of a value that has no per-node meaning.
26777
+ */
26778
+ component: string().nullable(),
26591
26779
  /** Explicitly set here, or `null` when this layer inherits. */
26592
26780
  level: LogLevelSchema$1.nullable()
26593
26781
  });
@@ -26629,6 +26817,49 @@ var DiagnosticWindowPatchSchema = object({
26629
26817
  reportEveryMs: number().int().positive().optional()
26630
26818
  });
26631
26819
  /**
26820
+ * A channel ARMED, as the document reports it.
26821
+ *
26822
+ * `armMs` is not echoed back: what an operator needs to see is the deadline
26823
+ * and the time left, because a diagnostic left running is itself an incident
26824
+ * and "armed for 10 minutes" said an hour ago is not an answer.
26825
+ */
26826
+ var LogChannelWindowStateSchema = object({
26827
+ channel: string(),
26828
+ armed: boolean(),
26829
+ /** Epoch ms the window closes at. 0 when disarmed. */
26830
+ armedUntilMs: number(),
26831
+ /** Ms left before it expires on its own. 0 when disarmed. */
26832
+ remainingMs: number(),
26833
+ /**
26834
+ * The cameras it is narrowed to, or `null` for every camera.
26835
+ *
26836
+ * A channel declared `perDevice: false` can only ever report `null` here:
26837
+ * its lines do not carry `tags: { deviceId }`, so narrowing them would
26838
+ * produce a filter that silently matches nothing. The server REFUSES such a
26839
+ * patch rather than quietly widening it — ignoring the request would teach
26840
+ * the operator that per-camera filtering works on that channel when it does
26841
+ * not.
26842
+ */
26843
+ deviceIds: array(number().int()).readonly().nullable()
26844
+ });
26845
+ /**
26846
+ * `armMs: 0` DISARMS. Same grammar as {@link DiagnosticWindowPatchSchema}, and
26847
+ * for the same reason: a channel is a window with a deadline, never a switch.
26848
+ */
26849
+ var LogChannelWindowPatchSchema = object({
26850
+ channel: string().min(1),
26851
+ armMs: number().int().min(0),
26852
+ /**
26853
+ * Narrow to these numeric device ids. Absent or `null` = every camera.
26854
+ *
26855
+ * Numeric because the repo's own rule makes it possible: every log line
26856
+ * about a device carries `tags: { deviceId }` with the numeric id. That rule
26857
+ * was paid for with a 22% thumbnail gap and a 3-hour media blackout both
26858
+ * diagnosed by hand, and this is the first thing that collects on it.
26859
+ */
26860
+ deviceIds: array(number().int()).readonly().nullable().optional()
26861
+ });
26862
+ /**
26632
26863
  * A PATCH, and patches MERGE.
26633
26864
  *
26634
26865
  * A field absent from the patch is left exactly as it was — arming a
@@ -26647,7 +26878,14 @@ var LoggingSettingsPatchSchema = object({
26647
26878
  * Only the diagnostics NAMED here change. An armed window that is not listed
26648
26879
  * keeps running — a patch is never a full replacement.
26649
26880
  */
26650
- diagnostics: array(DiagnosticWindowPatchSchema).readonly().optional()
26881
+ diagnostics: array(DiagnosticWindowPatchSchema).readonly().optional(),
26882
+ /**
26883
+ * Only the channels NAMED here change. An armed channel that is not listed
26884
+ * keeps running — same rule as `diagnostics`, because a patch that silently
26885
+ * disarmed the channels it did not mention would make the Levels page and
26886
+ * the Diagnostics page fight over the same value.
26887
+ */
26888
+ channels: array(LogChannelWindowPatchSchema).readonly().optional()
26651
26889
  });
26652
26890
  /**
26653
26891
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -26660,9 +26898,22 @@ var LoggingSettingsPatchSchema = object({
26660
26898
  * authority over the whole hierarchy and answers for every layer, so the
26661
26899
  * layer selector needs a name the transport does not already own.
26662
26900
  */
26663
- var GetLoggingSettingsInputSchema = object({ scopeNodeId: string().optional() });
26901
+ var GetLoggingSettingsInputSchema = object({
26902
+ scopeNodeId: string().optional(),
26903
+ /**
26904
+ * The declared CHANNEL this document is addressed at, when the caller wants
26905
+ * the `component` layer. Absent = the node/cluster hierarchy only.
26906
+ *
26907
+ * Naming it separately rather than overloading `scopeNodeId` keeps the two
26908
+ * axes from collapsing: a component level is cluster-wide, a node level is
26909
+ * not, and one selector for both would make "which of these two did I just
26910
+ * set" unanswerable — the exact ambiguity `explicit` exists to remove.
26911
+ */
26912
+ scopeComponent: string().optional()
26913
+ });
26664
26914
  var SetLoggingSettingsInputSchema = object({
26665
26915
  scopeNodeId: string().optional(),
26916
+ scopeComponent: string().optional(),
26666
26917
  patch: LoggingSettingsPatchSchema
26667
26918
  });
26668
26919
  /**
@@ -26677,9 +26928,20 @@ var SetLoggingSettingsInputSchema = object({
26677
26928
  var LoggingSettingsStateSchema = object({
26678
26929
  /** The layer this document was read at. `null` = the cluster layer. */
26679
26930
  scopeNodeId: string().nullable(),
26931
+ /** The channel this document was read at. `null` = no component layer. */
26932
+ scopeComponent: string().nullable(),
26680
26933
  effective: LoggingEffectiveSchema,
26681
26934
  explicit: LoggingExplicitSchema,
26682
26935
  activeWindows: array(DiagnosticWindowSchema).readonly(),
26936
+ /**
26937
+ * Every channel the cluster's addons DECLARE, gathered from the
26938
+ * `log-channels` providers. Not stored anywhere: assembled per read, so a
26939
+ * channel added by a redeployed addon appears without anybody editing a
26940
+ * list, and a channel whose addon is gone stops being offered.
26941
+ */
26942
+ channels: array(LogChannelDescriptorSchema).readonly(),
26943
+ /** The channels ARMED right now, each with its deadline. */
26944
+ activeChannels: array(LogChannelWindowStateSchema).readonly(),
26683
26945
  persisted: boolean()
26684
26946
  });
26685
26947
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
@@ -28256,6 +28518,12 @@ Object.freeze({
28256
28518
  addonId: null,
28257
28519
  access: "view"
28258
28520
  },
28521
+ "dataStoreProvider.aggregate": {
28522
+ capName: "data-store-provider",
28523
+ capScope: "system",
28524
+ addonId: null,
28525
+ access: "view"
28526
+ },
28259
28527
  "dataStoreProvider.count": {
28260
28528
  capName: "data-store-provider",
28261
28529
  capScope: "system",
@@ -28670,6 +28938,12 @@ Object.freeze({
28670
28938
  addonId: null,
28671
28939
  access: "view"
28672
28940
  },
28941
+ "deviceManager.getChildrenBatch": {
28942
+ capName: "device-manager",
28943
+ capScope: "system",
28944
+ addonId: null,
28945
+ access: "view"
28946
+ },
28673
28947
  "deviceManager.getConfigSchema": {
28674
28948
  capName: "device-manager",
28675
28949
  capScope: "system",
@@ -29720,6 +29994,18 @@ Object.freeze({
29720
29994
  addonId: null,
29721
29995
  access: "create"
29722
29996
  },
29997
+ "logChannels.apply": {
29998
+ capName: "log-channels",
29999
+ capScope: "system",
30000
+ addonId: null,
30001
+ access: "create"
30002
+ },
30003
+ "logChannels.list": {
30004
+ capName: "log-channels",
30005
+ capScope: "system",
30006
+ addonId: null,
30007
+ access: "view"
30008
+ },
29723
30009
  "logDestination.query": {
29724
30010
  capName: "log-destination",
29725
30011
  capScope: "system",
@@ -31874,6 +32160,12 @@ Object.freeze({
31874
32160
  addonId: null,
31875
32161
  access: "create"
31876
32162
  },
32163
+ "settingsStore.aggregate": {
32164
+ capName: "settings-store",
32165
+ capScope: "system",
32166
+ addonId: null,
32167
+ access: "view"
32168
+ },
31877
32169
  "settingsStore.count": {
31878
32170
  capName: "settings-store",
31879
32171
  capScope: "system",
@@ -33453,6 +33745,11 @@ Object.freeze({
33453
33745
  form: "single",
33454
33746
  optional: false
33455
33747
  }],
33748
+ "deviceManager.getChildrenBatch": [{
33749
+ name: "parentDeviceIds",
33750
+ form: "array",
33751
+ optional: false
33752
+ }],
33456
33753
  "deviceManager.getConfigSchema": [{
33457
33754
  name: "deviceId",
33458
33755
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-export-alexa",
3
- "version": "1.2.39",
3
+ "version": "1.2.40",
4
4
  "description": "Alexa Smart Home Skill export — hub-side OAuth provider + directive handler. The companion AWS Lambda forwards Alexa directives to this addon's /addon/export-alexa/directive endpoint.",
5
5
  "keywords": [
6
6
  "camstack",