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