@camstack/addon-mqtt-broker 1.2.8 → 1.2.10

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.
@@ -39,7 +39,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
39
39
  var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
40
40
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
41
41
  //#endregion
42
- //#region ../types/dist/event-category-Bz24uP1U.mjs
42
+ //#region ../types/dist/event-category-41fKf-q9.mjs
43
43
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
44
44
  EventCategory["SystemBoot"] = "system.boot";
45
45
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -99,6 +99,26 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
99
99
  */
100
100
  EventCategory["AddonRetryScheduled"] = "addon.retry-scheduled";
101
101
  /**
102
+ * A liveness invariant of this node is false — it has no devices, every
103
+ * camera is offline, or a camera that was recording has stopped producing
104
+ * segments. Emitted by the `liveness-monitor` builtin, once per fault (the
105
+ * `findingId` is stable across ticks), only after its boot grace period.
106
+ * AlertCenter raises a persistent operator-visible alert.
107
+ *
108
+ * It exists because on 2026-08-03 the hub ran four hours with zero devices
109
+ * and zero recordings while every surface stayed quiet.
110
+ *
111
+ * Payload: `{ findingId, severity, title, message, deviceId? }`.
112
+ */
113
+ EventCategory["SystemLivenessFailed"] = "system.liveness-failed";
114
+ /**
115
+ * A previously reported liveness fault is true again. AlertCenter dismisses
116
+ * the matching `SystemLivenessFailed` alert.
117
+ *
118
+ * Payload: `{ findingId }`.
119
+ */
120
+ EventCategory["SystemLivenessRecovered"] = "system.liveness-recovered";
121
+ /**
102
122
  * Monitor is attempting to reload a failed addon NOW. UI uses this
103
123
  * to show a spinner during the retry attempt. Same transient nature
104
124
  * as AddonRetryScheduled.
@@ -7349,14 +7369,7 @@ var RecordingConfigSchema = object({
7349
7369
  * windows only — existing sheets are immutable, and each window's index
7350
7370
  * carries its own tile dims so mixed-preset history renders correctly.
7351
7371
  */
7352
- scrubThumbnails: ScrubThumbnailPresetSchema.optional(),
7353
- /**
7354
- * OPT-IN thumbnail-strip generation for this camera: every keyframe of the
7355
- * low recording saved as a JPEG (the fast-drag scrub depth), a derived
7356
- * cache that eviction reclaims with the footage. Absent/false = no strips
7357
- * are written and scrub reads exact keyframes at every velocity.
7358
- */
7359
- stripsEnabled: boolean().optional()
7372
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7360
7373
  }).strict();
7361
7374
  /**
7362
7375
  * Ops-log — the durable, append-only operations audit shared by the
@@ -7418,7 +7431,7 @@ var OpsLogQueryInputSchema = object({
7418
7431
  /**
7419
7432
  * Entity-relocation job state (storage entity-routing spec, Phase 4).
7420
7433
  *
7421
- * One shape shared by the recorder's `relocateFootage` (segments + strips) and
7434
+ * One shape shared by the recorder's `relocateFootage` (segments) and
7422
7435
  * pipeline-analytics' `relocateMedia` (event media blobs) so the admin Data
7423
7436
  * page renders both movers with one component. Jobs are in-RAM (a restart
7424
7437
  * forgets them — re-running is safe by construction: copy-if-absent, delete
@@ -7440,7 +7453,7 @@ var RelocateJobSchema = object({
7440
7453
  toLocationId: string(),
7441
7454
  /** Scoped device, or null = every device. */
7442
7455
  deviceId: number().nullable(),
7443
- /** What the job moves (owner-addon specific: segments/strips or media). */
7456
+ /** What the job moves (owner-addon specific: segments or media). */
7444
7457
  entities: array(string()),
7445
7458
  filesMoved: number().int(),
7446
7459
  bytesMoved: number().int(),
@@ -7454,7 +7467,7 @@ var RelocateFootageInputSchema = object({
7454
7467
  deviceId: number().optional(),
7455
7468
  fromLocationId: string(),
7456
7469
  toLocationId: string(),
7457
- entities: array(_enum(["segments", "strips"])).optional(),
7470
+ entities: array(_enum(["segments"])).optional(),
7458
7471
  /** Copy throttle in MB/s (default 40) — the drain is a background chore,
7459
7472
  * never allowed to starve live writers. */
7460
7473
  throttleMbps: number().min(1).max(1e3).optional()
@@ -9012,6 +9025,276 @@ var MaskGridDimsSchema = object({
9012
9025
  height: number()
9013
9026
  });
9014
9027
  /**
9028
+ * notification-output — canonical, capability-gated notification delivery.
9029
+ *
9030
+ * Apprise-derived model (see
9031
+ * `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
9032
+ * callers emit ONE canonical `Notification`; each provider declares a
9033
+ * per-kind capability descriptor (`TargetKind`), and the pure degrade
9034
+ * engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
9035
+ * message to what the kind supports — callers never special-case a service.
9036
+ *
9037
+ * DESIGN DECISIONS (locked):
9038
+ * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
9039
+ * `setTargetEnabled`), each provider persisting via the `settings-store`
9040
+ * cap. Rationale: the admin UI needs one uniform surface across the
9041
+ * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
9042
+ * alternative would fork the UI per addon and cannot host the
9043
+ * discovery→adopt flow.
9044
+ * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
9045
+ * the generated cap-mount auto-`concatCollection`-fans them across every
9046
+ * registered provider (notifiers addon + HA addon) so one catalog is
9047
+ * routable. `send` / `testTarget` / CRUD route to ONE provider by the
9048
+ * `addonId` the generated collection router extracts from the call input.
9049
+ * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
9050
+ * transformer) + UDS MsgPack both round-trip typed arrays — already used by
9051
+ * `storage` / `storage-provider` / `recording` caps over the same path. No
9052
+ * base64 fallback needed.
9053
+ *
9054
+ * TODO (deferred, closed-set change — separate decision): add
9055
+ * `providerKind: 'notify'` so notification providers surface on the unified
9056
+ * admin "Integrations" page.
9057
+ */
9058
+ /**
9059
+ * Zentik-derived typed-media enum — the superset across every kind. Each
9060
+ * adapter picks what it supports and the degrade engine filters the rest.
9061
+ */
9062
+ var AttachmentMediaTypeSchema = _enum([
9063
+ "image",
9064
+ "video",
9065
+ "gif",
9066
+ "audio",
9067
+ "icon"
9068
+ ]);
9069
+ /**
9070
+ * A single attachment. Exactly one of `url` (remote source, most adapters
9071
+ * prefer this) or `bytes` (inline source; required for Pushover-style
9072
+ * bytes-only kinds) MUST be present — the degrade engine expresses a
9073
+ * url→bytes fetch as a `needsFetch` directive the adapter executes.
9074
+ */
9075
+ var AttachmentSchema = object({
9076
+ mediaType: AttachmentMediaTypeSchema,
9077
+ url: string().optional(),
9078
+ bytes: _instanceof(Uint8Array).optional(),
9079
+ mime: string().optional(),
9080
+ name: string().optional()
9081
+ }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
9082
+ var NotificationFormatSchema = _enum([
9083
+ "text",
9084
+ "markdown",
9085
+ "html"
9086
+ ]);
9087
+ /**
9088
+ * The CLOSED icon vocabulary an action button may use.
9089
+ *
9090
+ * A closed set, not a free string, and that is the whole point: an arbitrary
9091
+ * icon name is one that ntfy renders, zentik silently drops, and nobody
9092
+ * notices — the same class of gap as a zone vocabulary nothing produced
9093
+ * ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
9094
+ * declares `actionIcons: false` and the degrade engine strips the field.
9095
+ *
9096
+ * Named by INTENT, never by glyph. "check" would tie the vocabulary to one
9097
+ * renderer's icon set; "acknowledge" survives an adapter that draws it
9098
+ * differently.
9099
+ */
9100
+ var NotificationActionIconSchema = _enum([
9101
+ "acknowledge",
9102
+ "dismiss",
9103
+ "silence",
9104
+ "view",
9105
+ "play",
9106
+ "open",
9107
+ "close",
9108
+ "lock",
9109
+ "unlock",
9110
+ "arm",
9111
+ "disarm",
9112
+ "light",
9113
+ "alert"
9114
+ ]);
9115
+ /** A single tap-through action button. */
9116
+ var NotificationActionSchema = object({
9117
+ id: string(),
9118
+ label: string(),
9119
+ url: string().optional(),
9120
+ /** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
9121
+ icon: NotificationActionIconSchema.optional(),
9122
+ /**
9123
+ * Renders in a warning style where the notifier supports it.
9124
+ *
9125
+ * A HINT, never a gate. The callback's authority is its token and nothing
9126
+ * else — see `notification-center/action-token.ts` for what that does and
9127
+ * does not buy.
9128
+ */
9129
+ destructive: boolean().optional()
9130
+ });
9131
+ /**
9132
+ * The canonical notification. `body` is the only hard field (Apprise model).
9133
+ * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
9134
+ * NOT a fixed severity enum — each kind declares its own `caps.levels` and
9135
+ * the adapter maps this ordinal onto its native level. `level?` is an
9136
+ * optional kind-native level id (`emergency`, `silent`, …) that overrides
9137
+ * `priority` for that one target.
9138
+ */
9139
+ var NotificationSchema = object({
9140
+ body: string(),
9141
+ title: string().optional(),
9142
+ format: NotificationFormatSchema.default("text"),
9143
+ priority: number().int().min(1).max(5).default(3),
9144
+ level: string().optional(),
9145
+ attachments: array(AttachmentSchema).optional(),
9146
+ clickUrl: string().optional(),
9147
+ actions: array(NotificationActionSchema).optional(),
9148
+ sound: string().optional(),
9149
+ ttl: number().optional(),
9150
+ tag: string().optional(),
9151
+ deviceId: number().optional(),
9152
+ eventId: string().optional(),
9153
+ metadata: record(string(), unknown()).optional()
9154
+ });
9155
+ /** One declared native severity/priority level for a kind. */
9156
+ var TargetKindLevelSchema = object({
9157
+ id: string(),
9158
+ label: string(),
9159
+ /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
9160
+ ordinal: number().int().min(1).max(5).nullable(),
9161
+ flags: object({
9162
+ critical: boolean().optional(),
9163
+ silent: boolean().optional(),
9164
+ noPush: boolean().optional()
9165
+ }).optional(),
9166
+ /** e.g. Pushover `emergency` requires `retry` / `expire`. */
9167
+ requires: array(string()).optional(),
9168
+ description: string().optional()
9169
+ });
9170
+ /** The full capability block consulted before dispatch. */
9171
+ var TargetKindCapsSchema = object({
9172
+ attachments: object({
9173
+ mediaTypes: array(AttachmentMediaTypeSchema),
9174
+ mode: _enum([
9175
+ "url",
9176
+ "bytes",
9177
+ "both"
9178
+ ]),
9179
+ max: number().int().nonnegative(),
9180
+ maxBytes: number().int().positive().optional()
9181
+ }),
9182
+ /** Max action buttons (0 = none). */
9183
+ actions: number().int().nonnegative(),
9184
+ /**
9185
+ * Whether this kind renders a per-action ICON.
9186
+ *
9187
+ * `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
9188
+ * run on the addon cap path — three production failures in one day taught
9189
+ * this repo that once. Absent is read as false by the degrade engine, which
9190
+ * is the safe direction: an icon that is not rendered costs nothing, an icon
9191
+ * assumed and dropped costs the operator's trust in the field.
9192
+ */
9193
+ actionIcons: boolean().optional(),
9194
+ levels: array(TargetKindLevelSchema),
9195
+ format: array(NotificationFormatSchema),
9196
+ clickUrl: boolean(),
9197
+ sound: boolean(),
9198
+ ttl: boolean(),
9199
+ bodyMaxLen: number().int().positive()
9200
+ });
9201
+ /**
9202
+ * `configSchema` is a `ConfigUISchema` tree passed through to the admin
9203
+ * FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
9204
+ * `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
9205
+ * the union is large and not meant for runtime validation here; the exported
9206
+ * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
9207
+ */
9208
+ var ConfigSchemaPassthrough$1 = unknown();
9209
+ var TargetKindSchema = object({
9210
+ kind: string(),
9211
+ label: string(),
9212
+ icon: string(),
9213
+ /** Stamped by each provider so the concat-fanned catalog stays routable. */
9214
+ addonId: string(),
9215
+ /**
9216
+ * URL of the kind's bundled BRAND icon, served by the providing addon over
9217
+ * its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
9218
+ * when the addon bundles no icon for that kind — the client then falls back
9219
+ * to a neutral glyph rather than rendering the raw `icon` NAME as text.
9220
+ *
9221
+ * Root-relative on purpose: it resolves against whatever origin serves a web
9222
+ * client, and a native client joins it onto its own hub base.
9223
+ *
9224
+ * DECLARED here deliberately. It used to travel as an undeclared passthrough
9225
+ * field that survived only because the runtime cap-router forwards provider
9226
+ * output verbatim — so every consumer had to re-declare it by hand to stop
9227
+ * its own Zod parse from stripping it, and the whole arrangement would have
9228
+ * broken silently the moment output validation was tightened anywhere.
9229
+ */
9230
+ iconUrl: string().optional(),
9231
+ /**
9232
+ * Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
9233
+ *
9234
+ * The server knows this and therefore says it, because the client cannot
9235
+ * safely guess: a React-Native client renders SVG and raster through two
9236
+ * DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
9237
+ * not decode SVG on iOS/Android), so without this it silently fell back to a
9238
+ * placeholder glyph for every vector icon while the web build looked fine.
9239
+ *
9240
+ * Absent when {@link iconUrl} is absent, or for a legacy provider that has
9241
+ * not been updated — a client that cannot determine the type should prefer
9242
+ * its raster path, which is the safe default for an unknown image.
9243
+ */
9244
+ iconMediaType: string().optional(),
9245
+ configSchema: ConfigSchemaPassthrough$1,
9246
+ supportsDiscovery: boolean(),
9247
+ caps: TargetKindCapsSchema
9248
+ });
9249
+ /**
9250
+ * A persisted target. `config` holds secrets; providers REDACT secret fields
9251
+ * (return a presence marker only) when serving `listTargets` — never
9252
+ * round-trip a stored secret to the UI.
9253
+ */
9254
+ var TargetSchema = object({
9255
+ id: string(),
9256
+ name: string(),
9257
+ kind: string(),
9258
+ addonId: string(),
9259
+ enabled: boolean(),
9260
+ config: record(string(), unknown())
9261
+ });
9262
+ /** A discovery-surfaced candidate (config is partial + non-secret). */
9263
+ var DiscoveredTargetSchema = object({
9264
+ kind: string(),
9265
+ suggestedName: string(),
9266
+ config: record(string(), unknown())
9267
+ });
9268
+ /** The degrade engine's report — what was resolved / dropped / degraded. */
9269
+ var RenderedAsSchema = object({
9270
+ level: string(),
9271
+ format: NotificationFormatSchema,
9272
+ attachmentsSent: number().int().nonnegative(),
9273
+ actionsSent: number().int().nonnegative(),
9274
+ truncated: boolean(),
9275
+ dropped: array(string())
9276
+ });
9277
+ var SendResultSchema = object({
9278
+ success: boolean(),
9279
+ error: string().optional(),
9280
+ renderedAs: RenderedAsSchema.optional()
9281
+ });
9282
+ /** Same shape as SendResult — kept as a distinct name for the test panel. */
9283
+ var TestResultSchema = SendResultSchema;
9284
+ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema)), method(object({
9285
+ kind: string(),
9286
+ config: record(string(), unknown()).optional()
9287
+ }), array(DiscoveredTargetSchema)), method(object({
9288
+ targetId: string(),
9289
+ notification: NotificationSchema
9290
+ }), SendResultSchema, { kind: "mutation" }), method(object({
9291
+ targetId: string(),
9292
+ sample: NotificationSchema.optional()
9293
+ }), TestResultSchema, { kind: "mutation" }), method(object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }), method(object({ targetId: string() }), _void(), { kind: "mutation" }), method(object({
9294
+ targetId: string(),
9295
+ enabled: boolean()
9296
+ }), _void(), { kind: "mutation" });
9297
+ /**
9015
9298
  * notification-rules — the Notification Center rule surface (P1 core).
9016
9299
  *
9017
9300
  * Spec: `docs/superpowers/specs/2026-07-22-notification-center-requirements.md`
@@ -9141,7 +9424,115 @@ var NcZoneConditionSchema = object({
9141
9424
  * The P1 condition set — a flat AND of groups; absent group = pass;
9142
9425
  * membership lists are OR within the list (spec §2.3).
9143
9426
  */
9427
+ /**
9428
+ * What a rule may actuate.
9429
+ *
9430
+ * **No hand-maintained allowlist** (operator decision, and the right one — a
9431
+ * written list of methods is a third parallel map to keep aligned, and this
9432
+ * repo has paid for those). The boundary instead comes from a property the
9433
+ * capabilities already carry: an action may target only a **device-scoped**
9434
+ * capability method.
9435
+ *
9436
+ * That is not decoration. A rule can be authored by a NON-ADMIN — personal
9437
+ * rules are a supported flow — and the executor runs with the addon's
9438
+ * privileges, so an unbounded action is an arbitrary RPC channel with a
9439
+ * privilege escalation attached. Restricting to device scope excludes the
9440
+ * system caps (`device-manager.removeDevice` and friends) by construction,
9441
+ * costs nothing to maintain, and cannot rot: a cap that stops being
9442
+ * device-scoped stops being actuatable in the same change.
9443
+ *
9444
+ * The executor enforces it; {@link NcRuleActionSchema} carries the intent.
9445
+ */
9446
+ /**
9447
+ * One step of a sequence.
9448
+ *
9449
+ * `wait` is a first-class step rather than a property of the next action: it is
9450
+ * what makes a sequence a SEQUENCE and not a list — "unlock, wait 5s, open"
9451
+ * cannot be expressed otherwise.
9452
+ */
9453
+ var NcRuleActionSchema = discriminatedUnion("kind", [object({
9454
+ kind: literal("wait"),
9455
+ seconds: number().min(0).max(300)
9456
+ }), object({
9457
+ kind: literal("cap"),
9458
+ deviceId: number().int(),
9459
+ /** Capability name, e.g. `alarm-panel`. */
9460
+ cap: string().min(1),
9461
+ /** Method on it. The executor refuses a non-device-scoped cap. */
9462
+ method: string().min(1),
9463
+ /** Method arguments, minus `deviceId` (the executor injects it). */
9464
+ args: record(string(), unknown()).optional()
9465
+ })]);
9466
+ /**
9467
+ * A named, ordered run of steps with its own throttle.
9468
+ *
9469
+ * `minDelaySec` exists because a noisy rule otherwise hammers a physical
9470
+ * actuator — the rule's own cooldown governs NOTIFICATIONS, which is a
9471
+ * different budget from "how often may this gate actually open".
9472
+ */
9473
+ var NcRuleActionSequenceSchema = object({
9474
+ name: string().min(1).max(120),
9475
+ enabled: boolean(),
9476
+ minDelaySec: number().int().min(0).max(86400).optional(),
9477
+ actions: array(NcRuleActionSchema).min(1)
9478
+ });
9479
+ /**
9480
+ * One button carried by the notification, running a named sequence on tap.
9481
+ *
9482
+ * **Read this before adding a button that does something physical.** The tap
9483
+ * arrives over a link that travelled through third-party infrastructure — ntfy,
9484
+ * a push relay, whatever forwarded the message — and the callback's ONLY
9485
+ * authority is the token in that link: single-use, short-lived, bound to this
9486
+ * one action of this one notification. It does not identify who tapped.
9487
+ * Whoever holds the notification can run the button, once, inside the window.
9488
+ * That is the operator's explicit choice (2026-08-05), and `destructive` is a
9489
+ * rendering hint, not a second gate. [D47](decisions/adr-0047.md).
9490
+ */
9491
+ var NcRuleNotificationButtonSchema = object({
9492
+ /** Stable id — travels in the callback and identifies the button in logs. */
9493
+ id: string().min(1).max(64),
9494
+ label: string().min(1).max(40),
9495
+ /** Name of a sequence in `onTrigger`. The dispatcher drops a button whose
9496
+ * sequence does not exist rather than minting a token for nothing. */
9497
+ sequence: string().min(1).max(120),
9498
+ icon: NotificationActionIconSchema.optional(),
9499
+ destructive: boolean().optional()
9500
+ });
9501
+ /**
9502
+ * Sequences a rule runs, by hook point.
9503
+ *
9504
+ * ONLY `onTrigger` is here, deliberately. The reference also has activation /
9505
+ * deactivation / reset / post-generation hooks, and they are wanted — but this
9506
+ * repo's expensive failure mode is declaring a surface nothing produces, so a
9507
+ * hook appears here in the same change that produces its edge, never before.
9508
+ */
9509
+ var NcRuleActionsSchema = object({
9510
+ /** Runs when the rule MATCHES. */
9511
+ onTrigger: array(NcRuleActionSequenceSchema).optional(),
9512
+ /**
9513
+ * Buttons the NOTIFICATION carries, each running one of this rule's
9514
+ * sequences when tapped.
9515
+ *
9516
+ * Deliberately a REFERENCE to a sequence rather than a second place to
9517
+ * author steps. A button that could define its own actions would be a
9518
+ * parallel actuation vocabulary — the executor's device-scope check, the
9519
+ * stop-at-first-failure rule and the per-sequence throttle all live on
9520
+ * sequences, and a second authoring surface would drift from every one of
9521
+ * them.
9522
+ *
9523
+ * A sequence reachable ONLY by a button simply appears in `onTrigger` with
9524
+ * `enabled: false`: it is then authored, throttled and validated like the
9525
+ * rest, and nothing runs it automatically.
9526
+ */
9527
+ buttons: array(NcRuleNotificationButtonSchema).max(8).optional()
9528
+ });
9144
9529
  var NcConditionsSchema = object({
9530
+ /** Gate on ANOTHER device's current state (the alarm armed, a switch on). */
9531
+ deviceState: object({
9532
+ deviceId: number().int(),
9533
+ /** Any of these matches. */
9534
+ states: array(string().min(1)).min(1)
9535
+ }).optional(),
9145
9536
  /** Device scope — absent = all devices. */
9146
9537
  devices: array(number()).optional(),
9147
9538
  /** Detector class names (any overlap with the record's class set). */
@@ -9342,6 +9733,14 @@ var NcMediaPolicySchema = object({
9342
9733
  clipPreRollSec: number().int().min(0).max(30).optional(),
9343
9734
  clipPostRollSec: number().int().min(0).max(30).optional(),
9344
9735
  /**
9736
+ * Playback rate of the attached gif / clip. Absent = 2x.
9737
+ *
9738
+ * A notification clip is GLANCED at on a lock screen, not watched: at real
9739
+ * time an eight-second passage is eight seconds of the recipient's attention
9740
+ * and twice the bytes. 1 is real time for the operator who wants it.
9741
+ */
9742
+ clipSpeed: number().min(1).max(8).optional(),
9743
+ /**
9345
9744
  * Which stream profile the footage is cut from. Absent = the CHEAPEST
9346
9745
  * assigned profile: a notification is watched on a phone, so the 4K
9347
9746
  * rendition would burn CPU to produce a file the client downscales anyway.
@@ -9413,7 +9812,30 @@ var NcRuleInputSchema = object({
9413
9812
  * behaviour, visible to all, read-only in the viewer). Present = personal
9414
9813
  * rule owned by this userId. Server-stamped; never trusted from a client.
9415
9814
  */
9416
- ownerUserId: string().optional()
9815
+ ownerUserId: string().optional(),
9816
+ /**
9817
+ * May a non-admin snooze this rule for EVERYONE, not just themselves?
9818
+ *
9819
+ * A snooze is personal by default — it silences the person who set it. This
9820
+ * opts THIS rule into the "the gardener is here all afternoon" case, where
9821
+ * silencing the camera for the whole household is legitimate. It silences
9822
+ * other people, so it is off unless a rule deliberately allows it.
9823
+ *
9824
+ * `.optional()`, deliberately NOT `.default()`: a Zod default does not run on
9825
+ * the addon cap path (three production failures in one day), so absent is
9826
+ * read as `false` by {@link canSetGlobal} in the engine. Admins are not bound
9827
+ * by this flag — see the scope rules on that function.
9828
+ */
9829
+ snoozeAllowGlobal: boolean().optional(),
9830
+ /**
9831
+ * Devices this rule ACTUATES — arm the alarm, open a gate, turn on a light.
9832
+ *
9833
+ * This is what makes the rule set the alarm's trigger set without the alarm
9834
+ * being a special case: arming is
9835
+ * `{ cap: 'alarm-panel', method: 'arm', args: { mode: 'away' } }`, the same
9836
+ * shape as every other actuation.
9837
+ */
9838
+ actions: NcRuleActionsSchema.optional()
9417
9839
  });
9418
9840
  /**
9419
9841
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -9484,7 +9906,8 @@ var NcConditionDescriptorSchema = object({
9484
9906
  "packagePhase",
9485
9907
  "crossingSelect",
9486
9908
  "polygonDraw",
9487
- "occupancy"
9909
+ "occupancy",
9910
+ "deviceState"
9488
9911
  ]),
9489
9912
  operator: _enum([
9490
9913
  "in",
@@ -9498,7 +9921,28 @@ var NcConditionDescriptorSchema = object({
9498
9921
  /** Which delivery kinds the condition applies to. */
9499
9922
  appliesTo: array(NcDeliverySchema),
9500
9923
  phase: string(),
9501
- description: string().optional()
9924
+ description: string().optional(),
9925
+ /**
9926
+ * The CHOICES for a single-choice widget (`sourceSelect`, `crossingSelect`,
9927
+ * `packagePhase`, …), served with the descriptor.
9928
+ *
9929
+ * Before this the descriptor said which widget to render and not what to put
9930
+ * in it, so every option list lived in three places: this file's enums, the
9931
+ * admin's `NC_*_OPTIONS` and the viewer's `NC_*_VALUES`. That triple mirror
9932
+ * is the drift that emptied the viewer's rule editor on 2026-08-04 — the app
9933
+ * mirrors the cap by hand, so it is only ever as current as its last build.
9934
+ *
9935
+ * With the options on the wire, a condition of an EXISTING `valueType` costs
9936
+ * zero client changes. Clients keep a local fallback for an older hub that
9937
+ * does not send them; absent here is "use your own list", not "no choices".
9938
+ */
9939
+ options: array(object({
9940
+ /** Written to the rule verbatim. `''` means the ABSENT state. */
9941
+ value: string(),
9942
+ label: string(),
9943
+ /** What THIS choice matches — shown one at a time, under the control. */
9944
+ hint: string().optional()
9945
+ })).readonly().optional()
9502
9946
  });
9503
9947
  /**
9504
9948
  * The delivery lifecycle status of a history row — a straight read of the
@@ -9583,6 +10027,74 @@ var NcHistoryFilterSchema = object({
9583
10027
  until: number().optional(),
9584
10028
  limit: number().int().min(1).max(500).default(100)
9585
10029
  });
10030
+ /**
10031
+ * What a snooze covers. Broader scopes win when several overlap, so one window
10032
+ * leaves ONE digest rather than a rule snooze and a whole-feed snooze both
10033
+ * summarising the same silence.
10034
+ */
10035
+ var NcSnoozeScopeSchema = _enum([
10036
+ "rule",
10037
+ "device",
10038
+ "all"
10039
+ ]);
10040
+ /**
10041
+ * Client-authored snooze. The server stamps `userId`, `startedAt` and
10042
+ * `expiresAt` — a DURATION is sent rather than an instant so a client with a
10043
+ * skewed clock cannot author a window that is already over, or never ends.
10044
+ */
10045
+ var NcSnoozeInputSchema = object({
10046
+ scope: NcSnoozeScopeSchema,
10047
+ /** Required when `scope: 'rule'` — a scoped snooze with no id matches
10048
+ * NOTHING rather than degrading to "everything". */
10049
+ ruleId: string().optional(),
10050
+ /** Required when `scope: 'device'`. */
10051
+ deviceId: number().int().optional(),
10052
+ durationMinutes: number().int().min(1).max(1440),
10053
+ /**
10054
+ * Silence this for EVERY recipient, not just the caller. Permission is
10055
+ * checked server-side (the rule's `snoozeAllowGlobal`, or admin for the
10056
+ * broader scopes). Absent = personal.
10057
+ */
10058
+ global: boolean().optional(),
10059
+ /**
10060
+ * Deliver a summary of what was suppressed when the window ends. Absent =
10061
+ * ON: someone silencing a nuisance camera wants it off, someone silencing a
10062
+ * SECURITY camera wants to know what they missed, and choosing "off" for
10063
+ * everybody is how a snooze becomes an outage. Resolved to a concrete
10064
+ * boolean by the server at create time — never left to a Zod default, which
10065
+ * does not run on the addon cap path.
10066
+ */
10067
+ summary: boolean().optional()
10068
+ });
10069
+ /** A persisted snooze window. */
10070
+ var NcSnoozeSchema = object({
10071
+ id: string(),
10072
+ /** Who set it. Also who it silences, unless `global`. */
10073
+ userId: string(),
10074
+ scope: NcSnoozeScopeSchema,
10075
+ ruleId: string().optional(),
10076
+ deviceId: number().int().optional(),
10077
+ startedAt: number(),
10078
+ /** Exclusive: at exactly this instant the snooze is over. Expiry is a
10079
+ * COMPARISON, not a job — no sweeper can leave the operator silenced. */
10080
+ expiresAt: number(),
10081
+ global: boolean(),
10082
+ summary: boolean(),
10083
+ /** When the end-of-window digest went out. Absent = not sent (yet, or the
10084
+ * window has not closed, or `summary` is false). */
10085
+ digestSentAt: number().optional()
10086
+ });
10087
+ object({
10088
+ snoozeId: string(),
10089
+ targetId: string(),
10090
+ ruleId: string(),
10091
+ ruleName: string(),
10092
+ deviceId: number().int(),
10093
+ /** How many notifications this snooze hid for that pair. */
10094
+ count: number().int(),
10095
+ firstAt: number(),
10096
+ lastAt: number()
10097
+ });
9586
10098
  method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), method(object({ ruleId: string() }), object({ rule: NcRuleSchema.nullable() }), { auth: "admin" }), method(object({ rule: NcRuleInputSchema }), object({ rule: NcRuleSchema }), {
9587
10099
  kind: "mutation",
9588
10100
  auth: "admin",
@@ -9612,7 +10124,13 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
9612
10124
  }), method(object({}), object({
9613
10125
  catalog: array(NcConditionDescriptorSchema),
9614
10126
  taxonomy: NcTaxonomySchema.optional()
9615
- })), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" });
10127
+ })), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" }), method(object({}), object({ snoozes: array(NcSnoozeSchema) }), { caller: "required" }), method(object({ snooze: NcSnoozeInputSchema }), object({ snooze: NcSnoozeSchema }), {
10128
+ kind: "mutation",
10129
+ caller: "required"
10130
+ }), method(object({ snoozeId: string() }), object({ success: literal(true) }), {
10131
+ kind: "mutation",
10132
+ caller: "required"
10133
+ });
9616
10134
  /**
9617
10135
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
9618
10136
  *
@@ -10360,7 +10878,15 @@ method(object({
10360
10878
  format: _enum(["gif", "mp4"]).default("gif"),
10361
10879
  maxWidth: number().int().min(120).max(1920).default(480),
10362
10880
  /** GIF only — MP4 keeps the source cadence. */
10363
- fps: number().int().min(1).max(15).default(5)
10881
+ fps: number().int().min(1).max(15).default(5),
10882
+ /**
10883
+ * Playback rate. A notification clip is GLANCED at on a lock screen,
10884
+ * not watched, so 2x is the default: the recipient sees the whole
10885
+ * passage in half the time and the GIF is half the bytes. `1` is real
10886
+ * time. Applies to MP4 as well — the operator set a speed, not a GIF
10887
+ * speed.
10888
+ */
10889
+ speed: number().min(1).max(8).default(2)
10364
10890
  }), object({
10365
10891
  base64: string(),
10366
10892
  mime: string(),
@@ -14942,6 +15468,20 @@ var GetInputSchema = object({ id: string() });
14942
15468
  var AddInputSchema = object({
14943
15469
  kind: string().min(1),
14944
15470
  name: string().min(1),
15471
+ /**
15472
+ * ADOPT an existing id instead of minting a new one.
15473
+ *
15474
+ * Written the day this cost an outage. A broker's id is not a detail: HA
15475
+ * devices carry it inside their `stableId` (`ha:ha_004:dev:…`), so a broker
15476
+ * lost from config and re-added as `ha_001` leaves every one of its devices
15477
+ * bound to a broker that no longer exists. Re-entering the password under the
15478
+ * ORIGINAL id turns a multi-step device migration back into re-entering a
15479
+ * password.
15480
+ *
15481
+ * A provider MUST refuse an id that is already in use — adopting a live
15482
+ * broker's id would silently take it over.
15483
+ */
15484
+ id: string().min(1).optional(),
14945
15485
  /** Kind-specific settings (e.g. MQTT `{url,username,password}` or HA
14946
15486
  * `{baseUrl,accessToken}`). Validated by the kind-specific provider
14947
15487
  * branch on receipt — invalid shape rejects the add. */
@@ -15154,6 +15694,210 @@ method(object({ codec: string() }), boolean()), method(_void(), object({
15154
15694
  kind: "mutation",
15155
15695
  auth: "admin"
15156
15696
  });
15697
+ /**
15698
+ * Query filter for settings-store collections.
15699
+ */
15700
+ var QueryFilterSchema = object({
15701
+ where: record(string(), unknown()).optional(),
15702
+ whereIn: record(string(), array(unknown())).optional(),
15703
+ whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
15704
+ orderBy: object({
15705
+ field: string(),
15706
+ direction: _enum(["asc", "desc"])
15707
+ }).optional(),
15708
+ limit: number().optional(),
15709
+ offset: number().optional()
15710
+ });
15711
+ /**
15712
+ * The predicate half of a filter, for BULK MUTATIONS.
15713
+ *
15714
+ * Deliberately not `QueryFilterSchema`: `orderBy` / `limit` / `offset` have no
15715
+ * meaning for a statement that rewrites a set, and accepting them would invite
15716
+ * a caller to believe `limit` bounds the damage. Every field is optional here
15717
+ * only so the shape stays composable — the implementation REJECTS a filter
15718
+ * that compiles to no predicate, because that is the whole collection.
15719
+ */
15720
+ var MutationFilterSchema = object({
15721
+ where: record(string(), unknown()).optional(),
15722
+ whereIn: record(string(), array(unknown())).optional(),
15723
+ whereBetween: record(string(), tuple([unknown(), unknown()])).optional()
15724
+ });
15725
+ /** A single stored record: `{ id, data }`. */
15726
+ var SettingsRecordSchema = object({
15727
+ id: string(),
15728
+ data: record(string(), unknown())
15729
+ });
15730
+ /**
15731
+ * Column declaration for a structured (SQL-backed) collection.
15732
+ *
15733
+ * Logical types — the backend translates each to the matching SQLite
15734
+ * storage class and handles per-type marshaling:
15735
+ * - `TEXT` / `INTEGER` / `REAL` — native SQLite types, pass-through
15736
+ * - `JSON` — TEXT under the hood; serialised on write, parsed on read
15737
+ * - `BOOLEAN` — INTEGER 0/1 under the hood; coerced both directions
15738
+ */
15739
+ var CollectionColumnSchema = object({
15740
+ name: string(),
15741
+ type: _enum([
15742
+ "TEXT",
15743
+ "INTEGER",
15744
+ "REAL",
15745
+ "JSON",
15746
+ "BOOLEAN"
15747
+ ]),
15748
+ primaryKey: boolean().optional(),
15749
+ notNull: boolean().optional(),
15750
+ unique: boolean().optional(),
15751
+ /**
15752
+ * Column DEFAULT. Required for B2: the four `ensureTable` consumers declare
15753
+ * columns like `enabled INTEGER NOT NULL DEFAULT 1`, and without this the
15754
+ * collection surface simply cannot express their existing tables — which is
15755
+ * why they were still on the uncapped `table*` API.
15756
+ */
15757
+ defaultValue: union([
15758
+ string(),
15759
+ number(),
15760
+ boolean()
15761
+ ]).optional()
15762
+ });
15763
+ var CollectionIndexSchema = object({
15764
+ name: string(),
15765
+ columns: array(string()).readonly(),
15766
+ unique: boolean().optional()
15767
+ });
15768
+ method(object({
15769
+ namespace: string().optional(),
15770
+ collection: string(),
15771
+ key: string()
15772
+ }), unknown()), method(object({
15773
+ namespace: string().optional(),
15774
+ collection: string(),
15775
+ key: string(),
15776
+ value: unknown()
15777
+ }), _void(), { kind: "mutation" }), method(object({
15778
+ namespace: string().optional(),
15779
+ collection: string(),
15780
+ filter: QueryFilterSchema.optional()
15781
+ }), array(SettingsRecordSchema).readonly()), method(object({
15782
+ namespace: string().optional(),
15783
+ collection: string(),
15784
+ record: SettingsRecordSchema
15785
+ }), _void(), { kind: "mutation" }), method(object({
15786
+ namespace: string().optional(),
15787
+ collection: string(),
15788
+ id: string(),
15789
+ data: record(string(), unknown())
15790
+ }), _void(), { kind: "mutation" }), method(object({
15791
+ namespace: string().optional(),
15792
+ collection: string(),
15793
+ key: string()
15794
+ }), _void(), { kind: "mutation" }), method(object({
15795
+ namespace: string().optional(),
15796
+ collection: string(),
15797
+ filter: MutationFilterSchema
15798
+ }), object({ deleted: number().int() }), { kind: "mutation" }), method(object({
15799
+ namespace: string().optional(),
15800
+ collection: string(),
15801
+ filter: MutationFilterSchema,
15802
+ data: record(string(), unknown())
15803
+ }), object({ updated: number().int() }), { kind: "mutation" }), method(object({
15804
+ namespace: string().optional(),
15805
+ collection: string(),
15806
+ filter: QueryFilterSchema.optional()
15807
+ }), number()), method(object({
15808
+ namespace: string().optional(),
15809
+ collection: string(),
15810
+ field: string(),
15811
+ bucketSize: number().int().positive(),
15812
+ origin: number().int(),
15813
+ filter: QueryFilterSchema.optional()
15814
+ }), array(object({
15815
+ bucket: number().int(),
15816
+ count: number().int()
15817
+ })).readonly()), method(object({
15818
+ namespace: string().optional(),
15819
+ collection: string()
15820
+ }), boolean()), method(object({
15821
+ namespace: string().optional(),
15822
+ collection: string(),
15823
+ columns: array(CollectionColumnSchema).readonly(),
15824
+ indexes: array(CollectionIndexSchema).readonly().optional()
15825
+ }), _void(), { kind: "mutation" });
15826
+ /**
15827
+ * What one engine says about itself. The orchestrator uses `kind` to pick
15828
+ * a registrant for a collection; `engineId` is what a log line names when
15829
+ * a call is routed or refused.
15830
+ */
15831
+ var EngineInfoSchema = object({
15832
+ engineId: string(),
15833
+ /**
15834
+ * `relational` — rows, columns, indexes, the surface `settings-store`
15835
+ * has always described. `vector` — an embedding store answering
15836
+ * similarity queries. A registrant declares exactly one; an engine that
15837
+ * does both registers twice, because "both" would make the routing
15838
+ * decision ambiguous at exactly the point it must not be.
15839
+ */
15840
+ kind: _enum(["relational", "vector"]),
15841
+ displayName: string()
15842
+ });
15843
+ method(_void(), EngineInfoSchema), method(object({
15844
+ namespace: string().optional(),
15845
+ collection: string(),
15846
+ key: string()
15847
+ }), unknown()), method(object({
15848
+ namespace: string().optional(),
15849
+ collection: string(),
15850
+ key: string(),
15851
+ value: unknown()
15852
+ }), _void(), { kind: "mutation" }), method(object({
15853
+ namespace: string().optional(),
15854
+ collection: string(),
15855
+ filter: QueryFilterSchema.optional()
15856
+ }), array(SettingsRecordSchema).readonly()), method(object({
15857
+ namespace: string().optional(),
15858
+ collection: string(),
15859
+ record: SettingsRecordSchema
15860
+ }), _void(), { kind: "mutation" }), method(object({
15861
+ namespace: string().optional(),
15862
+ collection: string(),
15863
+ id: string(),
15864
+ data: record(string(), unknown())
15865
+ }), _void(), { kind: "mutation" }), method(object({
15866
+ namespace: string().optional(),
15867
+ collection: string(),
15868
+ key: string()
15869
+ }), _void(), { kind: "mutation" }), method(object({
15870
+ namespace: string().optional(),
15871
+ collection: string(),
15872
+ filter: MutationFilterSchema
15873
+ }), object({ deleted: number().int() }), { kind: "mutation" }), method(object({
15874
+ namespace: string().optional(),
15875
+ collection: string(),
15876
+ filter: MutationFilterSchema,
15877
+ data: record(string(), unknown())
15878
+ }), object({ updated: number().int() }), { kind: "mutation" }), method(object({
15879
+ namespace: string().optional(),
15880
+ collection: string(),
15881
+ filter: QueryFilterSchema.optional()
15882
+ }), number()), method(object({
15883
+ namespace: string().optional(),
15884
+ collection: string(),
15885
+ field: string(),
15886
+ bucketSize: number().int().positive(),
15887
+ origin: number().int(),
15888
+ filter: QueryFilterSchema.optional()
15889
+ }), array(object({
15890
+ bucket: number().int(),
15891
+ count: number().int()
15892
+ })).readonly()), method(object({
15893
+ namespace: string().optional(),
15894
+ collection: string()
15895
+ }), boolean()), method(object({
15896
+ namespace: string().optional(),
15897
+ collection: string(),
15898
+ columns: array(CollectionColumnSchema).readonly(),
15899
+ indexes: array(CollectionIndexSchema).readonly().optional()
15900
+ }), _void(), { kind: "mutation" });
15157
15901
  DeviceType.Camera;
15158
15902
  /**
15159
15903
  * `device-adoption` — generic discovery + adoption surface,
@@ -15832,7 +16576,20 @@ method(object({
15832
16576
  }), _void(), {
15833
16577
  kind: "mutation",
15834
16578
  auth: "admin"
15835
- }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
16579
+ }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({
16580
+ addonId: string().optional(),
16581
+ /**
16582
+ * `slim` omits `config` (returned `{}`), `metadata` (null) and the
16583
+ * `sourceInfo` derived from config — and skips the per-device settings
16584
+ * read that produces them. Everything identifying a device (id, name,
16585
+ * type, online, features, isCamera, parent/link ids) is unchanged.
16586
+ * Do not use it for dispatch routing, which needs `sourceInfo`.
16587
+ */
16588
+ projection: _enum(["full", "slim"]).optional(),
16589
+ /** Return only camera devices. Filtering server-side instead of
16590
+ * shipping 293 rows to find 12. */
16591
+ isCamera: boolean().optional()
16592
+ }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
15836
16593
  mode: DeviceLinkModeSchema,
15837
16594
  devices: array(LinkedDeviceSchema)
15838
16595
  })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
@@ -16271,14 +17028,14 @@ var LlmProfileSchema = object({
16271
17028
  /** ConfigUISchema tree passed through untyped on the wire (the
16272
17029
  * notification-output `ConfigSchemaPassthrough` precedent at
16273
17030
  * notification-output.cap.ts:151); the exported TS type re-tightens it. */
16274
- var ConfigSchemaPassthrough$1 = unknown();
17031
+ var ConfigSchemaPassthrough = unknown();
16275
17032
  var LlmProfileKindDescriptorSchema = object({
16276
17033
  kind: LlmProfileKindSchema,
16277
17034
  label: string(),
16278
17035
  icon: string(),
16279
17036
  /** Stamped by each provider so the concat-fanned catalog stays routable. */
16280
17037
  addonId: string(),
16281
- configSchema: ConfigSchemaPassthrough$1
17038
+ configSchema: ConfigSchemaPassthrough
16282
17039
  });
16283
17040
  var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
16284
17041
  var LlmDefaultSchema = object({
@@ -16828,227 +17585,102 @@ var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
16828
17585
  });
16829
17586
  method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
16830
17587
  /**
16831
- * notification-outputcanonical, capability-gated notification delivery.
16832
- *
16833
- * Apprise-derived model (see
16834
- * `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
16835
- * callers emit ONE canonical `Notification`; each provider declares a
16836
- * per-kind capability descriptor (`TargetKind`), and the pure degrade
16837
- * engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
16838
- * message to what the kind supports callers never special-case a service.
16839
- *
16840
- * DESIGN DECISIONS (locked):
16841
- * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
16842
- * `setTargetEnabled`), each provider persisting via the `settings-store`
16843
- * cap. Rationale: the admin UI needs one uniform surface across the
16844
- * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
16845
- * alternative would fork the UI per addon and cannot host the
16846
- * discovery→adopt flow.
16847
- * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
16848
- * the generated cap-mount auto-`concatCollection`-fans them across every
16849
- * registered provider (notifiers addon + HA addon) so one catalog is
16850
- * routable. `send` / `testTarget` / CRUD route to ONE provider by the
16851
- * `addonId` the generated collection router extracts from the call input.
16852
- * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
16853
- * transformer) + UDS MsgPack both round-trip typed arrays already used by
16854
- * `storage` / `storage-provider` / `recording` caps over the same path. No
16855
- * base64 fallback needed.
16856
- *
16857
- * TODO (deferred, closed-set change — separate decision): add
16858
- * `providerKind: 'notify'` so notification providers surface on the unified
16859
- * admin "Integrations" page.
16860
- */
16861
- /**
16862
- * Zentik-derived typed-media enum — the superset across every kind. Each
16863
- * adapter picks what it supports and the degrade engine filters the rest.
16864
- */
16865
- var AttachmentMediaTypeSchema = _enum([
16866
- "image",
16867
- "video",
16868
- "gif",
16869
- "audio",
16870
- "icon"
16871
- ]);
16872
- /**
16873
- * A single attachment. Exactly one of `url` (remote source, most adapters
16874
- * prefer this) or `bytes` (inline source; required for Pushover-style
16875
- * bytes-only kinds) MUST be present — the degrade engine expresses a
16876
- * url→bytes fetch as a `needsFetch` directive the adapter executes.
16877
- */
16878
- var AttachmentSchema = object({
16879
- mediaType: AttachmentMediaTypeSchema,
16880
- url: string().optional(),
16881
- bytes: _instanceof(Uint8Array).optional(),
16882
- mime: string().optional(),
16883
- name: string().optional()
16884
- }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
16885
- var NotificationFormatSchema = _enum([
16886
- "text",
16887
- "markdown",
16888
- "html"
17588
+ * core-blocksuser-authored TypeScript, stored in the kernel and executed in
17589
+ * its own process.
17590
+ *
17591
+ * Spec: `docs/superpowers/specs/2026-08-04-core-blocks-and-synthetic-devices-design.md`.
17592
+ *
17593
+ * The first use is **owning devices without being a device provider**: a block
17594
+ * declares devices under a system or custom integration and drives their state,
17595
+ * with the same `ctx` an addon gets. Automations come later; nothing here
17596
+ * models a trigger.
17597
+ *
17598
+ * **Stated plainly, because it does not change by being true:** a block has an
17599
+ * addon's powers devices, storage, the event bus, `ctx.api`. It is a plugin
17600
+ * with no review step. What makes that survivable is not a sandbox, it is
17601
+ * PROCESS ISOLATION: one process per block, supervised by `CrashSupervisor`,
17602
+ * so a block that throws or never returns is marked `failed` and visible
17603
+ * instead of taking the hub with it (D6). Every method here is admin-only, and
17604
+ * must stay so.
17605
+ */
17606
+ /** Where a block runs. The operator chooses a block driving a device on an
17607
+ * agent is the reason placement is not fixed to the hub. */
17608
+ var CoreBlockPlacementSchema = union([literal("hub"), string().min(1)]);
17609
+ /** What a block's process is doing. Mirrors the addon runner's own lifecycle so
17610
+ * a failing block reads the same way a failing addon does. */
17611
+ var CoreBlockStatusSchema = _enum([
17612
+ "stopped",
17613
+ "starting",
17614
+ "running",
17615
+ "failed"
16889
17616
  ]);
16890
- /** A single tap-through action button. */
16891
- var NotificationActionSchema = object({
16892
- id: string(),
16893
- label: string(),
16894
- url: string().optional()
16895
- });
16896
- /**
16897
- * The canonical notification. `body` is the only hard field (Apprise model).
16898
- * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
16899
- * NOT a fixed severity enum — each kind declares its own `caps.levels` and
16900
- * the adapter maps this ordinal onto its native level. `level?` is an
16901
- * optional kind-native level id (`emergency`, `silent`, …) that overrides
16902
- * `priority` for that one target.
16903
- */
16904
- var NotificationSchema = object({
16905
- body: string(),
16906
- title: string().optional(),
16907
- format: NotificationFormatSchema.default("text"),
16908
- priority: number().int().min(1).max(5).default(3),
16909
- level: string().optional(),
16910
- attachments: array(AttachmentSchema).optional(),
16911
- clickUrl: string().optional(),
16912
- actions: array(NotificationActionSchema).optional(),
16913
- sound: string().optional(),
16914
- ttl: number().optional(),
16915
- tag: string().optional(),
16916
- deviceId: number().optional(),
16917
- eventId: string().optional(),
16918
- metadata: record(string(), unknown()).optional()
16919
- });
16920
- /** One declared native severity/priority level for a kind. */
16921
- var TargetKindLevelSchema = object({
16922
- id: string(),
16923
- label: string(),
16924
- /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
16925
- ordinal: number().int().min(1).max(5).nullable(),
16926
- flags: object({
16927
- critical: boolean().optional(),
16928
- silent: boolean().optional(),
16929
- noPush: boolean().optional()
16930
- }).optional(),
16931
- /** e.g. Pushover `emergency` requires `retry` / `expire`. */
16932
- requires: array(string()).optional(),
16933
- description: string().optional()
16934
- });
16935
- /** The full capability block consulted before dispatch. */
16936
- var TargetKindCapsSchema = object({
16937
- attachments: object({
16938
- mediaTypes: array(AttachmentMediaTypeSchema),
16939
- mode: _enum([
16940
- "url",
16941
- "bytes",
16942
- "both"
16943
- ]),
16944
- max: number().int().nonnegative(),
16945
- maxBytes: number().int().positive().optional()
16946
- }),
16947
- /** Max action buttons (0 = none). */
16948
- actions: number().int().nonnegative(),
16949
- levels: array(TargetKindLevelSchema),
16950
- format: array(NotificationFormatSchema),
16951
- clickUrl: boolean(),
16952
- sound: boolean(),
16953
- ttl: boolean(),
16954
- bodyMaxLen: number().int().positive()
16955
- });
16956
- /**
16957
- * `configSchema` is a `ConfigUISchema` tree passed through to the admin
16958
- * FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
16959
- * `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
16960
- * the union is large and not meant for runtime validation here; the exported
16961
- * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
16962
- */
16963
- var ConfigSchemaPassthrough = unknown();
16964
- var TargetKindSchema = object({
16965
- kind: string(),
16966
- label: string(),
16967
- icon: string(),
16968
- /** Stamped by each provider so the concat-fanned catalog stays routable. */
16969
- addonId: string(),
16970
- /**
16971
- * URL of the kind's bundled BRAND icon, served by the providing addon over
16972
- * its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
16973
- * when the addon bundles no icon for that kind — the client then falls back
16974
- * to a neutral glyph rather than rendering the raw `icon` NAME as text.
16975
- *
16976
- * Root-relative on purpose: it resolves against whatever origin serves a web
16977
- * client, and a native client joins it onto its own hub base.
16978
- *
16979
- * DECLARED here deliberately. It used to travel as an undeclared passthrough
16980
- * field that survived only because the runtime cap-router forwards provider
16981
- * output verbatim — so every consumer had to re-declare it by hand to stop
16982
- * its own Zod parse from stripping it, and the whole arrangement would have
16983
- * broken silently the moment output validation was tightened anywhere.
16984
- */
16985
- iconUrl: string().optional(),
17617
+ /** Client-authored fields. */
17618
+ var CoreBlockInputSchema = object({
17619
+ name: string().min(1).max(120),
17620
+ /** TypeScript source. Compiled server-side before it is ever stored — a
17621
+ * block that does not compile is a fork failure the operator would meet
17622
+ * minutes later, in a log, instead of in the editor. */
17623
+ code: string().max(2e5),
17624
+ enabled: boolean(),
17625
+ placement: CoreBlockPlacementSchema,
16986
17626
  /**
16987
- * Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
16988
- *
16989
- * The server knows this and therefore says it, because the client cannot
16990
- * safely guess: a React-Native client renders SVG and raster through two
16991
- * DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
16992
- * not decode SVG on iOS/Android), so without this it silently fell back to a
16993
- * placeholder glyph for every vector icon while the web build looked fine.
16994
- *
16995
- * Absent when {@link iconUrl} is absent, or for a legacy provider that has
16996
- * not been updated — a client that cannot determine the type should prefer
16997
- * its raster path, which is the safe default for an unknown image.
17627
+ * Integration the block's devices hang from. Absent = the system integration
17628
+ * blocks share. A block may declare its own instead.
16998
17629
  */
16999
- iconMediaType: string().optional(),
17000
- configSchema: ConfigSchemaPassthrough,
17001
- supportsDiscovery: boolean(),
17002
- caps: TargetKindCapsSchema
17630
+ integrationId: string().optional()
17003
17631
  });
17004
- /**
17005
- * A persisted target. `config` holds secrets; providers REDACT secret fields
17006
- * (return a presence marker only) when serving `listTargets` — never
17007
- * round-trip a stored secret to the UI.
17008
- */
17009
- var TargetSchema = object({
17632
+ /** A stored block. */
17633
+ var CoreBlockSchema = CoreBlockInputSchema.extend({
17010
17634
  id: string(),
17011
- name: string(),
17012
- kind: string(),
17013
- addonId: string(),
17014
- enabled: boolean(),
17015
- config: record(string(), unknown())
17016
- });
17017
- /** A discovery-surfaced candidate (config is partial + non-secret). */
17018
- var DiscoveredTargetSchema = object({
17019
- kind: string(),
17020
- suggestedName: string(),
17021
- config: record(string(), unknown())
17022
- });
17023
- /** The degrade engine's report — what was resolved / dropped / degraded. */
17024
- var RenderedAsSchema = object({
17025
- level: string(),
17026
- format: NotificationFormatSchema,
17027
- attachmentsSent: number().int().nonnegative(),
17028
- actionsSent: number().int().nonnegative(),
17029
- truncated: boolean(),
17030
- dropped: array(string())
17635
+ createdAt: number(),
17636
+ updatedAt: number(),
17637
+ /** Server-stamped author. */
17638
+ createdBy: string(),
17639
+ status: CoreBlockStatusSchema,
17640
+ /**
17641
+ * Why the block is not running, when it is not. The operator's ONLY window
17642
+ * into a block that failed at load — a block that is silently absent is the
17643
+ * failure mode this whole feature has to avoid.
17644
+ */
17645
+ lastError: string().optional(),
17646
+ /** Ms epoch of the last state change. */
17647
+ lastChangedAt: number()
17031
17648
  });
17032
- var SendResultSchema = object({
17033
- success: boolean(),
17649
+ /** What a compile attempt produced. */
17650
+ var CoreBlockCompileResultSchema = object({
17651
+ ok: boolean(),
17652
+ /** Present when `ok` is false — the first error, in the author's words. */
17034
17653
  error: string().optional(),
17035
- renderedAs: RenderedAsSchema.optional()
17654
+ line: number().optional(),
17655
+ column: number().optional()
17036
17656
  });
17037
- /** Same shape as SendResult kept as a distinct name for the test panel. */
17038
- var TestResultSchema = SendResultSchema;
17039
- method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema)), method(object({
17040
- kind: string(),
17041
- config: record(string(), unknown()).optional()
17042
- }), array(DiscoveredTargetSchema)), method(object({
17043
- targetId: string(),
17044
- notification: NotificationSchema
17045
- }), SendResultSchema, { kind: "mutation" }), method(object({
17046
- targetId: string(),
17047
- sample: NotificationSchema.optional()
17048
- }), TestResultSchema, { kind: "mutation" }), method(object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }), method(object({ targetId: string() }), _void(), { kind: "mutation" }), method(object({
17049
- targetId: string(),
17657
+ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }), method(object({ blockId: string() }), object({ block: CoreBlockSchema.nullable() }), { auth: "admin" }), method(object({ block: CoreBlockInputSchema }), object({ block: CoreBlockSchema }), {
17658
+ kind: "mutation",
17659
+ auth: "admin",
17660
+ caller: "required"
17661
+ }), method(object({
17662
+ blockId: string(),
17663
+ block: CoreBlockInputSchema.partial()
17664
+ }), object({ block: CoreBlockSchema }), {
17665
+ kind: "mutation",
17666
+ auth: "admin",
17667
+ caller: "required"
17668
+ }), method(object({ blockId: string() }), object({ success: literal(true) }), {
17669
+ kind: "mutation",
17670
+ auth: "admin"
17671
+ }), method(object({
17672
+ blockId: string(),
17050
17673
  enabled: boolean()
17051
- }), _void(), { kind: "mutation" });
17674
+ }), object({ block: CoreBlockSchema }), {
17675
+ kind: "mutation",
17676
+ auth: "admin"
17677
+ }), method(object({ code: string() }), CoreBlockCompileResultSchema, {
17678
+ kind: "mutation",
17679
+ auth: "admin"
17680
+ }), method(object({}), object({ libs: array(object({
17681
+ filePath: string(),
17682
+ content: string()
17683
+ })) }), { auth: "admin" });
17052
17684
  /**
17053
17685
  * Zod schemas for persisted record types.
17054
17686
  *
@@ -17296,6 +17928,11 @@ var EventKindDescriptorSchema = object({
17296
17928
  deviceId: number()
17297
17929
  })
17298
17930
  });
17931
+ /** One camera's event vocabulary, as returned by `listEventKindsBatch`. */
17932
+ var EventKindsForDeviceSchema = object({
17933
+ deviceId: number(),
17934
+ kinds: array(EventKindDescriptorSchema).readonly()
17935
+ });
17299
17936
  var SensorEventSchema = object({
17300
17937
  id: string(),
17301
17938
  /** The CAMERA the event is attributed to (a sensor linked to N cameras
@@ -17552,6 +18189,19 @@ var MediaFileSchema = object({
17552
18189
  sizeBytes: number(),
17553
18190
  timestamp: number()
17554
18191
  });
18192
+ /**
18193
+ * One media row WITHOUT its bytes.
18194
+ *
18195
+ * A track's media is 5-8 MB of base64 (measured: 7.67 MB across 23 files for a
18196
+ * 140 s track), and a client that renders tiles from the media data plane needs
18197
+ * to know only WHAT EXISTS — the bytes then arrive per tile, lazily, over HTTP
18198
+ * with an immutable cache, instead of all at once inside a tRPC response that
18199
+ * blocks the whole view.
18200
+ *
18201
+ * `sizeBytes` is carried because it is what lets a client decide between the
18202
+ * stored blob and a `?variant=thumb` rendering without fetching either.
18203
+ */
18204
+ var MediaFileInfoSchema = MediaFileSchema.omit({ base64: true });
17555
18205
  var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
17556
18206
  var MAX_EVENT_QUERY_LIMIT = 5e3;
17557
18207
  var DeviceEventQueryInput = object({
@@ -17701,7 +18351,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17701
18351
  }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17702
18352
  kind: "mutation",
17703
18353
  auth: "admin"
17704
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
18354
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
17705
18355
  deviceId: number(),
17706
18356
  since: number().optional(),
17707
18357
  until: number().optional(),
@@ -17775,7 +18425,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17775
18425
  }), array(MediaFileSchema).readonly()), method(object({
17776
18426
  trackId: string(),
17777
18427
  kinds: array(MediaFileKindEnum).optional()
17778
- }), array(MediaFileSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
18428
+ }), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
17779
18429
  deviceId: number(),
17780
18430
  timestamp: number(),
17781
18431
  frameWidth: number(),
@@ -18405,6 +19055,28 @@ var ServerUpdateStateSchema = _enum([
18405
19055
  "pending-restart",
18406
19056
  "awaiting-confirmation"
18407
19057
  ]);
19058
+ var ImageContractSchema = object({
19059
+ state: _enum([
19060
+ "in-sync",
19061
+ "behind-patch",
19062
+ "behind-series",
19063
+ "ahead",
19064
+ "unknown"
19065
+ ]),
19066
+ /** The baked seed closure version — the image/app-bundle fingerprint. */
19067
+ seedVersion: string().nullable(),
19068
+ /** Best-known version the deployment contract delivers today. */
19069
+ contractVersion: string().nullable(),
19070
+ /**
19071
+ * Where `contractVersion` came from: a real registry check (`registry`), or
19072
+ * the node's own running version (`running` — a node can never run code
19073
+ * newer than the newest release, so `seed < running` proves image staleness
19074
+ * even before any registry check has run).
19075
+ */
19076
+ contractSource: _enum(["registry", "running"]).nullable(),
19077
+ /** One operator-grade sentence: this node runs image X; the contract says Y. */
19078
+ message: string()
19079
+ });
18408
19080
  var ServerRollbackInfoSchema = object({
18409
19081
  /** The version that failed (or was manually rolled back). */
18410
19082
  fromVersion: string(),
@@ -18441,7 +19113,12 @@ var ServerPackageStatusSchema = object({
18441
19113
  * versions are being IGNORED. Surfaced as a warning in the UI.
18442
19114
  */
18443
19115
  stateFileCorrupt: boolean(),
18444
- lastCheckedAtMs: number().nullable()
19116
+ lastCheckedAtMs: number().nullable(),
19117
+ /**
19118
+ * Seed-vs-contract verdict (see {@link ImageContractSchema}). Optional for
19119
+ * version skew: an older provider's payload simply omits it.
19120
+ */
19121
+ imageContract: ImageContractSchema.optional()
18445
19122
  });
18446
19123
  var ServerUpdateCheckResultSchema = object({
18447
19124
  packageName: string(),
@@ -18476,101 +19153,6 @@ version: string().optional() }), ServerUpdateActionResultSchema, {
18476
19153
  auth: "admin"
18477
19154
  });
18478
19155
  /**
18479
- * Query filter for settings-store collections.
18480
- */
18481
- var QueryFilterSchema = object({
18482
- where: record(string(), unknown()).optional(),
18483
- whereIn: record(string(), array(unknown())).optional(),
18484
- whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
18485
- orderBy: object({
18486
- field: string(),
18487
- direction: _enum(["asc", "desc"])
18488
- }).optional(),
18489
- limit: number().optional(),
18490
- offset: number().optional()
18491
- });
18492
- /** A single stored record: `{ id, data }`. */
18493
- var SettingsRecordSchema = object({
18494
- id: string(),
18495
- data: record(string(), unknown())
18496
- });
18497
- /**
18498
- * Column declaration for a structured (SQL-backed) collection.
18499
- *
18500
- * Logical types — the backend translates each to the matching SQLite
18501
- * storage class and handles per-type marshaling:
18502
- * - `TEXT` / `INTEGER` / `REAL` — native SQLite types, pass-through
18503
- * - `JSON` — TEXT under the hood; serialised on write, parsed on read
18504
- * - `BOOLEAN` — INTEGER 0/1 under the hood; coerced both directions
18505
- */
18506
- var CollectionColumnSchema = object({
18507
- name: string(),
18508
- type: _enum([
18509
- "TEXT",
18510
- "INTEGER",
18511
- "REAL",
18512
- "JSON",
18513
- "BOOLEAN"
18514
- ]),
18515
- primaryKey: boolean().optional(),
18516
- notNull: boolean().optional(),
18517
- unique: boolean().optional()
18518
- });
18519
- var CollectionIndexSchema = object({
18520
- name: string(),
18521
- columns: array(string()).readonly(),
18522
- unique: boolean().optional()
18523
- });
18524
- method(object({
18525
- namespace: string().optional(),
18526
- collection: string(),
18527
- key: string()
18528
- }), unknown()), method(object({
18529
- namespace: string().optional(),
18530
- collection: string(),
18531
- key: string(),
18532
- value: unknown()
18533
- }), _void(), { kind: "mutation" }), method(object({
18534
- namespace: string().optional(),
18535
- collection: string(),
18536
- filter: QueryFilterSchema.optional()
18537
- }), array(SettingsRecordSchema).readonly()), method(object({
18538
- namespace: string().optional(),
18539
- collection: string(),
18540
- record: SettingsRecordSchema
18541
- }), _void(), { kind: "mutation" }), method(object({
18542
- namespace: string().optional(),
18543
- collection: string(),
18544
- id: string(),
18545
- data: record(string(), unknown())
18546
- }), _void(), { kind: "mutation" }), method(object({
18547
- namespace: string().optional(),
18548
- collection: string(),
18549
- key: string()
18550
- }), _void(), { kind: "mutation" }), method(object({
18551
- namespace: string().optional(),
18552
- collection: string(),
18553
- filter: QueryFilterSchema.optional()
18554
- }), number()), method(object({
18555
- namespace: string().optional(),
18556
- collection: string(),
18557
- field: string(),
18558
- bucketSize: number().int().positive(),
18559
- origin: number().int(),
18560
- filter: QueryFilterSchema.optional()
18561
- }), array(object({
18562
- bucket: number().int(),
18563
- count: number().int()
18564
- })).readonly()), method(object({
18565
- namespace: string().optional(),
18566
- collection: string()
18567
- }), boolean()), method(object({
18568
- namespace: string().optional(),
18569
- collection: string(),
18570
- columns: array(CollectionColumnSchema).readonly(),
18571
- indexes: array(CollectionIndexSchema).readonly().optional()
18572
- }), _void(), { kind: "mutation" });
18573
- /**
18574
19156
  * `smtp-provider` — pluggable email delivery surface.
18575
19157
  *
18576
19158
  * Collection cap: a deployment may install multiple SMTP relays (e.g.
@@ -22433,6 +23015,54 @@ Object.freeze({
22433
23015
  addonId: null,
22434
23016
  access: "create"
22435
23017
  },
23018
+ "coreBlocks.compile": {
23019
+ capName: "core-blocks",
23020
+ capScope: "system",
23021
+ addonId: null,
23022
+ access: "create"
23023
+ },
23024
+ "coreBlocks.create": {
23025
+ capName: "core-blocks",
23026
+ capScope: "system",
23027
+ addonId: null,
23028
+ access: "create"
23029
+ },
23030
+ "coreBlocks.delete": {
23031
+ capName: "core-blocks",
23032
+ capScope: "system",
23033
+ addonId: null,
23034
+ access: "delete"
23035
+ },
23036
+ "coreBlocks.get": {
23037
+ capName: "core-blocks",
23038
+ capScope: "system",
23039
+ addonId: null,
23040
+ access: "view"
23041
+ },
23042
+ "coreBlocks.getTypeDefs": {
23043
+ capName: "core-blocks",
23044
+ capScope: "system",
23045
+ addonId: null,
23046
+ access: "view"
23047
+ },
23048
+ "coreBlocks.list": {
23049
+ capName: "core-blocks",
23050
+ capScope: "system",
23051
+ addonId: null,
23052
+ access: "view"
23053
+ },
23054
+ "coreBlocks.setEnabled": {
23055
+ capName: "core-blocks",
23056
+ capScope: "system",
23057
+ addonId: null,
23058
+ access: "create"
23059
+ },
23060
+ "coreBlocks.update": {
23061
+ capName: "core-blocks",
23062
+ capScope: "system",
23063
+ addonId: null,
23064
+ access: "create"
23065
+ },
22436
23066
  "cover.close": {
22437
23067
  capName: "cover",
22438
23068
  capScope: "device",
@@ -22469,6 +23099,84 @@ Object.freeze({
22469
23099
  addonId: null,
22470
23100
  access: "view"
22471
23101
  },
23102
+ "dataStoreProvider.count": {
23103
+ capName: "data-store-provider",
23104
+ capScope: "system",
23105
+ addonId: null,
23106
+ access: "view"
23107
+ },
23108
+ "dataStoreProvider.declareCollection": {
23109
+ capName: "data-store-provider",
23110
+ capScope: "system",
23111
+ addonId: null,
23112
+ access: "create"
23113
+ },
23114
+ "dataStoreProvider.delete": {
23115
+ capName: "data-store-provider",
23116
+ capScope: "system",
23117
+ addonId: null,
23118
+ access: "delete"
23119
+ },
23120
+ "dataStoreProvider.deleteWhere": {
23121
+ capName: "data-store-provider",
23122
+ capScope: "system",
23123
+ addonId: null,
23124
+ access: "delete"
23125
+ },
23126
+ "dataStoreProvider.get": {
23127
+ capName: "data-store-provider",
23128
+ capScope: "system",
23129
+ addonId: null,
23130
+ access: "view"
23131
+ },
23132
+ "dataStoreProvider.getEngineInfo": {
23133
+ capName: "data-store-provider",
23134
+ capScope: "system",
23135
+ addonId: null,
23136
+ access: "view"
23137
+ },
23138
+ "dataStoreProvider.histogram": {
23139
+ capName: "data-store-provider",
23140
+ capScope: "system",
23141
+ addonId: null,
23142
+ access: "view"
23143
+ },
23144
+ "dataStoreProvider.insert": {
23145
+ capName: "data-store-provider",
23146
+ capScope: "system",
23147
+ addonId: null,
23148
+ access: "create"
23149
+ },
23150
+ "dataStoreProvider.isEmpty": {
23151
+ capName: "data-store-provider",
23152
+ capScope: "system",
23153
+ addonId: null,
23154
+ access: "view"
23155
+ },
23156
+ "dataStoreProvider.query": {
23157
+ capName: "data-store-provider",
23158
+ capScope: "system",
23159
+ addonId: null,
23160
+ access: "view"
23161
+ },
23162
+ "dataStoreProvider.set": {
23163
+ capName: "data-store-provider",
23164
+ capScope: "system",
23165
+ addonId: null,
23166
+ access: "create"
23167
+ },
23168
+ "dataStoreProvider.update": {
23169
+ capName: "data-store-provider",
23170
+ capScope: "system",
23171
+ addonId: null,
23172
+ access: "create"
23173
+ },
23174
+ "dataStoreProvider.updateWhere": {
23175
+ capName: "data-store-provider",
23176
+ capScope: "system",
23177
+ addonId: null,
23178
+ access: "create"
23179
+ },
22472
23180
  "dayNight.getOptions": {
22473
23181
  capName: "day-night",
22474
23182
  capScope: "device",
@@ -24263,12 +24971,24 @@ Object.freeze({
24263
24971
  addonId: null,
24264
24972
  access: "create"
24265
24973
  },
24974
+ "notificationRules.cancelSnooze": {
24975
+ capName: "notification-rules",
24976
+ capScope: "system",
24977
+ addonId: null,
24978
+ access: "create"
24979
+ },
24266
24980
  "notificationRules.createRule": {
24267
24981
  capName: "notification-rules",
24268
24982
  capScope: "system",
24269
24983
  addonId: null,
24270
24984
  access: "create"
24271
24985
  },
24986
+ "notificationRules.createSnooze": {
24987
+ capName: "notification-rules",
24988
+ capScope: "system",
24989
+ addonId: null,
24990
+ access: "create"
24991
+ },
24272
24992
  "notificationRules.deleteRule": {
24273
24993
  capName: "notification-rules",
24274
24994
  capScope: "system",
@@ -24299,6 +25019,12 @@ Object.freeze({
24299
25019
  addonId: null,
24300
25020
  access: "view"
24301
25021
  },
25022
+ "notificationRules.listSnoozes": {
25023
+ capName: "notification-rules",
25024
+ capScope: "system",
25025
+ addonId: null,
25026
+ access: "view"
25027
+ },
24302
25028
  "notificationRules.setRuleEnabled": {
24303
25029
  capName: "notification-rules",
24304
25030
  capScope: "system",
@@ -24503,6 +25229,12 @@ Object.freeze({
24503
25229
  addonId: null,
24504
25230
  access: "view"
24505
25231
  },
25232
+ "pipelineAnalytics.listEventKindsBatch": {
25233
+ capName: "pipeline-analytics",
25234
+ capScope: "device",
25235
+ addonId: null,
25236
+ access: "view"
25237
+ },
24506
25238
  "pipelineAnalytics.listOpsLog": {
24507
25239
  capName: "pipeline-analytics",
24508
25240
  capScope: "device",
@@ -24515,6 +25247,12 @@ Object.freeze({
24515
25247
  addonId: null,
24516
25248
  access: "view"
24517
25249
  },
25250
+ "pipelineAnalytics.listTrackMedia": {
25251
+ capName: "pipeline-analytics",
25252
+ capScope: "device",
25253
+ addonId: null,
25254
+ access: "view"
25255
+ },
24518
25256
  "pipelineAnalytics.listTracks": {
24519
25257
  capName: "pipeline-analytics",
24520
25258
  capScope: "device",
@@ -25547,6 +26285,12 @@ Object.freeze({
25547
26285
  addonId: null,
25548
26286
  access: "delete"
25549
26287
  },
26288
+ "settingsStore.deleteWhere": {
26289
+ capName: "settings-store",
26290
+ capScope: "system",
26291
+ addonId: null,
26292
+ access: "delete"
26293
+ },
25550
26294
  "settingsStore.get": {
25551
26295
  capName: "settings-store",
25552
26296
  capScope: "system",
@@ -25589,6 +26333,12 @@ Object.freeze({
25589
26333
  addonId: null,
25590
26334
  access: "create"
25591
26335
  },
26336
+ "settingsStore.updateWhere": {
26337
+ capName: "settings-store",
26338
+ capScope: "system",
26339
+ addonId: null,
26340
+ access: "create"
26341
+ },
25592
26342
  "smtpProvider.getStatus": {
25593
26343
  capName: "smtp-provider",
25594
26344
  capScope: "system",
@@ -59042,8 +59792,9 @@ var MqttBrokerAddon = class extends BaseAddon {
59042
59792
  label: "MQTT"
59043
59793
  }]
59044
59794
  }],
59045
- add: async ({ kind, name, settings }) => {
59795
+ add: async ({ kind, name, settings, id }) => {
59046
59796
  if (kind !== KIND) throw new Error(`mqtt-broker: only kind '${KIND}' is handled here (got '${kind}')`);
59797
+ if (id !== void 0) throw new Error("mqtt-broker: adopting an existing broker id is not supported by this provider");
59047
59798
  if (isOnboardRequested(settings)) return this.addOnboardBroker(name, settings);
59048
59799
  const url = typeof settings["url"] === "string" ? settings["url"] : "";
59049
59800
  if (!url) throw new Error("mqtt-broker.add: settings.url is required");