camstack 1.2.5 → 1.2.6

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.
@@ -14522,7 +14522,7 @@ function date4(params) {
14522
14522
  // ../../node_modules/zod/v4/classic/external.js
14523
14523
  config(en_default());
14524
14524
 
14525
- // ../types/dist/sleep-DiOnzenz.mjs
14525
+ // ../types/dist/sleep-C3ceNSsf.mjs
14526
14526
  var WELL_KNOWN_TABS = [
14527
14527
  {
14528
14528
  id: "overview",
@@ -16914,617 +16914,1118 @@ var BATTERY_DEVICE_PROFILE = {
16914
16914
  "streamBroker.preBufferEnabled": false
16915
16915
  }
16916
16916
  };
16917
- var DeviceStatusSchema = external_exports.object({
16917
+ var MaskPointSchema = external_exports.object({
16918
+ x: external_exports.number(),
16919
+ y: external_exports.number()
16920
+ });
16921
+ var MaskRectShapeSchema = external_exports.object({
16922
+ kind: external_exports.literal("rect"),
16923
+ x: external_exports.number(),
16924
+ y: external_exports.number(),
16925
+ width: external_exports.number(),
16926
+ height: external_exports.number()
16927
+ });
16928
+ var MaskPolygonShapeSchema = external_exports.object({
16929
+ kind: external_exports.literal("polygon"),
16930
+ points: external_exports.array(MaskPointSchema)
16931
+ });
16932
+ var MaskGridShapeSchema = external_exports.object({
16933
+ kind: external_exports.literal("grid"),
16934
+ gridWidth: external_exports.number(),
16935
+ gridHeight: external_exports.number(),
16936
+ cells: external_exports.array(external_exports.boolean())
16937
+ });
16938
+ var MaskLineShapeSchema = external_exports.object({
16939
+ kind: external_exports.literal("line"),
16940
+ points: external_exports.array(MaskPointSchema)
16941
+ });
16942
+ var MaskShapeSchema = external_exports.discriminatedUnion("kind", [
16943
+ MaskRectShapeSchema,
16944
+ MaskPolygonShapeSchema,
16945
+ MaskGridShapeSchema,
16946
+ MaskLineShapeSchema
16947
+ ]);
16948
+ var MaskShapeKindSchema = external_exports.enum([
16949
+ "rect",
16950
+ "polygon",
16951
+ "grid",
16952
+ "line"
16953
+ ]);
16954
+ var MaskPolygonVerticesSchema = external_exports.object({
16955
+ min: external_exports.number(),
16956
+ max: external_exports.number()
16957
+ });
16958
+ var MaskGridDimsSchema = external_exports.object({
16959
+ width: external_exports.number(),
16960
+ height: external_exports.number()
16961
+ });
16962
+ var NcDeliverySchema = external_exports.enum([
16963
+ "immediate",
16964
+ "track-end",
16965
+ "device-event",
16966
+ "package-event"
16967
+ ]);
16968
+ var NcScheduleWindowSchema = external_exports.object({
16969
+ /** Days of week the window STARTS on (0 = Sunday … 6 = Saturday). */
16970
+ days: external_exports.array(external_exports.number().int().min(0).max(6)).min(1),
16971
+ startMinute: external_exports.number().int().min(0).max(1439),
16972
+ endMinute: external_exports.number().int().min(0).max(1439)
16973
+ });
16974
+ var NcScheduleSchema = external_exports.object({
16975
+ windows: external_exports.array(NcScheduleWindowSchema).min(1),
16976
+ /** IANA timezone; default = hub host timezone. */
16977
+ timezone: external_exports.string().optional(),
16978
+ /** Active OUTSIDE the windows (e.g. "only outside business hours"). */
16979
+ invert: external_exports.boolean().optional()
16980
+ });
16981
+ var NcPlateMatcherSchema = external_exports.object({
16982
+ values: external_exports.array(external_exports.string().min(1)).min(1),
16983
+ /** Max Levenshtein distance after normalization (uppercase alphanumeric). */
16984
+ maxDistance: external_exports.number().int().min(0).max(3).default(1)
16985
+ });
16986
+ var NcOccupancyConditionSchema = external_exports.object({
16987
+ /** Admin zone id to scope the count to; absent = whole-frame occupancy. */
16988
+ zoneId: external_exports.string().optional(),
16989
+ /** Object class to count; absent = any class. */
16990
+ className: external_exports.string().optional(),
16991
+ op: external_exports.enum([
16992
+ "became-occupied",
16993
+ "became-free",
16994
+ ">=",
16995
+ "<="
16996
+ ]).default("became-occupied"),
16997
+ count: external_exports.number().int().min(0).default(1),
16998
+ sustainSeconds: external_exports.number().int().min(0).max(3600).default(15)
16999
+ });
17000
+ var NcZoneConditionSchema = external_exports.object({
17001
+ ids: external_exports.array(external_exports.string().min(1)).min(1),
17002
+ /** Quantifier over `ids` — at least one / every one visited. */
17003
+ match: external_exports.enum(["any", "all"]).default("any")
17004
+ });
17005
+ var NcConditionsSchema = external_exports.object({
17006
+ /** Device scope — absent = all devices. */
17007
+ devices: external_exports.array(external_exports.number()).optional(),
17008
+ /** Detector class names (any overlap with the record's class set). */
17009
+ classes: external_exports.array(external_exports.string().min(1)).optional(),
17010
+ /** Veto classes — any overlap fails the rule. */
17011
+ classesExclude: external_exports.array(external_exports.string().min(1)).optional(),
17012
+ /** Minimum detection confidence 0–1 (fails when the record has none). */
17013
+ minConfidence: external_exports.number().min(0).max(1).optional(),
17014
+ /** Admin zone membership over event `zones` / track `zonesVisited`. */
17015
+ zones: NcZoneConditionSchema.optional(),
17016
+ /** Veto zones — any hit fails the rule. */
17017
+ zonesExclude: external_exports.array(external_exports.string().min(1)).optional(),
16918
17018
  /**
16919
- * Device-level liveness. Drivers flip via `markOnline(boolean)` on
16920
- * `BaseDevice`. Provider semantics vary RTSP aggregates broker
16921
- * stream-health, Reolink reads firmware push events, ONVIF tracks
16922
- * ping responses. This cap intentionally does NOT prescribe which
16923
- * signal drives the flag.
17019
+ * Exact (case-insensitive) match on the record's collapsed `label`
17020
+ * (identity name / plate text / subclass).
16924
17021
  */
16925
- online: external_exports.boolean(),
16926
- /** Ms epoch of the last `online` transition. Lets consumers tell
16927
- * apart "just came online" from "still online". */
16928
- lastChangedAt: external_exports.number()
16929
- });
16930
- var deviceStatusCapability = {
16931
- name: "device-status",
16932
- scope: "device",
16933
- deviceNative: true,
16934
- mode: "singleton",
16935
- methods: {},
16936
- events: {
16937
- /** Emitted when `online` transitions. Mirrors the semantics of
16938
- * `battery.onStatusChanged`. */
16939
- onStatusChanged: { data: external_exports.object({
16940
- deviceId: external_exports.number(),
16941
- status: DeviceStatusSchema
16942
- }) }
16943
- },
16944
- status: {
16945
- schema: DeviceStatusSchema,
16946
- kind: "push"
16947
- },
16948
- runtimeState: DeviceStatusSchema
16949
- };
16950
- var FeatureProbeStatusSchema = external_exports.object({
17022
+ labelEquals: external_exports.array(external_exports.string().min(1)).optional(),
16951
17023
  /**
16952
- * Driver-specific flag bag. Each driver picks its own key names — the
16953
- * cap deliberately does NOT enforce a closed enum here. Reolink keys:
16954
- * `hasPtz`, `hasIntercom`, `hasDoorbell`, `hasFloodlight`, `hasSiren`,
16955
- * `hasPirSensor`, `hasAutotrack`, `hasBattery`. Hikvision keys:
16956
- * `hasSupplementalLight`, `lightHasWhiteLight`, `hasAlarmIo`, `hasPtz`.
17024
+ * Identity matcher. P1 boundary: matched against the record's collapsed
17025
+ * `label` (the identity display name propagated by the face pipeline)
17026
+ * identity-ID matching rides in P2 when identity ids reach the record.
16957
17027
  */
16958
- flags: external_exports.record(external_exports.string(), external_exports.unknown()),
17028
+ identities: external_exports.array(external_exports.string().min(1)).optional(),
17029
+ /** Fuzzy plate matcher against the record's `label` (plate text). */
17030
+ plates: NcPlateMatcherSchema.optional(),
16959
17031
  /**
16960
- * Coarse driver-classificationlets cross-process consumers tell apart
16961
- * cameras / battery-cams / NVRs without re-running the probe. `null`
16962
- * before the first probe completes.
17032
+ * Identity EXCLUDEmirror of {@link identities} with `notIn` semantics.
17033
+ * Same P1 boundary: matched against the record's collapsed `label` (the
17034
+ * identity display name). A record with NO label passes (nothing to
17035
+ * exclude), unlike the include variant which fails on an absent label.
16963
17036
  */
16964
- deviceType: external_exports.string().nullable(),
16965
- /** Camera/firmware model string. `null` when the firmware doesn't expose it. */
16966
- model: external_exports.string().nullable(),
16967
- /** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
16968
- channelCount: external_exports.number().nullable(),
17037
+ identitiesExclude: external_exports.array(external_exports.string().min(1)).optional(),
16969
17038
  /**
16970
- * Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
16971
- * completes drivers' `getAccessoryChildren()` should treat zero as
16972
- * "probe not done yet, return empty" so accessories aren't spawned
16973
- * before the firmware is queried.
17039
+ * Minimum server-computed key-event importance in [0,1] (`Track.importance`).
17040
+ * TRACK-END only: importance is scored at track close, so it does not exist
17041
+ * at immediate / object-event evaluation time (see catalog `appliesTo`). At
17042
+ * close the value is threaded via the close-time info (the `Track` clone is
17043
+ * captured before the DB row is updated, so it would otherwise read stale).
17044
+ * Fails when the record carries no importance (never guess quality — the
17045
+ * `minConfidence` precedent). MVP cut: a single scalar threshold.
16974
17046
  */
16975
- lastProbedAt: external_exports.number(),
17047
+ minImportance: external_exports.number().min(0).max(1).optional(),
16976
17048
  /**
16977
- * Framework convention: every runtime-state slice carries this for the
16978
- * createRuntimeStateBridge stale-check helper. We keep it in sync with
16979
- * `lastProbedAt` on every write.
17049
+ * Minimum track dwell in SECONDS `(lastSeen firstSeen) / 1000`.
17050
+ * TRACK-END only: an `immediate` / object-event subject has no closed
17051
+ * lifespan, so a dwell condition never matches immediate delivery
17052
+ * (documented choice — the object-event record carries no `firstSeen`,
17053
+ * so dwell cannot be computed from what the subject actually carries).
16980
17054
  */
16981
- lastFetchedAt: external_exports.number()
17055
+ minDwellSeconds: external_exports.number().min(0).optional(),
17056
+ /**
17057
+ * Detection provenance filter. `any` (default / absent) matches every
17058
+ * source; otherwise the subject's source must equal it. Legacy records
17059
+ * with no stamped source are treated as `pipeline`. The union spans both
17060
+ * record kinds — object events carry `pipeline` | `onboard`, synthetic
17061
+ * tracks carry `sensor`.
17062
+ */
17063
+ source: external_exports.enum([
17064
+ "pipeline",
17065
+ "onboard",
17066
+ "sensor",
17067
+ "any"
17068
+ ]).optional(),
17069
+ /**
17070
+ * Minimum identity / plate MATCH confidence in [0,1] — DISTINCT from the
17071
+ * detector `minConfidence` (that gates the object-detection score; this
17072
+ * gates the recognition/OCR match score). Fails when the subject carries
17073
+ * no label-match confidence (never guess). TRACK-END only: the confidence
17074
+ * lives on the recognition result and reaches the subject at track close.
17075
+ *
17076
+ * What it measures precisely (plumbed at track close — the closer threads
17077
+ * the value into `NcTrackClosedInfo.labelConfidence`, the same seam as
17078
+ * `importance`): the BEST recognition match confidence observed for the
17079
+ * label the track carries at close — for a face, the peak cosine similarity
17080
+ * of the ASSIGNED identity (`FaceMatch.score`, reset on an identity switch);
17081
+ * for a plate, the peak OCR read score of the best-held plate
17082
+ * (`plateText.confidence`). When BOTH a face and a plate were recognized on
17083
+ * one track the higher of the two is used. A track that ended with no
17084
+ * confident identity/plate match carries no value, so the condition fails
17085
+ * closed for it (an un-recognized subject).
17086
+ */
17087
+ minLabelConfidence: external_exports.number().min(0).max(1).optional(),
17088
+ /**
17089
+ * DEVICE-EVENT only. Raw device event-type tokens (`EventFire.eventType`,
17090
+ * e.g. a doorbell `press` / `press_long`) — matched case-insensitively
17091
+ * against the token carried on the device-event subject (extracted from the
17092
+ * event-emitter runtime slice's `lastEvent.eventType`). Fails when the
17093
+ * subject carries no token. Doorbell-pulse / passive-sensor kinds emit no
17094
+ * eventType, so gate those with {@link sensorKinds} instead.
17095
+ */
17096
+ eventTypeTokens: external_exports.array(external_exports.string().min(1)).optional(),
17097
+ /**
17098
+ * DEVICE-EVENT only. Sensor/control taxonomy kinds (e.g. `doorbell`,
17099
+ * `contact`, `button`, `device-event`) — matched against the persisted
17100
+ * `SensorEvent.kind` (see `sensor-event-kinds.ts`). Membership is OR.
17101
+ */
17102
+ sensorKinds: external_exports.array(external_exports.string().min(1)).optional(),
17103
+ /**
17104
+ * PACKAGE-EVENT only. Which package phase fires the rule — `delivered`
17105
+ * (a parked parcel appeared), `picked-up` (it departed), or `both`. Fails
17106
+ * when the subject's phase does not match (a subject always carries a phase
17107
+ * on the package-event trigger).
17108
+ */
17109
+ packagePhase: external_exports.enum([
17110
+ "delivered",
17111
+ "picked-up",
17112
+ "both"
17113
+ ]).optional(),
17114
+ /**
17115
+ * PERSONAL-RULE custom zones (viewer-drawn). Inline normalized polygons
17116
+ * (MaskShape vocabulary). A record passes when its bbox overlaps ANY
17117
+ * listed polygon (ZoneEngine membership semantics). Evaluated only when
17118
+ * the subject carries a bbox; absent bbox ⇒ the condition FAILS.
17119
+ */
17120
+ customZones: external_exports.array(MaskPolygonShapeSchema).optional(),
17121
+ /**
17122
+ * DEVICE-EVENT only. ZoneAnalytics occupancy edge — fires when a device's
17123
+ * (optionally zone/class-scoped) occupancy count crosses the configured
17124
+ * threshold and holds for `sustainSeconds`. Fail-closed on missing
17125
+ * substrate (no snapshot / missing zone). See {@link NcOccupancyCondition}.
17126
+ */
17127
+ occupancy: NcOccupancyConditionSchema.optional()
16982
17128
  });
16983
- var featureProbeCapability = {
16984
- name: "feature-probe",
16985
- scope: "device",
16986
- deviceNative: true,
16987
- mode: "singleton",
16988
- methods: {},
16989
- events: {
16990
- /** Fires whenever a fresh probe completes (kernel-driven `reprobe()`
16991
- * or driver-initiated re-detect after a state change). */
16992
- onProbeChanged: { data: external_exports.object({
16993
- deviceId: external_exports.number(),
16994
- status: FeatureProbeStatusSchema
16995
- }) }
16996
- },
16997
- status: {
16998
- schema: FeatureProbeStatusSchema,
16999
- kind: "push"
17000
- },
17001
- runtimeState: FeatureProbeStatusSchema
17002
- };
17003
- var AirQualitySensorStatusSchema = external_exports.object({
17004
- /** Carbon dioxide concentration in ppm. */
17005
- co2Ppm: external_exports.number().min(0).optional(),
17006
- /** Total volatile organic compounds in ppb. */
17007
- vocPpb: external_exports.number().min(0).optional(),
17008
- /** Particulate matter ≤ 2.5 μm in µg/m³. */
17009
- pm25: external_exports.number().min(0).optional(),
17010
- /** Particulate matter ≤ 10 μm in µg/m³. */
17011
- pm10: external_exports.number().min(0).optional(),
17012
- /** Composite AQI value (typically 0..500). */
17013
- aqi: external_exports.number().optional(),
17014
- /** Ms epoch when the slice was last updated. */
17015
- lastFetchedAt: external_exports.number(),
17016
- /** Live display unit of the single metric this slice carries (e.g. HA
17017
- * `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
17018
- * upstream `sensor.*` entity surfaces ONE device_class, so one unit
17019
- * per slice is unambiguous. */
17020
- unit: external_exports.string().optional(),
17021
- /** Suggested decimal places for numeric display.
17022
- * Populated live from the upstream source when provided (e.g. HA
17023
- * `attributes.suggested_display_precision`). Falls back to
17024
- * auto-formatting when absent. */
17025
- precision: external_exports.number().int().min(0).max(10).optional()
17026
- });
17027
- var airQualitySensorCapability = {
17028
- name: "air-quality-sensor",
17029
- scope: "device",
17030
- deviceNative: true,
17031
- mode: "singleton",
17032
- deviceTypes: [DeviceType.Sensor],
17033
- methods: {},
17034
- status: {
17035
- schema: AirQualitySensorStatusSchema,
17036
- kind: "push"
17037
- },
17038
- runtimeState: AirQualitySensorStatusSchema
17039
- };
17040
- var AlarmStateSchema = external_exports.enum([
17041
- "disarmed",
17042
- "armed_home",
17043
- "armed_away",
17044
- "armed_night",
17045
- "armed_vacation",
17046
- "armed_custom_bypass",
17047
- "arming",
17048
- "disarming",
17049
- "pending",
17050
- "triggered"
17051
- ]);
17052
- var AlarmArmModeSchema = external_exports.enum([
17053
- "home",
17054
- "away",
17055
- "night",
17056
- "vacation",
17057
- "custom_bypass"
17058
- ]);
17059
- var AlarmPanelStatusSchema = external_exports.object({
17060
- /** Current lifecycle state. */
17061
- state: AlarmStateSchema,
17062
- /** Subset of arm modes the panel accepts. UI renders one button per
17063
- * mode in this list. */
17064
- availableModes: external_exports.array(AlarmArmModeSchema),
17065
- /** Whether the panel requires a PIN on arm / disarm. Mirrors
17066
- * `DeviceFeature.AlarmPinRequired` for slice consumers. */
17067
- requiresCode: external_exports.boolean(),
17068
- /** Ms epoch when the slice was last updated. */
17069
- lastChangedAt: external_exports.number()
17070
- });
17071
- var alarmPanelCapability = {
17072
- name: "alarm-panel",
17073
- scope: "device",
17074
- deviceNative: true,
17075
- mode: "singleton",
17076
- deviceTypes: [DeviceType.AlarmPanel],
17077
- methods: {
17078
- arm: method(external_exports.object({
17079
- deviceId: external_exports.number().int().nonnegative(),
17080
- mode: AlarmArmModeSchema,
17081
- /** Optional PIN code. Required when `requiresCode === true`.
17082
- * Passed through to the upstream service; never persisted. */
17083
- code: external_exports.string().min(1).optional()
17084
- }), external_exports.void(), {
17085
- kind: "mutation",
17086
- auth: "admin"
17087
- }),
17088
- disarm: method(external_exports.object({
17089
- deviceId: external_exports.number().int().nonnegative(),
17090
- code: external_exports.string().min(1).optional()
17091
- }), external_exports.void(), {
17092
- kind: "mutation",
17093
- auth: "admin"
17094
- }),
17095
- /**
17096
- * Force the panel into the `triggered` state — used by HA
17097
- * automations to surface external sensor events through the panel
17098
- * (e.g. a Reolink camera intrusion event firing the security
17099
- * system). Provider rejects when the panel hardware doesn't
17100
- * support a software-initiated trigger.
17101
- */
17102
- trigger: method(external_exports.object({ deviceId: external_exports.number().int().nonnegative() }), external_exports.void(), {
17103
- kind: "mutation",
17104
- auth: "admin"
17105
- })
17106
- },
17107
- status: {
17108
- schema: AlarmPanelStatusSchema,
17109
- kind: "push"
17110
- },
17129
+ var NcRuleTargetSchema = external_exports.object({
17130
+ /** `notification-output` Target id. */
17131
+ targetId: external_exports.string().min(1),
17111
17132
  /**
17112
- * Runtime-state slice mirrored by the kernel. UI panel reads the
17113
- * full slice; renders an arm button per `availableModes` entry and
17114
- * a PIN field iff `requiresCode === true`.
17133
+ * Per-backend passthrough. Recognized keys are mapped onto the canonical
17134
+ * Notification (`priority`, `level`, `sound`, `clickUrl`, `ttl`); the
17135
+ * degrade engine drops what the backend can't render.
17115
17136
  */
17116
- runtimeState: AlarmPanelStatusSchema
17117
- };
17118
- var AmbientLightSensorStatusSchema = external_exports.object({
17119
- /** Current illuminance in lux (lx). */
17120
- lux: external_exports.number().min(0),
17121
- /** Ms epoch when the slice was last updated. */
17122
- lastFetchedAt: external_exports.number(),
17123
- /** Live display unit from the upstream source (e.g. HA
17124
- * `attributes.unit_of_measurement`). The UI prefers this over the
17125
- * role's canonical unit. Absent → fall back to the canonical unit. */
17126
- unit: external_exports.string().optional(),
17127
- /** Suggested decimal places for numeric display.
17128
- * Populated live from the upstream source when provided (e.g. HA
17129
- * `attributes.suggested_display_precision`). Falls back to
17130
- * auto-formatting when absent. */
17131
- precision: external_exports.number().int().min(0).max(10).optional()
17137
+ params: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
17132
17138
  });
17133
- var ambientLightSensorCapability = {
17134
- name: "ambient-light-sensor",
17135
- scope: "device",
17136
- deviceNative: true,
17137
- mode: "singleton",
17138
- deviceTypes: [DeviceType.Sensor],
17139
- methods: {},
17140
- status: {
17141
- schema: AmbientLightSensorStatusSchema,
17142
- kind: "push"
17143
- },
17144
- runtimeState: AmbientLightSensorStatusSchema
17145
- };
17146
- var AudioClassSummarySchema = external_exports.object({
17147
- className: external_exports.string(),
17148
- /** Number of windows (chunks) where this class was the top hit. */
17149
- hits: external_exports.number().int().nonnegative(),
17150
- /** Mean score across those hits, clamped to [0,1]. */
17151
- avgScore: external_exports.number().min(0).max(1),
17152
- /** Peak score in the window. */
17153
- peakScore: external_exports.number().min(0).max(1)
17139
+ var NcMediaPolicySchema = external_exports.object({ attach: external_exports.enum([
17140
+ "best",
17141
+ "best-matching",
17142
+ "keyFrame",
17143
+ "none"
17144
+ ]).default("best") });
17145
+ var NcThrottleSchema = external_exports.object({
17146
+ cooldownSec: external_exports.number().int().min(0).max(86400).default(60),
17147
+ /** `rule` = one shared cooldown; `rule-device` = per-camera cooldown. */
17148
+ scope: external_exports.enum(["rule", "rule-device"]).default("rule-device")
17154
17149
  });
17155
- var AudioMetricsSnapshotSchema = external_exports.object({
17156
- /** Wall-clock timestamp (ms) of the most recent audio window. */
17157
- ts: external_exports.number().int(),
17158
- /** Sliding-window length (seconds) used for aggregation. */
17159
- windowSec: external_exports.number().int().positive(),
17160
- /** Latest level reading from the most recent window. */
17161
- level: external_exports.object({
17162
- rms: external_exports.number(),
17163
- dbfs: external_exports.number()
17150
+ var NcRuleInputSchema = external_exports.object({
17151
+ name: external_exports.string().min(1).max(200),
17152
+ enabled: external_exports.boolean().default(true),
17153
+ delivery: NcDeliverySchema,
17154
+ conditions: NcConditionsSchema.default({}),
17155
+ schedule: NcScheduleSchema.optional(),
17156
+ targets: external_exports.array(NcRuleTargetSchema).min(1),
17157
+ media: NcMediaPolicySchema.default({ attach: "best" }),
17158
+ throttle: NcThrottleSchema.default({
17159
+ cooldownSec: 60,
17160
+ scope: "rule-device"
17164
17161
  }),
17165
- /** Peak dBFS observed across the rolling window. */
17166
- peakDbfs: external_exports.number(),
17167
- /** Mean dBFS across the rolling window. */
17168
- avgDbfs: external_exports.number(),
17169
- /** Most recent above-threshold classification, or null on silence. */
17170
- current: external_exports.object({
17171
- className: external_exports.string(),
17172
- score: external_exports.number().min(0).max(1),
17173
- timestamp: external_exports.number().int()
17174
- }).nullable(),
17175
- /** Per-class summary across the rolling window keys are
17176
- * `macroClass` strings (e.g. `dog_bark`, `speech`, `glass_break`). */
17177
- byClass: external_exports.array(AudioClassSummarySchema).readonly()
17178
- });
17179
- var AudioMetricsHistoryPointSchema = external_exports.object({
17180
- /** Wall-clock ms when this sample was recorded. */
17181
- ts: external_exports.number().int(),
17182
- /** Instantaneous dBFS level at sample time. `null` for windows where
17183
- * the source had no level reading (rare; happens at decode startup). */
17184
- dbfs: external_exports.number().nullable(),
17185
- /** Rolling-window peak dBFS at sample time. Same window the live
17186
- * snapshot reports. */
17187
- peakDbfs: external_exports.number(),
17188
- /** Rolling-window mean dBFS at sample time. */
17189
- avgDbfs: external_exports.number(),
17190
- /** Dominant above-threshold class at sample time, or null on silence. */
17191
- topClass: external_exports.string().nullable(),
17192
- /** Score of the dominant class (`null` whenever `topClass` is null). */
17193
- topScore: external_exports.number().min(0).max(1).nullable()
17194
- });
17195
- var AudioMetricsHistorySchema = external_exports.object({
17196
- points: external_exports.array(AudioMetricsHistoryPointSchema).readonly(),
17197
- /** Actual ms between adjacent samples after any subsampling. */
17198
- effectiveSampleEveryMs: external_exports.number().int().positive(),
17199
- /** Wall-clock window covered by `points` (`points[N-1].ts - points[0].ts`),
17200
- * or `0` when there's fewer than 2 samples. */
17201
- windowMsActual: external_exports.number().int().nonnegative()
17162
+ /** `{{var}}` templating over camera/class/label/zones/confidence/time. */
17163
+ template: external_exports.object({
17164
+ title: external_exports.string().max(500).optional(),
17165
+ body: external_exports.string().max(2e3).optional()
17166
+ }).optional(),
17167
+ /** Canonical notification priority ordinal (1..5); per-target overridable. */
17168
+ priority: external_exports.number().int().min(1).max(5).default(3),
17169
+ /**
17170
+ * Ownership/visibility key. Absent = admin/global rule (unchanged legacy
17171
+ * behaviour, visible to all, read-only in the viewer). Present = personal
17172
+ * rule owned by this userId. Server-stamped; never trusted from a client.
17173
+ */
17174
+ ownerUserId: external_exports.string().optional()
17202
17175
  });
17203
- var audioMetricsCapability = {
17204
- name: "audio-metrics",
17205
- scope: "device",
17206
- mode: "singleton",
17207
- deviceTypes: [DeviceType.Camera],
17208
- methods: {
17209
- /** Latest snapshot for this device. Null until the analytics
17210
- * pipeline has processed at least one audio window. */
17211
- getCurrentSnapshot: method(external_exports.object({ deviceId: external_exports.number() }), AudioMetricsSnapshotSchema.nullable()),
17212
- /**
17213
- * Time-series view of recent audio-metrics samples. The provider
17214
- * keeps an in-memory ring of ~1Hz samples (matching the slice-
17215
- * write rate) capped at `MAX_HISTORY_POINTS_KEPT` (provider-side).
17216
- * `windowSec` selects how far back to read; `sampleEveryMs`
17217
- * downsamples by bucketed averaging when finer than the kept
17218
- * granularity. Empty `points` array on freshly-booted providers
17219
- * with no audio yet — same convention as `getCurrentSnapshot`.
17220
- */
17221
- getHistory: method(external_exports.object({
17222
- deviceId: external_exports.number(),
17223
- /** History window in seconds. Default 300 (5 minutes).
17224
- * Provider clamps to its retention cap if larger. */
17225
- windowSec: external_exports.number().int().positive().optional(),
17226
- /** Target sample interval in ms. Default 1000 (1 sample/second).
17227
- * Provider clamps to natural sample rate if smaller, and
17228
- * bucket-averages when bigger than the requested window
17229
- * would produce more than `maxPoints` samples. */
17230
- sampleEveryMs: external_exports.number().int().positive().optional()
17231
- }), AudioMetricsHistorySchema)
17232
- },
17233
- /** Reactive runtime-state mirror — live `device.state.audioMetrics.value`. */
17234
- runtimeState: AudioMetricsSnapshotSchema
17235
- };
17236
- var AutomationControlStatusSchema = external_exports.object({
17237
- /** Whether the automation is currently enabled. Disabled automations
17238
- * ignore their trigger block — manual `trigger` still works. */
17239
- enabled: external_exports.boolean(),
17240
- /** Whether the automation is currently executing its action block. */
17241
- isRunning: external_exports.boolean(),
17242
- /** Ms epoch of the last successful run. 0 when never run. */
17243
- lastTriggeredAt: external_exports.number(),
17244
- /** Failure description from the last completed run. Null on success
17245
- * or when never run. */
17246
- lastError: external_exports.string().nullable(),
17247
- /** Ms epoch when the slice was last updated. */
17248
- lastChangedAt: external_exports.number()
17176
+ var NcRulePatchSchema = NcRuleInputSchema.partial().extend({ disabledTargetIds: external_exports.array(external_exports.string()).optional() });
17177
+ var NcRuleSchema = NcRuleInputSchema.extend({
17178
+ id: external_exports.string(),
17179
+ /** userId of the admin who created the rule (server-stamped caller). */
17180
+ createdBy: external_exports.string(),
17181
+ createdAt: external_exports.number(),
17182
+ updatedAt: external_exports.number(),
17183
+ /**
17184
+ * Per-target opt-out set. A targetId here is suppressed for THIS rule at
17185
+ * send time. Only a target's OWNER may add/remove its id (server-checked
17186
+ * in `nc.setRuleTargetEnabled`). Defaults to empty.
17187
+ */
17188
+ disabledTargetIds: external_exports.array(external_exports.string()).default([])
17249
17189
  });
17250
- var automationControlCapability = {
17251
- name: "automation-control",
17252
- scope: "device",
17253
- deviceNative: true,
17190
+ var NcTestResultSchema = external_exports.object({
17191
+ recordId: external_exports.string(),
17192
+ recordKind: external_exports.enum([
17193
+ "object-event",
17194
+ "track",
17195
+ "device-event",
17196
+ "package-event"
17197
+ ]),
17198
+ deviceId: external_exports.number(),
17199
+ timestamp: external_exports.number(),
17200
+ wouldFire: external_exports.boolean(),
17201
+ /** Condition id that failed (first failing group), when `wouldFire` is false. */
17202
+ failedCondition: external_exports.string().optional(),
17203
+ className: external_exports.string().optional(),
17204
+ label: external_exports.string().optional()
17205
+ });
17206
+ var NcConditionDescriptorSchema = external_exports.object({
17207
+ /** Field id inside `NcConditions` (or `'schedule'` for the rule-level group). */
17208
+ id: external_exports.string(),
17209
+ group: external_exports.enum([
17210
+ "scope",
17211
+ "class",
17212
+ "zones",
17213
+ "quality",
17214
+ "label",
17215
+ "schedule",
17216
+ "device",
17217
+ "package",
17218
+ "occupancy"
17219
+ ]),
17220
+ label: external_exports.string(),
17221
+ /** Editor widget the UI renders — never hardcode per-condition forms. */
17222
+ valueType: external_exports.enum([
17223
+ "deviceIdList",
17224
+ "stringList",
17225
+ "number01",
17226
+ "number",
17227
+ "sourceSelect",
17228
+ "zoneSelection",
17229
+ "zoneIdList",
17230
+ "schedule",
17231
+ "plateMatcher",
17232
+ "packagePhase",
17233
+ "polygonDraw",
17234
+ "occupancy"
17235
+ ]),
17236
+ operator: external_exports.enum([
17237
+ "in",
17238
+ "notIn",
17239
+ "anyOf",
17240
+ "allOf",
17241
+ "gte",
17242
+ "fuzzyIn",
17243
+ "withinSchedule"
17244
+ ]),
17245
+ /** Which delivery kinds the condition applies to. */
17246
+ appliesTo: external_exports.array(NcDeliverySchema),
17247
+ phase: external_exports.string(),
17248
+ description: external_exports.string().optional()
17249
+ });
17250
+ var NcHistoryStatusSchema = external_exports.enum([
17251
+ "pending",
17252
+ "sent",
17253
+ "dead"
17254
+ ]);
17255
+ var NcHistoryRecordKindSchema = external_exports.enum([
17256
+ "object-event",
17257
+ "track-end",
17258
+ "device-event",
17259
+ "package-event"
17260
+ ]);
17261
+ var NcHistorySubjectSchema = external_exports.object({
17262
+ className: external_exports.string(),
17263
+ label: external_exports.string().optional(),
17264
+ confidence: external_exports.number().optional(),
17265
+ zones: external_exports.array(external_exports.string()),
17266
+ timestamp: external_exports.number()
17267
+ });
17268
+ var NcHistoryEntrySchema = external_exports.object({
17269
+ /** Outbox row id — the stable dedup id `ruleId:dedupRef:targetId`. */
17270
+ id: external_exports.string(),
17271
+ ruleId: external_exports.string(),
17272
+ /** Rule name frozen at fire time (outlives a later rename / delete). */
17273
+ ruleName: external_exports.string(),
17274
+ /** The rule urgency/trigger that produced this delivery. */
17275
+ delivery: NcDeliverySchema,
17276
+ targetId: external_exports.string(),
17277
+ deviceId: external_exports.number(),
17278
+ recordKind: NcHistoryRecordKindSchema,
17279
+ /** Event / track ref of the evaluated record (§3.2 `eventRef`). */
17280
+ recordId: external_exports.string(),
17281
+ /** Present for track-scoped deliveries (object-event / track-end). */
17282
+ trackId: external_exports.string().optional(),
17283
+ status: NcHistoryStatusSchema,
17284
+ /** Delivery attempts made so far. */
17285
+ attempts: external_exports.number().int(),
17286
+ /** Fire time (outbox enqueue). */
17287
+ createdAt: external_exports.number(),
17288
+ /** Last transition time (terminal for sent / dead). */
17289
+ updatedAt: external_exports.number(),
17290
+ /** Failure detail — present on a `dead` row. */
17291
+ error: external_exports.string().optional(),
17292
+ subject: NcHistorySubjectSchema
17293
+ });
17294
+ var NcHistoryFilterSchema = external_exports.object({
17295
+ ruleId: external_exports.string().optional(),
17296
+ deviceId: external_exports.number().optional(),
17297
+ status: NcHistoryStatusSchema.optional(),
17298
+ since: external_exports.number().optional(),
17299
+ until: external_exports.number().optional(),
17300
+ limit: external_exports.number().int().min(1).max(500).default(100)
17301
+ });
17302
+ var notificationRulesCapability = {
17303
+ name: "notification-rules",
17304
+ scope: "system",
17254
17305
  mode: "singleton",
17255
- deviceTypes: [DeviceType.Automation],
17256
17306
  methods: {
17257
- enable: method(external_exports.object({ deviceId: external_exports.number().int().nonnegative() }), external_exports.void(), {
17307
+ listRules: method(external_exports.object({}), external_exports.object({ rules: external_exports.array(NcRuleSchema) }), { auth: "admin" }),
17308
+ getRule: method(external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ rule: NcRuleSchema.nullable() }), { auth: "admin" }),
17309
+ createRule: method(external_exports.object({ rule: NcRuleInputSchema }), external_exports.object({ rule: NcRuleSchema }), {
17310
+ kind: "mutation",
17311
+ auth: "admin",
17312
+ caller: "required"
17313
+ }),
17314
+ updateRule: method(external_exports.object({
17315
+ ruleId: external_exports.string(),
17316
+ patch: NcRulePatchSchema
17317
+ }), external_exports.object({ rule: NcRuleSchema }), {
17318
+ kind: "mutation",
17319
+ auth: "admin",
17320
+ caller: "required"
17321
+ }),
17322
+ deleteRule: method(external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ success: external_exports.literal(true) }), {
17258
17323
  kind: "mutation",
17259
17324
  auth: "admin"
17260
17325
  }),
17261
- disable: method(external_exports.object({ deviceId: external_exports.number().int().nonnegative() }), external_exports.void(), {
17326
+ setRuleEnabled: method(external_exports.object({
17327
+ ruleId: external_exports.string(),
17328
+ enabled: external_exports.boolean()
17329
+ }), external_exports.object({ success: external_exports.literal(true) }), {
17262
17330
  kind: "mutation",
17263
17331
  auth: "admin"
17264
17332
  }),
17265
- trigger: method(external_exports.object({
17266
- deviceId: external_exports.number().int().nonnegative(),
17267
- /** When true, fires the action block while bypassing the
17268
- * automation's condition evaluation. Gated by
17269
- * `DeviceFeature.AutomationSkipCondition`. */
17270
- skipCondition: external_exports.boolean().optional()
17271
- }), external_exports.void(), {
17333
+ /**
17334
+ * Dry-run a rule against recently persisted records (object events for
17335
+ * `immediate`, closed tracks for `track-end`). Mutation kind only to
17336
+ * carry the full rule object safely; no side effects.
17337
+ */
17338
+ testRule: method(external_exports.object({
17339
+ rule: NcRuleInputSchema,
17340
+ lookbackMinutes: external_exports.number().int().min(1).max(1440).default(60)
17341
+ }), external_exports.object({ results: external_exports.array(NcTestResultSchema) }), {
17272
17342
  kind: "mutation",
17273
17343
  auth: "admin"
17274
- })
17275
- },
17276
- status: {
17277
- schema: AutomationControlStatusSchema,
17278
- kind: "push"
17279
- },
17344
+ }),
17345
+ getConditionCatalog: method(external_exports.object({}), external_exports.object({ catalog: external_exports.array(NcConditionDescriptorSchema) })),
17346
+ /**
17347
+ * Queryable delivery history — a read-only view over the durable outbox
17348
+ * (fired rule, subject summary, target, status, timestamps, error on a
17349
+ * dead row). Newest-first, bounded by `filter.limit`. Retention follows
17350
+ * the outbox's own terminal-row prune horizon (no separate history
17351
+ * horizon — single collection, single source of truth). Admin-only in
17352
+ * P1 (no user dimension); the P2 viewer History screen adds per-caller
17353
+ * scoping on the same method.
17354
+ */
17355
+ getHistory: method(external_exports.object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), external_exports.object({ entries: external_exports.array(NcHistoryEntrySchema) }), { auth: "admin" })
17356
+ }
17357
+ };
17358
+ var TimelapseTemplateSchema = external_exports.object({
17359
+ title: external_exports.string().max(500).optional(),
17360
+ body: external_exports.string().max(2e3).optional()
17361
+ });
17362
+ var NameField = external_exports.string().min(1).max(200);
17363
+ var DeviceIdsField = external_exports.array(external_exports.number()).min(1);
17364
+ var CadenceSecField = external_exports.number().int().min(2).max(3600);
17365
+ var FramerateField = external_exports.number().int().min(1).max(60);
17366
+ var TargetsField = external_exports.array(NcRuleTargetSchema).min(1);
17367
+ var PriorityField = external_exports.number().int().min(1).max(5);
17368
+ var TimelapseRuleInputSchema = external_exports.object({
17369
+ name: NameField,
17370
+ enabled: external_exports.boolean().default(true),
17371
+ /** Cameras sampled by this rule — one scratch dir + one artifact per device. */
17372
+ deviceIds: DeviceIdsField,
17280
17373
  /**
17281
- * Runtime-state slice mirrored by the kernel. UI automation tile
17282
- * reads `enabled` (toggle) + `isRunning` (spinner) + `lastError`
17283
- * (badge) directly.
17374
+ * Activation window(s). REQUIRED (unlike `NcRule`, where an absent schedule
17375
+ * means "always active"): a timelapse is defined by its window boundaries —
17376
+ * open clears the scratch, close assembles and delivers.
17284
17377
  */
17285
- runtimeState: AutomationControlStatusSchema
17286
- };
17287
- var BatteryStatusSchema = external_exports.object({
17288
- /** 0..100 inclusive. Firmware-reported. */
17289
- percentage: external_exports.number().min(0).max(100),
17378
+ schedule: NcScheduleSchema,
17379
+ /** Force-snapshot cadence inside the window, seconds (predecessor parity). */
17380
+ cadenceSec: CadenceSecField.default(15),
17381
+ /** Output frames per second of the assembled mp4 (predecessor parity). */
17382
+ framerate: FramerateField.default(10),
17383
+ /** `notification-output` targets the finished video/thumbnail is sent to. */
17384
+ targets: TargetsField,
17385
+ template: TimelapseTemplateSchema.optional(),
17386
+ /** Canonical notification priority ordinal (1..5); per-target overridable. */
17387
+ priority: PriorityField.default(3)
17388
+ });
17389
+ var TimelapseRulePatchSchema = external_exports.object({
17390
+ name: NameField.optional(),
17391
+ enabled: external_exports.boolean().optional(),
17392
+ deviceIds: DeviceIdsField.optional(),
17393
+ schedule: NcScheduleSchema.optional(),
17394
+ cadenceSec: CadenceSecField.optional(),
17395
+ framerate: FramerateField.optional(),
17396
+ targets: TargetsField.optional(),
17397
+ template: TimelapseTemplateSchema.nullable().optional(),
17398
+ priority: PriorityField.optional()
17399
+ });
17400
+ var TimelapseRuleSchema = TimelapseRuleInputSchema.extend({
17401
+ id: external_exports.string(),
17290
17402
  /**
17291
- * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
17292
- * Reolink-specific for the Solar Panel 2 accessory (will become
17293
- * common on other battery cams). `'none'` means running on battery
17294
- * alone.
17403
+ * Ownership/visibility key. Absent = admin/global rule (visible to all).
17404
+ * Present = personal rule owned by this userId. Server-stamped from the
17405
+ * resolved caller; never trusted from a client payload.
17295
17406
  */
17296
- charging: external_exports.enum([
17297
- "dc",
17298
- "solar",
17299
- "none"
17300
- ]),
17407
+ ownerUserId: external_exports.string().optional(),
17301
17408
  /**
17302
- * True when the camera firmware has gone into low-power mode. Battery
17303
- * providers MUST avoid polling during sleep reading the battery
17304
- * wakes the camera up and drains charge.
17409
+ * Epoch-ms of the last successful generation the 1-hour re-generation
17410
+ * guard's durable state (predecessor parity). Absent = never generated.
17305
17411
  */
17306
- sleeping: external_exports.boolean(),
17307
- /** Ms epoch of the last observation. Lets consumers reason about freshness. */
17308
- lastUpdated: external_exports.number(),
17412
+ lastGeneratedAt: external_exports.number().optional(),
17413
+ /** userId of the caller who created the rule (server-stamped). */
17414
+ createdBy: external_exports.string(),
17415
+ createdAt: external_exports.number(),
17416
+ updatedAt: external_exports.number()
17417
+ });
17418
+ var DeviceStatusSchema = external_exports.object({
17309
17419
  /**
17310
- * True when the source is a BINARY low-battery indicator (HA
17311
- * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
17312
- * charge level `percentage` is then a coarse stand-in (100 = normal,
17313
- * sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
17314
- * misleading exact percentage. Absent/false → genuine 0–100 % reading.
17420
+ * Device-level liveness. Drivers flip via `markOnline(boolean)` on
17421
+ * `BaseDevice`. Provider semantics vary RTSP aggregates broker
17422
+ * stream-health, Reolink reads firmware push events, ONVIF tracks
17423
+ * ping responses. This cap intentionally does NOT prescribe which
17424
+ * signal drives the flag.
17315
17425
  */
17316
- binary: external_exports.boolean().optional()
17426
+ online: external_exports.boolean(),
17427
+ /** Ms epoch of the last `online` transition. Lets consumers tell
17428
+ * apart "just came online" from "still online". */
17429
+ lastChangedAt: external_exports.number()
17317
17430
  });
17318
- var batteryCapability = {
17319
- name: "battery",
17431
+ var deviceStatusCapability = {
17432
+ name: "device-status",
17320
17433
  scope: "device",
17321
17434
  deviceNative: true,
17322
17435
  mode: "singleton",
17323
- deviceTypes: [
17324
- DeviceType.Camera,
17325
- DeviceType.Sensor,
17326
- DeviceType.Button,
17327
- DeviceType.Switch
17328
- ],
17329
- methods: {
17330
- /**
17331
- * Explicitly wake the camera from low-power sleep ahead of a
17332
- * streaming session start. Consumers that initiate a stream
17333
- * against a sleeping battery cam (HomeKit Secure Video, Alexa
17334
- * RTCSession, snapshot wrappers) call this with a short timeout
17335
- * before establishing the media pipeline — the broker's own
17336
- * passive wake-on-dial works but adds 5–7 seconds to first-frame,
17337
- * during which the consumer renders a black screen. Pre-waking
17338
- * compresses that gap.
17339
- *
17340
- * Returns `awoke: true` when the firmware acknowledged the wake
17341
- * before `timeoutMs`. Returns `awoke: false` when it timed out OR
17342
- * the cap surface is unavailable (no Baichuan / firmware
17343
- * channel); the caller should still attempt the stream — the
17344
- * passive broker wake remains as fallback.
17345
- */
17346
- wakeForStream: method(external_exports.object({
17347
- deviceId: external_exports.number(),
17348
- /** Bound on the wait. Sensible range 3000–10000ms. */
17349
- timeoutMs: external_exports.number().int().min(500).max(3e4).default(8e3)
17350
- }), external_exports.object({
17351
- awoke: external_exports.boolean(),
17352
- durationMs: external_exports.number()
17353
- }), { kind: "mutation" })
17354
- },
17436
+ methods: {},
17355
17437
  events: {
17356
- /**
17357
- * Emitted whenever the cached status changes (firmware push OR
17358
- * poll observes a delta). The DeviceEventPropagator mirrors this
17359
- * event on the parent chain — subscribing to a camera's source
17360
- * receives battery events from child accessories automatically.
17361
- */
17438
+ /** Emitted when `online` transitions. Mirrors the semantics of
17439
+ * `battery.onStatusChanged`. */
17362
17440
  onStatusChanged: { data: external_exports.object({
17363
17441
  deviceId: external_exports.number(),
17364
- status: BatteryStatusSchema
17442
+ status: DeviceStatusSchema
17365
17443
  }) }
17366
17444
  },
17367
17445
  status: {
17368
- schema: BatteryStatusSchema,
17369
- kind: "push",
17370
- empty: {
17371
- percentage: 0,
17372
- charging: "none",
17373
- sleeping: false,
17374
- lastUpdated: 0
17375
- }
17446
+ schema: DeviceStatusSchema,
17447
+ kind: "push"
17376
17448
  },
17449
+ runtimeState: DeviceStatusSchema
17450
+ };
17451
+ var FeatureProbeStatusSchema = external_exports.object({
17377
17452
  /**
17378
- * Runtime-state slice every provider that registers this cap
17379
- * stores the same shape under `device.runtimeState[battery]`.
17380
- * Cross-provider uniformity: a Reolink Argus, a Frigate sensor
17381
- * proxy, an ONVIF battery cam all read/write the same keys.
17382
- * Consumers (BatteryBadge, snapshot wrapper sleep gate) read once
17383
- * via `device.runtimeState.getCapState('battery')` regardless of
17384
- * the underlying driver.
17453
+ * Driver-specific flag bag. Each driver picks its own key names — the
17454
+ * cap deliberately does NOT enforce a closed enum here. Reolink keys:
17455
+ * `hasPtz`, `hasIntercom`, `hasDoorbell`, `hasFloodlight`, `hasSiren`,
17456
+ * `hasPirSensor`, `hasAutotrack`, `hasBattery`. Hikvision keys:
17457
+ * `hasSupplementalLight`, `lightHasWhiteLight`, `hasAlarmIo`, `hasPtz`.
17385
17458
  */
17386
- runtimeState: BatteryStatusSchema
17459
+ flags: external_exports.record(external_exports.string(), external_exports.unknown()),
17460
+ /**
17461
+ * Coarse driver-classification — lets cross-process consumers tell apart
17462
+ * cameras / battery-cams / NVRs without re-running the probe. `null`
17463
+ * before the first probe completes.
17464
+ */
17465
+ deviceType: external_exports.string().nullable(),
17466
+ /** Camera/firmware model string. `null` when the firmware doesn't expose it. */
17467
+ model: external_exports.string().nullable(),
17468
+ /** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
17469
+ channelCount: external_exports.number().nullable(),
17470
+ /**
17471
+ * Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
17472
+ * completes — drivers' `getAccessoryChildren()` should treat zero as
17473
+ * "probe not done yet, return empty" so accessories aren't spawned
17474
+ * before the firmware is queried.
17475
+ */
17476
+ lastProbedAt: external_exports.number(),
17477
+ /**
17478
+ * Framework convention: every runtime-state slice carries this for the
17479
+ * createRuntimeStateBridge stale-check helper. We keep it in sync with
17480
+ * `lastProbedAt` on every write.
17481
+ */
17482
+ lastFetchedAt: external_exports.number()
17483
+ });
17484
+ var featureProbeCapability = {
17485
+ name: "feature-probe",
17486
+ scope: "device",
17487
+ deviceNative: true,
17488
+ mode: "singleton",
17489
+ methods: {},
17490
+ events: {
17491
+ /** Fires whenever a fresh probe completes (kernel-driven `reprobe()`
17492
+ * or driver-initiated re-detect after a state change). */
17493
+ onProbeChanged: { data: external_exports.object({
17494
+ deviceId: external_exports.number(),
17495
+ status: FeatureProbeStatusSchema
17496
+ }) }
17497
+ },
17498
+ status: {
17499
+ schema: FeatureProbeStatusSchema,
17500
+ kind: "push"
17501
+ },
17502
+ runtimeState: FeatureProbeStatusSchema
17387
17503
  };
17388
- var BinaryStatusSchema = external_exports.object({
17389
- on: external_exports.boolean(),
17390
- /** Ms epoch of the last transition. 0 if never observed. */
17391
- lastChangedAt: external_exports.number()
17504
+ var AirQualitySensorStatusSchema = external_exports.object({
17505
+ /** Carbon dioxide concentration in ppm. */
17506
+ co2Ppm: external_exports.number().min(0).optional(),
17507
+ /** Total volatile organic compounds in ppb. */
17508
+ vocPpb: external_exports.number().min(0).optional(),
17509
+ /** Particulate matter ≤ 2.5 μm in µg/m³. */
17510
+ pm25: external_exports.number().min(0).optional(),
17511
+ /** Particulate matter ≤ 10 μm in µg/m³. */
17512
+ pm10: external_exports.number().min(0).optional(),
17513
+ /** Composite AQI value (typically 0..500). */
17514
+ aqi: external_exports.number().optional(),
17515
+ /** Ms epoch when the slice was last updated. */
17516
+ lastFetchedAt: external_exports.number(),
17517
+ /** Live display unit of the single metric this slice carries (e.g. HA
17518
+ * `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
17519
+ * upstream `sensor.*` entity surfaces ONE device_class, so one unit
17520
+ * per slice is unambiguous. */
17521
+ unit: external_exports.string().optional(),
17522
+ /** Suggested decimal places for numeric display.
17523
+ * Populated live from the upstream source when provided (e.g. HA
17524
+ * `attributes.suggested_display_precision`). Falls back to
17525
+ * auto-formatting when absent. */
17526
+ precision: external_exports.number().int().min(0).max(10).optional()
17392
17527
  });
17393
- var binaryCapability = {
17394
- name: "binary",
17528
+ var airQualitySensorCapability = {
17529
+ name: "air-quality-sensor",
17395
17530
  scope: "device",
17396
17531
  deviceNative: true,
17397
17532
  mode: "singleton",
17398
17533
  deviceTypes: [DeviceType.Sensor],
17399
17534
  methods: {},
17400
17535
  status: {
17401
- schema: BinaryStatusSchema,
17536
+ schema: AirQualitySensorStatusSchema,
17402
17537
  kind: "push"
17403
17538
  },
17404
- runtimeState: BinaryStatusSchema
17539
+ runtimeState: AirQualitySensorStatusSchema
17405
17540
  };
17406
- var BrightnessStatusSchema = external_exports.object({
17407
- /** Current level as 0..100 inclusive. Firmware-reported. */
17408
- percentage: external_exports.number().min(0).max(100),
17409
- /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
17541
+ var AlarmStateSchema = external_exports.enum([
17542
+ "disarmed",
17543
+ "armed_home",
17544
+ "armed_away",
17545
+ "armed_night",
17546
+ "armed_vacation",
17547
+ "armed_custom_bypass",
17548
+ "arming",
17549
+ "disarming",
17550
+ "pending",
17551
+ "triggered"
17552
+ ]);
17553
+ var AlarmArmModeSchema = external_exports.enum([
17554
+ "home",
17555
+ "away",
17556
+ "night",
17557
+ "vacation",
17558
+ "custom_bypass"
17559
+ ]);
17560
+ var AlarmPanelStatusSchema = external_exports.object({
17561
+ /** Current lifecycle state. */
17562
+ state: AlarmStateSchema,
17563
+ /** Subset of arm modes the panel accepts. UI renders one button per
17564
+ * mode in this list. */
17565
+ availableModes: external_exports.array(AlarmArmModeSchema),
17566
+ /** Whether the panel requires a PIN on arm / disarm. Mirrors
17567
+ * `DeviceFeature.AlarmPinRequired` for slice consumers. */
17568
+ requiresCode: external_exports.boolean(),
17569
+ /** Ms epoch when the slice was last updated. */
17410
17570
  lastChangedAt: external_exports.number()
17411
17571
  });
17412
- var brightnessCapability = {
17413
- name: "brightness",
17572
+ var alarmPanelCapability = {
17573
+ name: "alarm-panel",
17414
17574
  scope: "device",
17415
17575
  deviceNative: true,
17416
17576
  mode: "singleton",
17417
- deviceTypes: [DeviceType.Light],
17418
- methods: { setBrightness: method(external_exports.object({
17419
- deviceId: external_exports.number().int().nonnegative(),
17420
- percentage: external_exports.number().min(0).max(100)
17421
- }), external_exports.void(), {
17422
- kind: "mutation",
17423
- auth: "admin"
17424
- }) },
17425
- events: {
17577
+ deviceTypes: [DeviceType.AlarmPanel],
17578
+ methods: {
17579
+ arm: method(external_exports.object({
17580
+ deviceId: external_exports.number().int().nonnegative(),
17581
+ mode: AlarmArmModeSchema,
17582
+ /** Optional PIN code. Required when `requiresCode === true`.
17583
+ * Passed through to the upstream service; never persisted. */
17584
+ code: external_exports.string().min(1).optional()
17585
+ }), external_exports.void(), {
17586
+ kind: "mutation",
17587
+ auth: "admin"
17588
+ }),
17589
+ disarm: method(external_exports.object({
17590
+ deviceId: external_exports.number().int().nonnegative(),
17591
+ code: external_exports.string().min(1).optional()
17592
+ }), external_exports.void(), {
17593
+ kind: "mutation",
17594
+ auth: "admin"
17595
+ }),
17426
17596
  /**
17427
- * Emitted whenever the brightness changesoperator action OR
17428
- * firmware push. Subscribers (UI sliders, automation engines) react
17429
- * without polling.
17597
+ * Force the panel into the `triggered` stateused by HA
17598
+ * automations to surface external sensor events through the panel
17599
+ * (e.g. a Reolink camera intrusion event firing the security
17600
+ * system). Provider rejects when the panel hardware doesn't
17601
+ * support a software-initiated trigger.
17430
17602
  */
17431
- onBrightnessChanged: { data: external_exports.object({
17432
- deviceId: external_exports.number(),
17433
- percentage: external_exports.number().min(0).max(100),
17434
- lastChangedAt: external_exports.number()
17435
- }) }
17603
+ trigger: method(external_exports.object({ deviceId: external_exports.number().int().nonnegative() }), external_exports.void(), {
17604
+ kind: "mutation",
17605
+ auth: "admin"
17606
+ })
17436
17607
  },
17437
17608
  status: {
17438
- schema: BrightnessStatusSchema,
17439
- kind: "command-driven"
17609
+ schema: AlarmPanelStatusSchema,
17610
+ kind: "push"
17440
17611
  },
17441
17612
  /**
17442
- * Runtime-state slice — the last applied brightness level, mirrored
17443
- * by the kernel. Read via `device.state.brightness.value` so UI
17444
- * sliders surface the current level without polling the provider.
17613
+ * Runtime-state slice — mirrored by the kernel. UI panel reads the
17614
+ * full slice; renders an arm button per `availableModes` entry and
17615
+ * a PIN field iff `requiresCode === true`.
17445
17616
  */
17446
- runtimeState: BrightnessStatusSchema
17617
+ runtimeState: AlarmPanelStatusSchema
17447
17618
  };
17448
- var StreamFormatSchema = external_exports.enum([
17449
- "webrtc",
17450
- "hls",
17451
- "mjpeg",
17452
- "rtsp"
17453
- ]);
17454
- var RtspRestreamEntrySchema = external_exports.object({
17455
- brokerId: external_exports.string(),
17456
- url: external_exports.string(),
17457
- mutedUrl: external_exports.string(),
17458
- enabled: external_exports.boolean(),
17459
- /**
17460
- * Source-stream codec / resolution for the camStream this entry serves
17461
- * (the broker's "high"/"mid"/"low" profile slot for this device).
17462
- * Used by exporter pickers (`pickPreferredRtspEntry`) to resolve
17463
- * `streamPreference: 'auto'` to the slot whose source is closest to
17464
- * the consumer's target — Alexa wants ~720p, HomeKit wants ~1080p,
17465
- * and there's no point dialling the 4K slot for an Echo Show. Absent
17466
- * when the source publisher never advertised the field; pickers fall
17467
- * back to first-enabled in that case.
17468
- */
17469
- codec: external_exports.string().optional(),
17470
- resolution: external_exports.object({
17471
- width: external_exports.number().int().positive(),
17472
- height: external_exports.number().int().positive()
17473
- }).optional()
17619
+ var AmbientLightSensorStatusSchema = external_exports.object({
17620
+ /** Current illuminance in lux (lx). */
17621
+ lux: external_exports.number().min(0),
17622
+ /** Ms epoch when the slice was last updated. */
17623
+ lastFetchedAt: external_exports.number(),
17624
+ /** Live display unit from the upstream source (e.g. HA
17625
+ * `attributes.unit_of_measurement`). The UI prefers this over the
17626
+ * role's canonical unit. Absent → fall back to the canonical unit. */
17627
+ unit: external_exports.string().optional(),
17628
+ /** Suggested decimal places for numeric display.
17629
+ * Populated live from the upstream source when provided (e.g. HA
17630
+ * `attributes.suggested_display_precision`). Falls back to
17631
+ * auto-formatting when absent. */
17632
+ precision: external_exports.number().int().min(0).max(10).optional()
17474
17633
  });
17475
- var BrokerRtspClientSchema = external_exports.object({
17476
- sessionId: external_exports.string(),
17477
- remoteAddr: external_exports.string(),
17478
- /** Client `User-Agent` (e.g. `recorder` for the recorder's RTSP pull), or
17479
- * null/absent when the client sent none. Lets the UI label a consumer by
17480
- * purpose. Optional so a client built against an older schema stays valid. */
17481
- userAgent: external_exports.string().nullish(),
17482
- playing: external_exports.boolean(),
17483
- muted: external_exports.boolean(),
17484
- connectedAt: external_exports.number(),
17485
- lastRtpAt: external_exports.number(),
17486
- bytesSent: external_exports.number()
17634
+ var ambientLightSensorCapability = {
17635
+ name: "ambient-light-sensor",
17636
+ scope: "device",
17637
+ deviceNative: true,
17638
+ mode: "singleton",
17639
+ deviceTypes: [DeviceType.Sensor],
17640
+ methods: {},
17641
+ status: {
17642
+ schema: AmbientLightSensorStatusSchema,
17643
+ kind: "push"
17644
+ },
17645
+ runtimeState: AmbientLightSensorStatusSchema
17646
+ };
17647
+ var AudioClassSummarySchema = external_exports.object({
17648
+ className: external_exports.string(),
17649
+ /** Number of windows (chunks) where this class was the top hit. */
17650
+ hits: external_exports.number().int().nonnegative(),
17651
+ /** Mean score across those hits, clamped to [0,1]. */
17652
+ avgScore: external_exports.number().min(0).max(1),
17653
+ /** Peak score in the window. */
17654
+ peakScore: external_exports.number().min(0).max(1)
17487
17655
  });
17488
- var BrokerDecodedClientSchema = external_exports.object({
17489
- tag: external_exports.string(),
17490
- subscribedAt: external_exports.number(),
17491
- maxFps: external_exports.number(),
17492
- framesDelivered: external_exports.number(),
17493
- framesDropped: external_exports.number()
17656
+ var AudioMetricsSnapshotSchema = external_exports.object({
17657
+ /** Wall-clock timestamp (ms) of the most recent audio window. */
17658
+ ts: external_exports.number().int(),
17659
+ /** Sliding-window length (seconds) used for aggregation. */
17660
+ windowSec: external_exports.number().int().positive(),
17661
+ /** Latest level reading from the most recent window. */
17662
+ level: external_exports.object({
17663
+ rms: external_exports.number(),
17664
+ dbfs: external_exports.number()
17665
+ }),
17666
+ /** Peak dBFS observed across the rolling window. */
17667
+ peakDbfs: external_exports.number(),
17668
+ /** Mean dBFS across the rolling window. */
17669
+ avgDbfs: external_exports.number(),
17670
+ /** Most recent above-threshold classification, or null on silence. */
17671
+ current: external_exports.object({
17672
+ className: external_exports.string(),
17673
+ score: external_exports.number().min(0).max(1),
17674
+ timestamp: external_exports.number().int()
17675
+ }).nullable(),
17676
+ /** Per-class summary across the rolling window — keys are
17677
+ * `macroClass` strings (e.g. `dog_bark`, `speech`, `glass_break`). */
17678
+ byClass: external_exports.array(AudioClassSummarySchema).readonly()
17494
17679
  });
17495
- var BrokerAudioClientSchema = external_exports.object({
17496
- tag: external_exports.string(),
17497
- subscribedAt: external_exports.number(),
17498
- chunksDelivered: external_exports.number()
17680
+ var AudioMetricsHistoryPointSchema = external_exports.object({
17681
+ /** Wall-clock ms when this sample was recorded. */
17682
+ ts: external_exports.number().int(),
17683
+ /** Instantaneous dBFS level at sample time. `null` for windows where
17684
+ * the source had no level reading (rare; happens at decode startup). */
17685
+ dbfs: external_exports.number().nullable(),
17686
+ /** Rolling-window peak dBFS at sample time. Same window the live
17687
+ * snapshot reports. */
17688
+ peakDbfs: external_exports.number(),
17689
+ /** Rolling-window mean dBFS at sample time. */
17690
+ avgDbfs: external_exports.number(),
17691
+ /** Dominant above-threshold class at sample time, or null on silence. */
17692
+ topClass: external_exports.string().nullable(),
17693
+ /** Score of the dominant class (`null` whenever `topClass` is null). */
17694
+ topScore: external_exports.number().min(0).max(1).nullable()
17499
17695
  });
17500
- var BrokerConsumerKindSchema = external_exports.enum([
17501
- "alexa",
17502
- "homekit",
17503
- "webrtc-browser",
17504
- "webrtc-mobile",
17505
- "webrtc-whep",
17506
- "rtsp-listen",
17507
- "derived-broker",
17508
- "recording",
17509
- "pipeline",
17510
- "snapshot",
17511
- "warmup",
17512
- "unknown"
17513
- ]);
17514
- var BrokerConsumerAttributionSchema = external_exports.object({
17515
- kind: BrokerConsumerKindSchema,
17696
+ var AudioMetricsHistorySchema = external_exports.object({
17697
+ points: external_exports.array(AudioMetricsHistoryPointSchema).readonly(),
17698
+ /** Actual ms between adjacent samples after any subsampling. */
17699
+ effectiveSampleEveryMs: external_exports.number().int().positive(),
17700
+ /** Wall-clock window covered by `points` (`points[N-1].ts - points[0].ts`),
17701
+ * or `0` when there's fewer than 2 samples. */
17702
+ windowMsActual: external_exports.number().int().nonnegative()
17703
+ });
17704
+ var audioMetricsCapability = {
17705
+ name: "audio-metrics",
17706
+ scope: "device",
17707
+ mode: "singleton",
17708
+ deviceTypes: [DeviceType.Camera],
17709
+ methods: {
17710
+ /** Latest snapshot for this device. Null until the analytics
17711
+ * pipeline has processed at least one audio window. */
17712
+ getCurrentSnapshot: method(external_exports.object({ deviceId: external_exports.number() }), AudioMetricsSnapshotSchema.nullable()),
17713
+ /**
17714
+ * Time-series view of recent audio-metrics samples. The provider
17715
+ * keeps an in-memory ring of ~1Hz samples (matching the slice-
17716
+ * write rate) capped at `MAX_HISTORY_POINTS_KEPT` (provider-side).
17717
+ * `windowSec` selects how far back to read; `sampleEveryMs`
17718
+ * downsamples by bucketed averaging when finer than the kept
17719
+ * granularity. Empty `points` array on freshly-booted providers
17720
+ * with no audio yet — same convention as `getCurrentSnapshot`.
17721
+ */
17722
+ getHistory: method(external_exports.object({
17723
+ deviceId: external_exports.number(),
17724
+ /** History window in seconds. Default 300 (5 minutes).
17725
+ * Provider clamps to its retention cap if larger. */
17726
+ windowSec: external_exports.number().int().positive().optional(),
17727
+ /** Target sample interval in ms. Default 1000 (1 sample/second).
17728
+ * Provider clamps to natural sample rate if smaller, and
17729
+ * bucket-averages when bigger than the requested window
17730
+ * would produce more than `maxPoints` samples. */
17731
+ sampleEveryMs: external_exports.number().int().positive().optional()
17732
+ }), AudioMetricsHistorySchema)
17733
+ },
17734
+ /** Reactive runtime-state mirror — live `device.state.audioMetrics.value`. */
17735
+ runtimeState: AudioMetricsSnapshotSchema
17736
+ };
17737
+ var AutomationControlStatusSchema = external_exports.object({
17738
+ /** Whether the automation is currently enabled. Disabled automations
17739
+ * ignore their trigger block — manual `trigger` still works. */
17740
+ enabled: external_exports.boolean(),
17741
+ /** Whether the automation is currently executing its action block. */
17742
+ isRunning: external_exports.boolean(),
17743
+ /** Ms epoch of the last successful run. 0 when never run. */
17744
+ lastTriggeredAt: external_exports.number(),
17745
+ /** Failure description from the last completed run. Null on success
17746
+ * or when never run. */
17747
+ lastError: external_exports.string().nullable(),
17748
+ /** Ms epoch when the slice was last updated. */
17749
+ lastChangedAt: external_exports.number()
17750
+ });
17751
+ var automationControlCapability = {
17752
+ name: "automation-control",
17753
+ scope: "device",
17754
+ deviceNative: true,
17755
+ mode: "singleton",
17756
+ deviceTypes: [DeviceType.Automation],
17757
+ methods: {
17758
+ enable: method(external_exports.object({ deviceId: external_exports.number().int().nonnegative() }), external_exports.void(), {
17759
+ kind: "mutation",
17760
+ auth: "admin"
17761
+ }),
17762
+ disable: method(external_exports.object({ deviceId: external_exports.number().int().nonnegative() }), external_exports.void(), {
17763
+ kind: "mutation",
17764
+ auth: "admin"
17765
+ }),
17766
+ trigger: method(external_exports.object({
17767
+ deviceId: external_exports.number().int().nonnegative(),
17768
+ /** When true, fires the action block while bypassing the
17769
+ * automation's condition evaluation. Gated by
17770
+ * `DeviceFeature.AutomationSkipCondition`. */
17771
+ skipCondition: external_exports.boolean().optional()
17772
+ }), external_exports.void(), {
17773
+ kind: "mutation",
17774
+ auth: "admin"
17775
+ })
17776
+ },
17777
+ status: {
17778
+ schema: AutomationControlStatusSchema,
17779
+ kind: "push"
17780
+ },
17516
17781
  /**
17517
- * Free-form label intended to disambiguate consumers OF THE SAME kind
17518
- * on the same broker e.g. user name, device alias. Should NOT repeat
17519
- * the kind / cam / cam-stream / sessionId (those are surfaced by
17520
- * dedicated fields). When empty the widget falls back to `${kind} ·
17521
- * <sessionId tail>`.
17782
+ * Runtime-state slice mirrored by the kernel. UI automation tile
17783
+ * reads `enabled` (toggle) + `isRunning` (spinner) + `lastError`
17784
+ * (badge) directly.
17522
17785
  */
17523
- label: external_exports.string().optional(),
17524
- /**
17525
- * Which part of the encoded plane this subscription consumes:
17526
- * - `'audio'` audio packets only
17527
- * - `'video'` — video packets only
17786
+ runtimeState: AutomationControlStatusSchema
17787
+ };
17788
+ var BatteryStatusSchema = external_exports.object({
17789
+ /** 0..100 inclusive. Firmware-reported. */
17790
+ percentage: external_exports.number().min(0).max(100),
17791
+ /**
17792
+ * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
17793
+ * Reolink-specific for the Solar Panel 2 accessory (will become
17794
+ * common on other battery cams). `'none'` means running on battery
17795
+ * alone.
17796
+ */
17797
+ charging: external_exports.enum([
17798
+ "dc",
17799
+ "solar",
17800
+ "none"
17801
+ ]),
17802
+ /**
17803
+ * True when the camera firmware has gone into low-power mode. Battery
17804
+ * providers MUST avoid polling during sleep — reading the battery
17805
+ * wakes the camera up and drains charge.
17806
+ */
17807
+ sleeping: external_exports.boolean(),
17808
+ /** Ms epoch of the last observation. Lets consumers reason about freshness. */
17809
+ lastUpdated: external_exports.number(),
17810
+ /**
17811
+ * True when the source is a BINARY low-battery indicator (HA
17812
+ * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
17813
+ * charge level — `percentage` is then a coarse stand-in (100 = normal,
17814
+ * sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
17815
+ * misleading exact percentage. Absent/false → genuine 0–100 % reading.
17816
+ */
17817
+ binary: external_exports.boolean().optional()
17818
+ });
17819
+ var batteryCapability = {
17820
+ name: "battery",
17821
+ scope: "device",
17822
+ deviceNative: true,
17823
+ mode: "singleton",
17824
+ deviceTypes: [
17825
+ DeviceType.Camera,
17826
+ DeviceType.Sensor,
17827
+ DeviceType.Button,
17828
+ DeviceType.Switch
17829
+ ],
17830
+ methods: {
17831
+ /**
17832
+ * Explicitly wake the camera from low-power sleep ahead of a
17833
+ * streaming session start. Consumers that initiate a stream
17834
+ * against a sleeping battery cam (HomeKit Secure Video, Alexa
17835
+ * RTCSession, snapshot wrappers) call this with a short timeout
17836
+ * before establishing the media pipeline — the broker's own
17837
+ * passive wake-on-dial works but adds 5–7 seconds to first-frame,
17838
+ * during which the consumer renders a black screen. Pre-waking
17839
+ * compresses that gap.
17840
+ *
17841
+ * Returns `awoke: true` when the firmware acknowledged the wake
17842
+ * before `timeoutMs`. Returns `awoke: false` when it timed out OR
17843
+ * the cap surface is unavailable (no Baichuan / firmware
17844
+ * channel); the caller should still attempt the stream — the
17845
+ * passive broker wake remains as fallback.
17846
+ */
17847
+ wakeForStream: method(external_exports.object({
17848
+ deviceId: external_exports.number(),
17849
+ /** Bound on the wait. Sensible range 3000–10000ms. */
17850
+ timeoutMs: external_exports.number().int().min(500).max(3e4).default(8e3)
17851
+ }), external_exports.object({
17852
+ awoke: external_exports.boolean(),
17853
+ durationMs: external_exports.number()
17854
+ }), { kind: "mutation" })
17855
+ },
17856
+ events: {
17857
+ /**
17858
+ * Emitted whenever the cached status changes (firmware push OR
17859
+ * poll observes a delta). The DeviceEventPropagator mirrors this
17860
+ * event on the parent chain — subscribing to a camera's source
17861
+ * receives battery events from child accessories automatically.
17862
+ */
17863
+ onStatusChanged: { data: external_exports.object({
17864
+ deviceId: external_exports.number(),
17865
+ status: BatteryStatusSchema
17866
+ }) }
17867
+ },
17868
+ status: {
17869
+ schema: BatteryStatusSchema,
17870
+ kind: "push",
17871
+ empty: {
17872
+ percentage: 0,
17873
+ charging: "none",
17874
+ sleeping: false,
17875
+ lastUpdated: 0
17876
+ }
17877
+ },
17878
+ /**
17879
+ * Runtime-state slice — every provider that registers this cap
17880
+ * stores the same shape under `device.runtimeState[battery]`.
17881
+ * Cross-provider uniformity: a Reolink Argus, a Frigate sensor
17882
+ * proxy, an ONVIF battery cam all read/write the same keys.
17883
+ * Consumers (BatteryBadge, snapshot wrapper sleep gate) read once
17884
+ * via `device.runtimeState.getCapState('battery')` regardless of
17885
+ * the underlying driver.
17886
+ */
17887
+ runtimeState: BatteryStatusSchema
17888
+ };
17889
+ var BinaryStatusSchema = external_exports.object({
17890
+ on: external_exports.boolean(),
17891
+ /** Ms epoch of the last transition. 0 if never observed. */
17892
+ lastChangedAt: external_exports.number()
17893
+ });
17894
+ var binaryCapability = {
17895
+ name: "binary",
17896
+ scope: "device",
17897
+ deviceNative: true,
17898
+ mode: "singleton",
17899
+ deviceTypes: [DeviceType.Sensor],
17900
+ methods: {},
17901
+ status: {
17902
+ schema: BinaryStatusSchema,
17903
+ kind: "push"
17904
+ },
17905
+ runtimeState: BinaryStatusSchema
17906
+ };
17907
+ var BrightnessStatusSchema = external_exports.object({
17908
+ /** Current level as 0..100 inclusive. Firmware-reported. */
17909
+ percentage: external_exports.number().min(0).max(100),
17910
+ /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
17911
+ lastChangedAt: external_exports.number()
17912
+ });
17913
+ var brightnessCapability = {
17914
+ name: "brightness",
17915
+ scope: "device",
17916
+ deviceNative: true,
17917
+ mode: "singleton",
17918
+ deviceTypes: [DeviceType.Light],
17919
+ methods: { setBrightness: method(external_exports.object({
17920
+ deviceId: external_exports.number().int().nonnegative(),
17921
+ percentage: external_exports.number().min(0).max(100)
17922
+ }), external_exports.void(), {
17923
+ kind: "mutation",
17924
+ auth: "admin"
17925
+ }) },
17926
+ events: {
17927
+ /**
17928
+ * Emitted whenever the brightness changes — operator action OR
17929
+ * firmware push. Subscribers (UI sliders, automation engines) react
17930
+ * without polling.
17931
+ */
17932
+ onBrightnessChanged: { data: external_exports.object({
17933
+ deviceId: external_exports.number(),
17934
+ percentage: external_exports.number().min(0).max(100),
17935
+ lastChangedAt: external_exports.number()
17936
+ }) }
17937
+ },
17938
+ status: {
17939
+ schema: BrightnessStatusSchema,
17940
+ kind: "command-driven"
17941
+ },
17942
+ /**
17943
+ * Runtime-state slice — the last applied brightness level, mirrored
17944
+ * by the kernel. Read via `device.state.brightness.value` so UI
17945
+ * sliders surface the current level without polling the provider.
17946
+ */
17947
+ runtimeState: BrightnessStatusSchema
17948
+ };
17949
+ var StreamFormatSchema = external_exports.enum([
17950
+ "webrtc",
17951
+ "hls",
17952
+ "mjpeg",
17953
+ "rtsp"
17954
+ ]);
17955
+ var RtspRestreamEntrySchema = external_exports.object({
17956
+ brokerId: external_exports.string(),
17957
+ url: external_exports.string(),
17958
+ mutedUrl: external_exports.string(),
17959
+ enabled: external_exports.boolean(),
17960
+ /**
17961
+ * Source-stream codec / resolution for the camStream this entry serves
17962
+ * (the broker's "high"/"mid"/"low" profile slot for this device).
17963
+ * Used by exporter pickers (`pickPreferredRtspEntry`) to resolve
17964
+ * `streamPreference: 'auto'` to the slot whose source is closest to
17965
+ * the consumer's target — Alexa wants ~720p, HomeKit wants ~1080p,
17966
+ * and there's no point dialling the 4K slot for an Echo Show. Absent
17967
+ * when the source publisher never advertised the field; pickers fall
17968
+ * back to first-enabled in that case.
17969
+ */
17970
+ codec: external_exports.string().optional(),
17971
+ resolution: external_exports.object({
17972
+ width: external_exports.number().int().positive(),
17973
+ height: external_exports.number().int().positive()
17974
+ }).optional()
17975
+ });
17976
+ var BrokerRtspClientSchema = external_exports.object({
17977
+ sessionId: external_exports.string(),
17978
+ remoteAddr: external_exports.string(),
17979
+ /** Client `User-Agent` (e.g. `recorder` for the recorder's RTSP pull), or
17980
+ * null/absent when the client sent none. Lets the UI label a consumer by
17981
+ * purpose. Optional so a client built against an older schema stays valid. */
17982
+ userAgent: external_exports.string().nullish(),
17983
+ playing: external_exports.boolean(),
17984
+ muted: external_exports.boolean(),
17985
+ connectedAt: external_exports.number(),
17986
+ lastRtpAt: external_exports.number(),
17987
+ bytesSent: external_exports.number()
17988
+ });
17989
+ var BrokerDecodedClientSchema = external_exports.object({
17990
+ tag: external_exports.string(),
17991
+ subscribedAt: external_exports.number(),
17992
+ maxFps: external_exports.number(),
17993
+ framesDelivered: external_exports.number(),
17994
+ framesDropped: external_exports.number()
17995
+ });
17996
+ var BrokerAudioClientSchema = external_exports.object({
17997
+ tag: external_exports.string(),
17998
+ subscribedAt: external_exports.number(),
17999
+ chunksDelivered: external_exports.number()
18000
+ });
18001
+ var BrokerConsumerKindSchema = external_exports.enum([
18002
+ "alexa",
18003
+ "homekit",
18004
+ "webrtc-browser",
18005
+ "webrtc-mobile",
18006
+ "webrtc-whep",
18007
+ "rtsp-listen",
18008
+ "derived-broker",
18009
+ "recording",
18010
+ "pipeline",
18011
+ "snapshot",
18012
+ "warmup",
18013
+ "unknown"
18014
+ ]);
18015
+ var BrokerConsumerAttributionSchema = external_exports.object({
18016
+ kind: BrokerConsumerKindSchema,
18017
+ /**
18018
+ * Free-form label intended to disambiguate consumers OF THE SAME kind
18019
+ * on the same broker — e.g. user name, device alias. Should NOT repeat
18020
+ * the kind / cam / cam-stream / sessionId (those are surfaced by
18021
+ * dedicated fields). When empty the widget falls back to `${kind} ·
18022
+ * <sessionId tail>`.
18023
+ */
18024
+ label: external_exports.string().optional(),
18025
+ /**
18026
+ * Which part of the encoded plane this subscription consumes:
18027
+ * - `'audio'` — audio packets only
18028
+ * - `'video'` — video packets only
17528
18029
  * - `'both'` — both video + audio (typical for AnnexB push paths
17529
18030
  * that don't split the source RTP)
17530
18031
  *
@@ -20705,55 +21206,10 @@ var motionTriggerCapability = {
20705
21206
  },
20706
21207
  runtimeState: MotionTriggerRuntimeStateSchema
20707
21208
  };
20708
- var MaskPointSchema = external_exports.object({
20709
- x: external_exports.number(),
20710
- y: external_exports.number()
20711
- });
20712
- var MaskRectShapeSchema = external_exports.object({
20713
- kind: external_exports.literal("rect"),
20714
- x: external_exports.number(),
20715
- y: external_exports.number(),
20716
- width: external_exports.number(),
20717
- height: external_exports.number()
20718
- });
20719
- var MaskPolygonShapeSchema = external_exports.object({
20720
- kind: external_exports.literal("polygon"),
20721
- points: external_exports.array(MaskPointSchema)
20722
- });
20723
- var MaskGridShapeSchema = external_exports.object({
20724
- kind: external_exports.literal("grid"),
20725
- gridWidth: external_exports.number(),
20726
- gridHeight: external_exports.number(),
20727
- cells: external_exports.array(external_exports.boolean())
20728
- });
20729
- var MaskLineShapeSchema = external_exports.object({
20730
- kind: external_exports.literal("line"),
20731
- points: external_exports.array(MaskPointSchema)
20732
- });
20733
- var MaskShapeSchema = external_exports.discriminatedUnion("kind", [
20734
- MaskRectShapeSchema,
20735
- MaskPolygonShapeSchema,
20736
- MaskGridShapeSchema,
20737
- MaskLineShapeSchema
20738
- ]);
20739
- var MaskShapeKindSchema = external_exports.enum([
20740
- "rect",
20741
- "polygon",
20742
- "grid",
20743
- "line"
20744
- ]);
20745
- var MaskPolygonVerticesSchema = external_exports.object({
20746
- min: external_exports.number(),
20747
- max: external_exports.number()
20748
- });
20749
- var MaskGridDimsSchema = external_exports.object({
20750
- width: external_exports.number(),
20751
- height: external_exports.number()
20752
- });
20753
- var MotionZoneRegionSchema = external_exports.object({
20754
- id: external_exports.number(),
20755
- enabled: external_exports.boolean(),
20756
- shape: MaskGridShapeSchema
21209
+ var MotionZoneRegionSchema = external_exports.object({
21210
+ id: external_exports.number(),
21211
+ enabled: external_exports.boolean(),
21212
+ shape: MaskGridShapeSchema
20757
21213
  });
20758
21214
  var MotionZoneStatusSchema = external_exports.object({
20759
21215
  enabled: external_exports.boolean(),
@@ -23277,6 +23733,60 @@ var authProviderCapability = {
23277
23733
  /** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
23278
23734
  mount: { kind: "skip" }
23279
23735
  };
23736
+ var TerminalSessionInfoSchema = external_exports.object({
23737
+ /** Opaque session id minted by the provider on `openSession`. */
23738
+ sessionId: external_exports.string(),
23739
+ /** The pre-declared profile this session runs (never a free-form command). */
23740
+ profileId: external_exports.string(),
23741
+ /** Human-readable profile label for the UI session list. */
23742
+ label: external_exports.string(),
23743
+ cols: external_exports.number().int().positive(),
23744
+ rows: external_exports.number().int().positive(),
23745
+ /** ms-epoch the session's pty was spawned. */
23746
+ startedAt: external_exports.number()
23747
+ });
23748
+ var TerminalProfileInfoSchema = external_exports.object({
23749
+ profileId: external_exports.string(),
23750
+ label: external_exports.string(),
23751
+ description: external_exports.string().optional()
23752
+ });
23753
+ var terminalSessionCapability = {
23754
+ name: "terminal-session",
23755
+ scope: "system",
23756
+ mode: "singleton",
23757
+ methods: {
23758
+ /** Pre-declared profiles the operator may open. */
23759
+ listProfiles: method(external_exports.void(), external_exports.array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
23760
+ /** Live sessions currently hosted by the provider. */
23761
+ listSessions: method(external_exports.void(), external_exports.array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }),
23762
+ /**
23763
+ * Spawn a pty for an allowlisted profile at the given grid size and return
23764
+ * its session id. Output/input then flow over the data plane by that id.
23765
+ */
23766
+ openSession: method(external_exports.object({
23767
+ profileId: external_exports.string(),
23768
+ cols: external_exports.number().int().positive(),
23769
+ rows: external_exports.number().int().positive()
23770
+ }), TerminalSessionInfoSchema, {
23771
+ kind: "mutation",
23772
+ auth: "admin"
23773
+ }),
23774
+ /** Resize a live session's pty (reflows the running program). */
23775
+ resize: method(external_exports.object({
23776
+ sessionId: external_exports.string(),
23777
+ cols: external_exports.number().int().positive(),
23778
+ rows: external_exports.number().int().positive()
23779
+ }), external_exports.void(), {
23780
+ kind: "mutation",
23781
+ auth: "admin"
23782
+ }),
23783
+ /** Terminate a live session and release its pty. */
23784
+ close: method(external_exports.object({ sessionId: external_exports.string() }), external_exports.void(), {
23785
+ kind: "mutation",
23786
+ auth: "admin"
23787
+ })
23788
+ }
23789
+ };
23280
23790
  var BackupDestinationInfoSchema = external_exports.object({
23281
23791
  /**
23282
23792
  * Sub-id within this addon. Convention `default` for single-
@@ -25595,710 +26105,314 @@ var metricsProviderCapability = {
25595
26105
  auth: "admin"
25596
26106
  })
25597
26107
  }
25598
- };
25599
- var ModelConvertInputSchema = external_exports.object({
25600
- sourceUrl: external_exports.string(),
25601
- metadata: ModelConvertMetadataSchema,
25602
- targets: external_exports.array(ConvertTargetSchema).min(1).readonly(),
25603
- calibrationRef: external_exports.string().optional(),
25604
- sessionId: external_exports.string().optional()
25605
- });
25606
- var modelConvertCapability = {
25607
- name: "model-convert",
25608
- scope: "system",
25609
- mode: "singleton",
25610
- internal: true,
25611
- methods: { convert: method(ModelConvertInputSchema, ConvertResultSchema, {
25612
- kind: "mutation",
25613
- auth: "admin",
25614
- timeoutMs: 6e5
25615
- }) }
25616
- };
25617
- var ModelDistributeInputSchema = external_exports.object({
25618
- nodeId: external_exports.string(),
25619
- modelId: external_exports.string(),
25620
- format: external_exports.enum(MODEL_FORMATS),
25621
- entry: ModelCatalogEntrySchema
25622
- });
25623
- var ModelDistributeResultSchema = external_exports.object({
25624
- ok: external_exports.boolean(),
25625
- /** sha256 of the staged tarball (empty for a hub-local no-op). */
25626
- sha256: external_exports.string(),
25627
- bytes: external_exports.number(),
25628
- /** The target node's modelsDir the artifact landed in. */
25629
- path: external_exports.string()
25630
- });
25631
- var modelDistributorCapability = {
25632
- name: "model-distributor",
25633
- scope: "system",
25634
- mode: "singleton",
25635
- internal: true,
25636
- methods: { distributeModel: method(ModelDistributeInputSchema, ModelDistributeResultSchema, {
25637
- kind: "mutation",
25638
- auth: "admin"
25639
- }) }
25640
- };
25641
- var BrokerKindSchema = external_exports.enum(["external", "embedded"]);
25642
- var BrokerStatusSchema$1 = external_exports.enum([
25643
- "connected",
25644
- "disconnected",
25645
- "auth-failed",
25646
- "unreachable",
25647
- "tls-error"
25648
- ]);
25649
- var BrokerInfoSchema = external_exports.object({
25650
- id: external_exports.string(),
25651
- name: external_exports.string(),
25652
- url: external_exports.string(),
25653
- kind: BrokerKindSchema,
25654
- status: BrokerStatusSchema$1,
25655
- latencyMs: external_exports.number().nullable(),
25656
- error: external_exports.string().optional(),
25657
- /** Embedded brokers only: number of MQTT clients currently connected. */
25658
- connectedClients: external_exports.number().int().nonnegative().optional(),
25659
- /** Epoch ms of the last live probe (external) or aedes snapshot (embedded). */
25660
- lastCheckedAt: external_exports.number().optional()
25661
- });
25662
- var BrokerConnectionDetailsSchema = external_exports.object({
25663
- url: external_exports.string(),
25664
- username: external_exports.string().optional(),
25665
- password: external_exports.string().optional(),
25666
- /**
25667
- * Suggested prefix for `clientId`. Each consumer should suffix this
25668
- * with its own discriminator (addon id, instance id) so reconnects
25669
- * don't kick each other off (MQTT spec: clientId must be unique per
25670
- * broker).
25671
- */
25672
- clientIdPrefix: external_exports.string().optional()
25673
- });
25674
- var AddBrokerInputSchema = external_exports.object({
25675
- name: external_exports.string().min(1),
25676
- url: external_exports.string().regex(/^(mqtt|mqtts|ws|wss):\/\//, "URL must start with mqtt(s):// or ws(s)://"),
25677
- username: external_exports.string().optional(),
25678
- password: external_exports.string().optional(),
25679
- clientIdPrefix: external_exports.string().optional()
25680
- });
25681
- var AddBrokerResultSchema = external_exports.object({ id: external_exports.string() });
25682
- var IdInputSchema = external_exports.object({ id: external_exports.string() });
25683
- var TestResultSchema$1 = external_exports.discriminatedUnion("ok", [external_exports.object({
25684
- ok: external_exports.literal(true),
25685
- latencyMs: external_exports.number()
25686
- }), external_exports.object({
25687
- ok: external_exports.literal(false),
25688
- error: external_exports.string()
25689
- })]);
25690
- var StartEmbeddedInputSchema = external_exports.object({
25691
- port: external_exports.number().int().min(1).max(65535).default(1883),
25692
- /** Allow anonymous connect (no username/password). Default: false. */
25693
- allowAnonymous: external_exports.boolean().default(false),
25694
- /** Optional shared username/password for clients. */
25695
- username: external_exports.string().optional(),
25696
- password: external_exports.string().optional()
25697
- });
25698
- var StartEmbeddedResultSchema = external_exports.object({
25699
- id: external_exports.string(),
25700
- url: external_exports.string()
25701
- });
25702
- var StatusSchema = external_exports.object({
25703
- brokerCount: external_exports.number(),
25704
- embeddedRunning: external_exports.boolean()
25705
- });
25706
- var mqttBrokerCapability = {
25707
- name: "mqtt-broker",
25708
- scope: "system",
25709
- mode: "collection",
25710
- providerKind: "broker",
25711
- status: {
25712
- schema: StatusSchema,
25713
- kind: "poll"
25714
- },
25715
- methods: {
25716
- listBrokers: method(external_exports.void(), external_exports.array(BrokerInfoSchema)),
25717
- getBrokerConfig: method(IdInputSchema, BrokerConnectionDetailsSchema),
25718
- addBroker: method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }),
25719
- removeBroker: method(IdInputSchema, external_exports.void(), { kind: "mutation" }),
25720
- testConnection: method(IdInputSchema, TestResultSchema$1, { kind: "mutation" }),
25721
- startEmbeddedBroker: method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }),
25722
- stopEmbeddedBroker: method(IdInputSchema, external_exports.void(), { kind: "mutation" }),
25723
- getStatus: method(external_exports.void(), StatusSchema)
25724
- }
25725
- };
25726
- var NetworkEndpointSchema = external_exports.object({
25727
- url: external_exports.string(),
25728
- hostname: external_exports.string(),
25729
- port: external_exports.number(),
25730
- protocol: external_exports.enum(["http", "https"])
25731
- });
25732
- var NetworkAccessStatusSchema = external_exports.object({
25733
- connected: external_exports.boolean(),
25734
- endpoint: NetworkEndpointSchema.nullable(),
25735
- error: external_exports.string().optional()
25736
- });
25737
- var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
25738
- /**
25739
- * Stable id within the provider — typically `<mode>-<sourcePort>` so
25740
- * the orchestrator can dedupe across `listEndpoints` polls.
25741
- */
25742
- id: external_exports.string(),
25743
- /** Operator-facing label (mirrors `MeshEndpoint.label`). */
25744
- label: external_exports.string(),
25745
- /** Optional provider-specific mode tag, used for icon/colour in admin UI. */
25746
- mode: external_exports.string().optional(),
25747
- /** Originating local port the ingress fronts (informational). */
25748
- sourcePort: external_exports.number().optional()
25749
- });
25750
- var networkAccessCapability = {
25751
- name: "network-access",
25752
- scope: "system",
25753
- mode: "collection",
25754
- providerKind: "ingress",
25755
- methods: {
25756
- start: method(external_exports.void(), NetworkEndpointSchema, { kind: "mutation" }),
25757
- stop: method(external_exports.void(), external_exports.void(), { kind: "mutation" }),
25758
- getEndpoint: method(external_exports.void(), NetworkEndpointSchema.nullable()),
25759
- getStatus: method(external_exports.void(), NetworkAccessStatusSchema),
25760
- /**
25761
- * Enumerate every active ingress entry. Providers that expose only a
25762
- * single endpoint may omit this method; callers fall back to
25763
- * `getEndpoint()` in that case.
25764
- */
25765
- listEndpoints: method(external_exports.void(), external_exports.array(NetworkEndpointEntrySchema).readonly())
25766
- }
25767
- };
25768
- var AttachmentMediaTypeSchema = external_exports.enum([
25769
- "image",
25770
- "video",
25771
- "gif",
25772
- "audio",
25773
- "icon"
25774
- ]);
25775
- var AttachmentSchema = external_exports.object({
25776
- mediaType: AttachmentMediaTypeSchema,
25777
- url: external_exports.string().optional(),
25778
- bytes: external_exports.instanceof(Uint8Array).optional(),
25779
- mime: external_exports.string().optional(),
25780
- name: external_exports.string().optional()
25781
- }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
25782
- var NotificationFormatSchema = external_exports.enum([
25783
- "text",
25784
- "markdown",
25785
- "html"
25786
- ]);
25787
- var NotificationActionSchema = external_exports.object({
25788
- id: external_exports.string(),
25789
- label: external_exports.string(),
25790
- url: external_exports.string().optional()
25791
- });
25792
- var NotificationSchema = external_exports.object({
25793
- body: external_exports.string(),
25794
- title: external_exports.string().optional(),
25795
- format: NotificationFormatSchema.default("text"),
25796
- priority: external_exports.number().int().min(1).max(5).default(3),
25797
- level: external_exports.string().optional(),
25798
- attachments: external_exports.array(AttachmentSchema).optional(),
25799
- clickUrl: external_exports.string().optional(),
25800
- actions: external_exports.array(NotificationActionSchema).optional(),
25801
- sound: external_exports.string().optional(),
25802
- ttl: external_exports.number().optional(),
25803
- tag: external_exports.string().optional(),
25804
- deviceId: external_exports.number().optional(),
25805
- eventId: external_exports.string().optional(),
25806
- metadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
25807
- });
25808
- var TargetKindLevelSchema = external_exports.object({
25809
- id: external_exports.string(),
25810
- label: external_exports.string(),
25811
- /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
25812
- ordinal: external_exports.number().int().min(1).max(5).nullable(),
25813
- flags: external_exports.object({
25814
- critical: external_exports.boolean().optional(),
25815
- silent: external_exports.boolean().optional(),
25816
- noPush: external_exports.boolean().optional()
25817
- }).optional(),
25818
- /** e.g. Pushover `emergency` requires `retry` / `expire`. */
25819
- requires: external_exports.array(external_exports.string()).optional(),
25820
- description: external_exports.string().optional()
25821
- });
25822
- var TargetKindAttachmentsCapsSchema = external_exports.object({
25823
- mediaTypes: external_exports.array(AttachmentMediaTypeSchema),
25824
- mode: external_exports.enum([
25825
- "url",
25826
- "bytes",
25827
- "both"
25828
- ]),
25829
- max: external_exports.number().int().nonnegative(),
25830
- maxBytes: external_exports.number().int().positive().optional()
25831
- });
25832
- var TargetKindCapsSchema = external_exports.object({
25833
- attachments: TargetKindAttachmentsCapsSchema,
25834
- /** Max action buttons (0 = none). */
25835
- actions: external_exports.number().int().nonnegative(),
25836
- levels: external_exports.array(TargetKindLevelSchema),
25837
- format: external_exports.array(NotificationFormatSchema),
25838
- clickUrl: external_exports.boolean(),
25839
- sound: external_exports.boolean(),
25840
- ttl: external_exports.boolean(),
25841
- bodyMaxLen: external_exports.number().int().positive()
25842
- });
25843
- var ConfigSchemaPassthrough = external_exports.unknown();
25844
- var TargetKindSchema = external_exports.object({
25845
- kind: external_exports.string(),
25846
- label: external_exports.string(),
25847
- icon: external_exports.string(),
25848
- /** Stamped by each provider so the concat-fanned catalog stays routable. */
25849
- addonId: external_exports.string(),
25850
- configSchema: ConfigSchemaPassthrough,
25851
- supportsDiscovery: external_exports.boolean(),
25852
- caps: TargetKindCapsSchema
25853
- });
25854
- var TargetSchema = external_exports.object({
25855
- id: external_exports.string(),
25856
- name: external_exports.string(),
25857
- kind: external_exports.string(),
25858
- addonId: external_exports.string(),
25859
- enabled: external_exports.boolean(),
25860
- config: external_exports.record(external_exports.string(), external_exports.unknown())
25861
- });
25862
- var DiscoveredTargetSchema = external_exports.object({
25863
- kind: external_exports.string(),
25864
- suggestedName: external_exports.string(),
25865
- config: external_exports.record(external_exports.string(), external_exports.unknown())
25866
- });
25867
- var RenderedAsSchema = external_exports.object({
25868
- level: external_exports.string(),
25869
- format: NotificationFormatSchema,
25870
- attachmentsSent: external_exports.number().int().nonnegative(),
25871
- actionsSent: external_exports.number().int().nonnegative(),
25872
- truncated: external_exports.boolean(),
25873
- dropped: external_exports.array(external_exports.string())
25874
- });
25875
- var SendResultSchema = external_exports.object({
25876
- success: external_exports.boolean(),
25877
- error: external_exports.string().optional(),
25878
- renderedAs: RenderedAsSchema.optional()
25879
- });
25880
- var TestResultSchema = SendResultSchema;
25881
- var notificationOutputCapability = {
25882
- name: "notification-output",
25883
- scope: "system",
25884
- mode: "collection",
25885
- methods: {
25886
- listTargetKinds: method(external_exports.object({}), external_exports.array(TargetKindSchema)),
25887
- listTargets: method(external_exports.object({}), external_exports.array(TargetSchema)),
25888
- discoverTargets: method(external_exports.object({
25889
- kind: external_exports.string(),
25890
- config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
25891
- }), external_exports.array(DiscoveredTargetSchema)),
25892
- send: method(external_exports.object({
25893
- targetId: external_exports.string(),
25894
- notification: NotificationSchema
25895
- }), SendResultSchema, { kind: "mutation" }),
25896
- testTarget: method(external_exports.object({
25897
- targetId: external_exports.string(),
25898
- sample: NotificationSchema.optional()
25899
- }), TestResultSchema, { kind: "mutation" }),
25900
- upsertTarget: method(external_exports.object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }),
25901
- deleteTarget: method(external_exports.object({ targetId: external_exports.string() }), external_exports.void(), { kind: "mutation" }),
25902
- setTargetEnabled: method(external_exports.object({
25903
- targetId: external_exports.string(),
25904
- enabled: external_exports.boolean()
25905
- }), external_exports.void(), { kind: "mutation" })
25906
- }
25907
- };
25908
- var NcDeliverySchema = external_exports.enum([
25909
- "immediate",
25910
- "track-end",
25911
- "device-event",
25912
- "package-event"
25913
- ]);
25914
- var NcScheduleWindowSchema = external_exports.object({
25915
- /** Days of week the window STARTS on (0 = Sunday … 6 = Saturday). */
25916
- days: external_exports.array(external_exports.number().int().min(0).max(6)).min(1),
25917
- startMinute: external_exports.number().int().min(0).max(1439),
25918
- endMinute: external_exports.number().int().min(0).max(1439)
25919
- });
25920
- var NcScheduleSchema = external_exports.object({
25921
- windows: external_exports.array(NcScheduleWindowSchema).min(1),
25922
- /** IANA timezone; default = hub host timezone. */
25923
- timezone: external_exports.string().optional(),
25924
- /** Active OUTSIDE the windows (e.g. "only outside business hours"). */
25925
- invert: external_exports.boolean().optional()
25926
- });
25927
- var NcPlateMatcherSchema = external_exports.object({
25928
- values: external_exports.array(external_exports.string().min(1)).min(1),
25929
- /** Max Levenshtein distance after normalization (uppercase alphanumeric). */
25930
- maxDistance: external_exports.number().int().min(0).max(3).default(1)
25931
- });
25932
- var NcOccupancyConditionSchema = external_exports.object({
25933
- /** Admin zone id to scope the count to; absent = whole-frame occupancy. */
25934
- zoneId: external_exports.string().optional(),
25935
- /** Object class to count; absent = any class. */
25936
- className: external_exports.string().optional(),
25937
- op: external_exports.enum([
25938
- "became-occupied",
25939
- "became-free",
25940
- ">=",
25941
- "<="
25942
- ]).default("became-occupied"),
25943
- count: external_exports.number().int().min(0).default(1),
25944
- sustainSeconds: external_exports.number().int().min(0).max(3600).default(15)
25945
- });
25946
- var NcZoneConditionSchema = external_exports.object({
25947
- ids: external_exports.array(external_exports.string().min(1)).min(1),
25948
- /** Quantifier over `ids` — at least one / every one visited. */
25949
- match: external_exports.enum(["any", "all"]).default("any")
25950
- });
25951
- var NcConditionsSchema = external_exports.object({
25952
- /** Device scope — absent = all devices. */
25953
- devices: external_exports.array(external_exports.number()).optional(),
25954
- /** Detector class names (any overlap with the record's class set). */
25955
- classes: external_exports.array(external_exports.string().min(1)).optional(),
25956
- /** Veto classes — any overlap fails the rule. */
25957
- classesExclude: external_exports.array(external_exports.string().min(1)).optional(),
25958
- /** Minimum detection confidence 0–1 (fails when the record has none). */
25959
- minConfidence: external_exports.number().min(0).max(1).optional(),
25960
- /** Admin zone membership over event `zones` / track `zonesVisited`. */
25961
- zones: NcZoneConditionSchema.optional(),
25962
- /** Veto zones — any hit fails the rule. */
25963
- zonesExclude: external_exports.array(external_exports.string().min(1)).optional(),
25964
- /**
25965
- * Exact (case-insensitive) match on the record's collapsed `label`
25966
- * (identity name / plate text / subclass).
25967
- */
25968
- labelEquals: external_exports.array(external_exports.string().min(1)).optional(),
25969
- /**
25970
- * Identity matcher. P1 boundary: matched against the record's collapsed
25971
- * `label` (the identity display name propagated by the face pipeline) —
25972
- * identity-ID matching rides in P2 when identity ids reach the record.
25973
- */
25974
- identities: external_exports.array(external_exports.string().min(1)).optional(),
25975
- /** Fuzzy plate matcher against the record's `label` (plate text). */
25976
- plates: NcPlateMatcherSchema.optional(),
25977
- /**
25978
- * Identity EXCLUDE — mirror of {@link identities} with `notIn` semantics.
25979
- * Same P1 boundary: matched against the record's collapsed `label` (the
25980
- * identity display name). A record with NO label passes (nothing to
25981
- * exclude), unlike the include variant which fails on an absent label.
25982
- */
25983
- identitiesExclude: external_exports.array(external_exports.string().min(1)).optional(),
25984
- /**
25985
- * Minimum server-computed key-event importance in [0,1] (`Track.importance`).
25986
- * TRACK-END only: importance is scored at track close, so it does not exist
25987
- * at immediate / object-event evaluation time (see catalog `appliesTo`). At
25988
- * close the value is threaded via the close-time info (the `Track` clone is
25989
- * captured before the DB row is updated, so it would otherwise read stale).
25990
- * Fails when the record carries no importance (never guess quality — the
25991
- * `minConfidence` precedent). MVP cut: a single scalar threshold.
25992
- */
25993
- minImportance: external_exports.number().min(0).max(1).optional(),
25994
- /**
25995
- * Minimum track dwell in SECONDS — `(lastSeen − firstSeen) / 1000`.
25996
- * TRACK-END only: an `immediate` / object-event subject has no closed
25997
- * lifespan, so a dwell condition never matches immediate delivery
25998
- * (documented choice — the object-event record carries no `firstSeen`,
25999
- * so dwell cannot be computed from what the subject actually carries).
26000
- */
26001
- minDwellSeconds: external_exports.number().min(0).optional(),
26002
- /**
26003
- * Detection provenance filter. `any` (default / absent) matches every
26004
- * source; otherwise the subject's source must equal it. Legacy records
26005
- * with no stamped source are treated as `pipeline`. The union spans both
26006
- * record kinds — object events carry `pipeline` | `onboard`, synthetic
26007
- * tracks carry `sensor`.
26008
- */
26009
- source: external_exports.enum([
26010
- "pipeline",
26011
- "onboard",
26012
- "sensor",
26013
- "any"
26014
- ]).optional(),
26015
- /**
26016
- * Minimum identity / plate MATCH confidence in [0,1] — DISTINCT from the
26017
- * detector `minConfidence` (that gates the object-detection score; this
26018
- * gates the recognition/OCR match score). Fails when the subject carries
26019
- * no label-match confidence (never guess). TRACK-END only: the confidence
26020
- * lives on the recognition result and reaches the subject at track close.
26021
- *
26022
- * What it measures precisely (plumbed at track close — the closer threads
26023
- * the value into `NcTrackClosedInfo.labelConfidence`, the same seam as
26024
- * `importance`): the BEST recognition match confidence observed for the
26025
- * label the track carries at close — for a face, the peak cosine similarity
26026
- * of the ASSIGNED identity (`FaceMatch.score`, reset on an identity switch);
26027
- * for a plate, the peak OCR read score of the best-held plate
26028
- * (`plateText.confidence`). When BOTH a face and a plate were recognized on
26029
- * one track the higher of the two is used. A track that ended with no
26030
- * confident identity/plate match carries no value, so the condition fails
26031
- * closed for it (an un-recognized subject).
26032
- */
26033
- minLabelConfidence: external_exports.number().min(0).max(1).optional(),
26034
- /**
26035
- * DEVICE-EVENT only. Raw device event-type tokens (`EventFire.eventType`,
26036
- * e.g. a doorbell `press` / `press_long`) — matched case-insensitively
26037
- * against the token carried on the device-event subject (extracted from the
26038
- * event-emitter runtime slice's `lastEvent.eventType`). Fails when the
26039
- * subject carries no token. Doorbell-pulse / passive-sensor kinds emit no
26040
- * eventType, so gate those with {@link sensorKinds} instead.
26041
- */
26042
- eventTypeTokens: external_exports.array(external_exports.string().min(1)).optional(),
26043
- /**
26044
- * DEVICE-EVENT only. Sensor/control taxonomy kinds (e.g. `doorbell`,
26045
- * `contact`, `button`, `device-event`) — matched against the persisted
26046
- * `SensorEvent.kind` (see `sensor-event-kinds.ts`). Membership is OR.
26047
- */
26048
- sensorKinds: external_exports.array(external_exports.string().min(1)).optional(),
26049
- /**
26050
- * PACKAGE-EVENT only. Which package phase fires the rule — `delivered`
26051
- * (a parked parcel appeared), `picked-up` (it departed), or `both`. Fails
26052
- * when the subject's phase does not match (a subject always carries a phase
26053
- * on the package-event trigger).
26054
- */
26055
- packagePhase: external_exports.enum([
26056
- "delivered",
26057
- "picked-up",
26058
- "both"
26059
- ]).optional(),
26060
- /**
26061
- * PERSONAL-RULE custom zones (viewer-drawn). Inline normalized polygons
26062
- * (MaskShape vocabulary). A record passes when its bbox overlaps ANY
26063
- * listed polygon (ZoneEngine membership semantics). Evaluated only when
26064
- * the subject carries a bbox; absent bbox ⇒ the condition FAILS.
26065
- */
26066
- customZones: external_exports.array(MaskPolygonShapeSchema).optional(),
26067
- /**
26068
- * DEVICE-EVENT only. ZoneAnalytics occupancy edge — fires when a device's
26069
- * (optionally zone/class-scoped) occupancy count crosses the configured
26070
- * threshold and holds for `sustainSeconds`. Fail-closed on missing
26071
- * substrate (no snapshot / missing zone). See {@link NcOccupancyCondition}.
26072
- */
26073
- occupancy: NcOccupancyConditionSchema.optional()
26108
+ };
26109
+ var ModelConvertInputSchema = external_exports.object({
26110
+ sourceUrl: external_exports.string(),
26111
+ metadata: ModelConvertMetadataSchema,
26112
+ targets: external_exports.array(ConvertTargetSchema).min(1).readonly(),
26113
+ calibrationRef: external_exports.string().optional(),
26114
+ sessionId: external_exports.string().optional()
26074
26115
  });
26075
- var NcRuleTargetSchema = external_exports.object({
26076
- /** `notification-output` Target id. */
26077
- targetId: external_exports.string().min(1),
26078
- /**
26079
- * Per-backend passthrough. Recognized keys are mapped onto the canonical
26080
- * Notification (`priority`, `level`, `sound`, `clickUrl`, `ttl`); the
26081
- * degrade engine drops what the backend can't render.
26082
- */
26083
- params: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
26116
+ var modelConvertCapability = {
26117
+ name: "model-convert",
26118
+ scope: "system",
26119
+ mode: "singleton",
26120
+ internal: true,
26121
+ methods: { convert: method(ModelConvertInputSchema, ConvertResultSchema, {
26122
+ kind: "mutation",
26123
+ auth: "admin",
26124
+ timeoutMs: 6e5
26125
+ }) }
26126
+ };
26127
+ var ModelDistributeInputSchema = external_exports.object({
26128
+ nodeId: external_exports.string(),
26129
+ modelId: external_exports.string(),
26130
+ format: external_exports.enum(MODEL_FORMATS),
26131
+ entry: ModelCatalogEntrySchema
26084
26132
  });
26085
- var NcMediaPolicySchema = external_exports.object({ attach: external_exports.enum([
26086
- "best",
26087
- "best-matching",
26088
- "keyFrame",
26089
- "none"
26090
- ]).default("best") });
26091
- var NcThrottleSchema = external_exports.object({
26092
- cooldownSec: external_exports.number().int().min(0).max(86400).default(60),
26093
- /** `rule` = one shared cooldown; `rule-device` = per-camera cooldown. */
26094
- scope: external_exports.enum(["rule", "rule-device"]).default("rule-device")
26133
+ var ModelDistributeResultSchema = external_exports.object({
26134
+ ok: external_exports.boolean(),
26135
+ /** sha256 of the staged tarball (empty for a hub-local no-op). */
26136
+ sha256: external_exports.string(),
26137
+ bytes: external_exports.number(),
26138
+ /** The target node's modelsDir the artifact landed in. */
26139
+ path: external_exports.string()
26095
26140
  });
26096
- var NcRuleInputSchema = external_exports.object({
26097
- name: external_exports.string().min(1).max(200),
26098
- enabled: external_exports.boolean().default(true),
26099
- delivery: NcDeliverySchema,
26100
- conditions: NcConditionsSchema.default({}),
26101
- schedule: NcScheduleSchema.optional(),
26102
- targets: external_exports.array(NcRuleTargetSchema).min(1),
26103
- media: NcMediaPolicySchema.default({ attach: "best" }),
26104
- throttle: NcThrottleSchema.default({
26105
- cooldownSec: 60,
26106
- scope: "rule-device"
26107
- }),
26108
- /** `{{var}}` templating over camera/class/label/zones/confidence/time. */
26109
- template: external_exports.object({
26110
- title: external_exports.string().max(500).optional(),
26111
- body: external_exports.string().max(2e3).optional()
26112
- }).optional(),
26113
- /** Canonical notification priority ordinal (1..5); per-target overridable. */
26114
- priority: external_exports.number().int().min(1).max(5).default(3),
26141
+ var modelDistributorCapability = {
26142
+ name: "model-distributor",
26143
+ scope: "system",
26144
+ mode: "singleton",
26145
+ internal: true,
26146
+ methods: { distributeModel: method(ModelDistributeInputSchema, ModelDistributeResultSchema, {
26147
+ kind: "mutation",
26148
+ auth: "admin"
26149
+ }) }
26150
+ };
26151
+ var BrokerKindSchema = external_exports.enum(["external", "embedded"]);
26152
+ var BrokerStatusSchema$1 = external_exports.enum([
26153
+ "connected",
26154
+ "disconnected",
26155
+ "auth-failed",
26156
+ "unreachable",
26157
+ "tls-error"
26158
+ ]);
26159
+ var BrokerInfoSchema = external_exports.object({
26160
+ id: external_exports.string(),
26161
+ name: external_exports.string(),
26162
+ url: external_exports.string(),
26163
+ kind: BrokerKindSchema,
26164
+ status: BrokerStatusSchema$1,
26165
+ latencyMs: external_exports.number().nullable(),
26166
+ error: external_exports.string().optional(),
26167
+ /** Embedded brokers only: number of MQTT clients currently connected. */
26168
+ connectedClients: external_exports.number().int().nonnegative().optional(),
26169
+ /** Epoch ms of the last live probe (external) or aedes snapshot (embedded). */
26170
+ lastCheckedAt: external_exports.number().optional()
26171
+ });
26172
+ var BrokerConnectionDetailsSchema = external_exports.object({
26173
+ url: external_exports.string(),
26174
+ username: external_exports.string().optional(),
26175
+ password: external_exports.string().optional(),
26115
26176
  /**
26116
- * Ownership/visibility key. Absent = admin/global rule (unchanged legacy
26117
- * behaviour, visible to all, read-only in the viewer). Present = personal
26118
- * rule owned by this userId. Server-stamped; never trusted from a client.
26177
+ * Suggested prefix for `clientId`. Each consumer should suffix this
26178
+ * with its own discriminator (addon id, instance id) so reconnects
26179
+ * don't kick each other off (MQTT spec: clientId must be unique per
26180
+ * broker).
26119
26181
  */
26120
- ownerUserId: external_exports.string().optional()
26182
+ clientIdPrefix: external_exports.string().optional()
26121
26183
  });
26122
- var NcRulePatchSchema = NcRuleInputSchema.partial().extend({ disabledTargetIds: external_exports.array(external_exports.string()).optional() });
26123
- var NcRuleSchema = NcRuleInputSchema.extend({
26184
+ var AddBrokerInputSchema = external_exports.object({
26185
+ name: external_exports.string().min(1),
26186
+ url: external_exports.string().regex(/^(mqtt|mqtts|ws|wss):\/\//, "URL must start with mqtt(s):// or ws(s)://"),
26187
+ username: external_exports.string().optional(),
26188
+ password: external_exports.string().optional(),
26189
+ clientIdPrefix: external_exports.string().optional()
26190
+ });
26191
+ var AddBrokerResultSchema = external_exports.object({ id: external_exports.string() });
26192
+ var IdInputSchema = external_exports.object({ id: external_exports.string() });
26193
+ var TestResultSchema$1 = external_exports.discriminatedUnion("ok", [external_exports.object({
26194
+ ok: external_exports.literal(true),
26195
+ latencyMs: external_exports.number()
26196
+ }), external_exports.object({
26197
+ ok: external_exports.literal(false),
26198
+ error: external_exports.string()
26199
+ })]);
26200
+ var StartEmbeddedInputSchema = external_exports.object({
26201
+ port: external_exports.number().int().min(1).max(65535).default(1883),
26202
+ /** Allow anonymous connect (no username/password). Default: false. */
26203
+ allowAnonymous: external_exports.boolean().default(false),
26204
+ /** Optional shared username/password for clients. */
26205
+ username: external_exports.string().optional(),
26206
+ password: external_exports.string().optional()
26207
+ });
26208
+ var StartEmbeddedResultSchema = external_exports.object({
26124
26209
  id: external_exports.string(),
26125
- /** userId of the admin who created the rule (server-stamped caller). */
26126
- createdBy: external_exports.string(),
26127
- createdAt: external_exports.number(),
26128
- updatedAt: external_exports.number(),
26210
+ url: external_exports.string()
26211
+ });
26212
+ var StatusSchema = external_exports.object({
26213
+ brokerCount: external_exports.number(),
26214
+ embeddedRunning: external_exports.boolean()
26215
+ });
26216
+ var mqttBrokerCapability = {
26217
+ name: "mqtt-broker",
26218
+ scope: "system",
26219
+ mode: "collection",
26220
+ providerKind: "broker",
26221
+ status: {
26222
+ schema: StatusSchema,
26223
+ kind: "poll"
26224
+ },
26225
+ methods: {
26226
+ listBrokers: method(external_exports.void(), external_exports.array(BrokerInfoSchema)),
26227
+ getBrokerConfig: method(IdInputSchema, BrokerConnectionDetailsSchema),
26228
+ addBroker: method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }),
26229
+ removeBroker: method(IdInputSchema, external_exports.void(), { kind: "mutation" }),
26230
+ testConnection: method(IdInputSchema, TestResultSchema$1, { kind: "mutation" }),
26231
+ startEmbeddedBroker: method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }),
26232
+ stopEmbeddedBroker: method(IdInputSchema, external_exports.void(), { kind: "mutation" }),
26233
+ getStatus: method(external_exports.void(), StatusSchema)
26234
+ }
26235
+ };
26236
+ var NetworkEndpointSchema = external_exports.object({
26237
+ url: external_exports.string(),
26238
+ hostname: external_exports.string(),
26239
+ port: external_exports.number(),
26240
+ protocol: external_exports.enum(["http", "https"])
26241
+ });
26242
+ var NetworkAccessStatusSchema = external_exports.object({
26243
+ connected: external_exports.boolean(),
26244
+ endpoint: NetworkEndpointSchema.nullable(),
26245
+ error: external_exports.string().optional()
26246
+ });
26247
+ var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
26129
26248
  /**
26130
- * Per-target opt-out set. A targetId here is suppressed for THIS rule at
26131
- * send time. Only a target's OWNER may add/remove its id (server-checked
26132
- * in `nc.setRuleTargetEnabled`). Defaults to empty.
26249
+ * Stable id within the provider typically `<mode>-<sourcePort>` so
26250
+ * the orchestrator can dedupe across `listEndpoints` polls.
26133
26251
  */
26134
- disabledTargetIds: external_exports.array(external_exports.string()).default([])
26252
+ id: external_exports.string(),
26253
+ /** Operator-facing label (mirrors `MeshEndpoint.label`). */
26254
+ label: external_exports.string(),
26255
+ /** Optional provider-specific mode tag, used for icon/colour in admin UI. */
26256
+ mode: external_exports.string().optional(),
26257
+ /** Originating local port the ingress fronts (informational). */
26258
+ sourcePort: external_exports.number().optional()
26135
26259
  });
26136
- var NcTestResultSchema = external_exports.object({
26137
- recordId: external_exports.string(),
26138
- recordKind: external_exports.enum([
26139
- "object-event",
26140
- "track",
26141
- "device-event",
26142
- "package-event"
26143
- ]),
26144
- deviceId: external_exports.number(),
26145
- timestamp: external_exports.number(),
26146
- wouldFire: external_exports.boolean(),
26147
- /** Condition id that failed (first failing group), when `wouldFire` is false. */
26148
- failedCondition: external_exports.string().optional(),
26149
- className: external_exports.string().optional(),
26150
- label: external_exports.string().optional()
26260
+ var networkAccessCapability = {
26261
+ name: "network-access",
26262
+ scope: "system",
26263
+ mode: "collection",
26264
+ providerKind: "ingress",
26265
+ methods: {
26266
+ start: method(external_exports.void(), NetworkEndpointSchema, { kind: "mutation" }),
26267
+ stop: method(external_exports.void(), external_exports.void(), { kind: "mutation" }),
26268
+ getEndpoint: method(external_exports.void(), NetworkEndpointSchema.nullable()),
26269
+ getStatus: method(external_exports.void(), NetworkAccessStatusSchema),
26270
+ /**
26271
+ * Enumerate every active ingress entry. Providers that expose only a
26272
+ * single endpoint may omit this method; callers fall back to
26273
+ * `getEndpoint()` in that case.
26274
+ */
26275
+ listEndpoints: method(external_exports.void(), external_exports.array(NetworkEndpointEntrySchema).readonly())
26276
+ }
26277
+ };
26278
+ var AttachmentMediaTypeSchema = external_exports.enum([
26279
+ "image",
26280
+ "video",
26281
+ "gif",
26282
+ "audio",
26283
+ "icon"
26284
+ ]);
26285
+ var AttachmentSchema = external_exports.object({
26286
+ mediaType: AttachmentMediaTypeSchema,
26287
+ url: external_exports.string().optional(),
26288
+ bytes: external_exports.instanceof(Uint8Array).optional(),
26289
+ mime: external_exports.string().optional(),
26290
+ name: external_exports.string().optional()
26291
+ }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
26292
+ var NotificationFormatSchema = external_exports.enum([
26293
+ "text",
26294
+ "markdown",
26295
+ "html"
26296
+ ]);
26297
+ var NotificationActionSchema = external_exports.object({
26298
+ id: external_exports.string(),
26299
+ label: external_exports.string(),
26300
+ url: external_exports.string().optional()
26301
+ });
26302
+ var NotificationSchema = external_exports.object({
26303
+ body: external_exports.string(),
26304
+ title: external_exports.string().optional(),
26305
+ format: NotificationFormatSchema.default("text"),
26306
+ priority: external_exports.number().int().min(1).max(5).default(3),
26307
+ level: external_exports.string().optional(),
26308
+ attachments: external_exports.array(AttachmentSchema).optional(),
26309
+ clickUrl: external_exports.string().optional(),
26310
+ actions: external_exports.array(NotificationActionSchema).optional(),
26311
+ sound: external_exports.string().optional(),
26312
+ ttl: external_exports.number().optional(),
26313
+ tag: external_exports.string().optional(),
26314
+ deviceId: external_exports.number().optional(),
26315
+ eventId: external_exports.string().optional(),
26316
+ metadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
26151
26317
  });
26152
- var NcConditionDescriptorSchema = external_exports.object({
26153
- /** Field id inside `NcConditions` (or `'schedule'` for the rule-level group). */
26318
+ var TargetKindLevelSchema = external_exports.object({
26154
26319
  id: external_exports.string(),
26155
- group: external_exports.enum([
26156
- "scope",
26157
- "class",
26158
- "zones",
26159
- "quality",
26160
- "label",
26161
- "schedule",
26162
- "device",
26163
- "package",
26164
- "occupancy"
26165
- ]),
26166
26320
  label: external_exports.string(),
26167
- /** Editor widget the UI renders never hardcode per-condition forms. */
26168
- valueType: external_exports.enum([
26169
- "deviceIdList",
26170
- "stringList",
26171
- "number01",
26172
- "number",
26173
- "sourceSelect",
26174
- "zoneSelection",
26175
- "zoneIdList",
26176
- "schedule",
26177
- "plateMatcher",
26178
- "packagePhase",
26179
- "polygonDraw",
26180
- "occupancy"
26181
- ]),
26182
- operator: external_exports.enum([
26183
- "in",
26184
- "notIn",
26185
- "anyOf",
26186
- "allOf",
26187
- "gte",
26188
- "fuzzyIn",
26189
- "withinSchedule"
26190
- ]),
26191
- /** Which delivery kinds the condition applies to. */
26192
- appliesTo: external_exports.array(NcDeliverySchema),
26193
- phase: external_exports.string(),
26321
+ /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
26322
+ ordinal: external_exports.number().int().min(1).max(5).nullable(),
26323
+ flags: external_exports.object({
26324
+ critical: external_exports.boolean().optional(),
26325
+ silent: external_exports.boolean().optional(),
26326
+ noPush: external_exports.boolean().optional()
26327
+ }).optional(),
26328
+ /** e.g. Pushover `emergency` requires `retry` / `expire`. */
26329
+ requires: external_exports.array(external_exports.string()).optional(),
26194
26330
  description: external_exports.string().optional()
26195
26331
  });
26196
- var NcHistoryStatusSchema = external_exports.enum([
26197
- "pending",
26198
- "sent",
26199
- "dead"
26200
- ]);
26201
- var NcHistoryRecordKindSchema = external_exports.enum([
26202
- "object-event",
26203
- "track-end",
26204
- "device-event",
26205
- "package-event"
26206
- ]);
26207
- var NcHistorySubjectSchema = external_exports.object({
26208
- className: external_exports.string(),
26209
- label: external_exports.string().optional(),
26210
- confidence: external_exports.number().optional(),
26211
- zones: external_exports.array(external_exports.string()),
26212
- timestamp: external_exports.number()
26332
+ var TargetKindAttachmentsCapsSchema = external_exports.object({
26333
+ mediaTypes: external_exports.array(AttachmentMediaTypeSchema),
26334
+ mode: external_exports.enum([
26335
+ "url",
26336
+ "bytes",
26337
+ "both"
26338
+ ]),
26339
+ max: external_exports.number().int().nonnegative(),
26340
+ maxBytes: external_exports.number().int().positive().optional()
26213
26341
  });
26214
- var NcHistoryEntrySchema = external_exports.object({
26215
- /** Outbox row id — the stable dedup id `ruleId:dedupRef:targetId`. */
26342
+ var TargetKindCapsSchema = external_exports.object({
26343
+ attachments: TargetKindAttachmentsCapsSchema,
26344
+ /** Max action buttons (0 = none). */
26345
+ actions: external_exports.number().int().nonnegative(),
26346
+ levels: external_exports.array(TargetKindLevelSchema),
26347
+ format: external_exports.array(NotificationFormatSchema),
26348
+ clickUrl: external_exports.boolean(),
26349
+ sound: external_exports.boolean(),
26350
+ ttl: external_exports.boolean(),
26351
+ bodyMaxLen: external_exports.number().int().positive()
26352
+ });
26353
+ var ConfigSchemaPassthrough = external_exports.unknown();
26354
+ var TargetKindSchema = external_exports.object({
26355
+ kind: external_exports.string(),
26356
+ label: external_exports.string(),
26357
+ icon: external_exports.string(),
26358
+ /** Stamped by each provider so the concat-fanned catalog stays routable. */
26359
+ addonId: external_exports.string(),
26360
+ configSchema: ConfigSchemaPassthrough,
26361
+ supportsDiscovery: external_exports.boolean(),
26362
+ caps: TargetKindCapsSchema
26363
+ });
26364
+ var TargetSchema = external_exports.object({
26216
26365
  id: external_exports.string(),
26217
- ruleId: external_exports.string(),
26218
- /** Rule name frozen at fire time (outlives a later rename / delete). */
26219
- ruleName: external_exports.string(),
26220
- /** The rule urgency/trigger that produced this delivery. */
26221
- delivery: NcDeliverySchema,
26222
- targetId: external_exports.string(),
26223
- deviceId: external_exports.number(),
26224
- recordKind: NcHistoryRecordKindSchema,
26225
- /** Event / track ref of the evaluated record (§3.2 `eventRef`). */
26226
- recordId: external_exports.string(),
26227
- /** Present for track-scoped deliveries (object-event / track-end). */
26228
- trackId: external_exports.string().optional(),
26229
- status: NcHistoryStatusSchema,
26230
- /** Delivery attempts made so far. */
26231
- attempts: external_exports.number().int(),
26232
- /** Fire time (outbox enqueue). */
26233
- createdAt: external_exports.number(),
26234
- /** Last transition time (terminal for sent / dead). */
26235
- updatedAt: external_exports.number(),
26236
- /** Failure detail — present on a `dead` row. */
26237
- error: external_exports.string().optional(),
26238
- subject: NcHistorySubjectSchema
26366
+ name: external_exports.string(),
26367
+ kind: external_exports.string(),
26368
+ addonId: external_exports.string(),
26369
+ enabled: external_exports.boolean(),
26370
+ config: external_exports.record(external_exports.string(), external_exports.unknown())
26239
26371
  });
26240
- var NcHistoryFilterSchema = external_exports.object({
26241
- ruleId: external_exports.string().optional(),
26242
- deviceId: external_exports.number().optional(),
26243
- status: NcHistoryStatusSchema.optional(),
26244
- since: external_exports.number().optional(),
26245
- until: external_exports.number().optional(),
26246
- limit: external_exports.number().int().min(1).max(500).default(100)
26372
+ var DiscoveredTargetSchema = external_exports.object({
26373
+ kind: external_exports.string(),
26374
+ suggestedName: external_exports.string(),
26375
+ config: external_exports.record(external_exports.string(), external_exports.unknown())
26247
26376
  });
26248
- var notificationRulesCapability = {
26249
- name: "notification-rules",
26377
+ var RenderedAsSchema = external_exports.object({
26378
+ level: external_exports.string(),
26379
+ format: NotificationFormatSchema,
26380
+ attachmentsSent: external_exports.number().int().nonnegative(),
26381
+ actionsSent: external_exports.number().int().nonnegative(),
26382
+ truncated: external_exports.boolean(),
26383
+ dropped: external_exports.array(external_exports.string())
26384
+ });
26385
+ var SendResultSchema = external_exports.object({
26386
+ success: external_exports.boolean(),
26387
+ error: external_exports.string().optional(),
26388
+ renderedAs: RenderedAsSchema.optional()
26389
+ });
26390
+ var TestResultSchema = SendResultSchema;
26391
+ var notificationOutputCapability = {
26392
+ name: "notification-output",
26250
26393
  scope: "system",
26251
- mode: "singleton",
26394
+ mode: "collection",
26252
26395
  methods: {
26253
- listRules: method(external_exports.object({}), external_exports.object({ rules: external_exports.array(NcRuleSchema) }), { auth: "admin" }),
26254
- getRule: method(external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ rule: NcRuleSchema.nullable() }), { auth: "admin" }),
26255
- createRule: method(external_exports.object({ rule: NcRuleInputSchema }), external_exports.object({ rule: NcRuleSchema }), {
26256
- kind: "mutation",
26257
- auth: "admin",
26258
- caller: "required"
26259
- }),
26260
- updateRule: method(external_exports.object({
26261
- ruleId: external_exports.string(),
26262
- patch: NcRulePatchSchema
26263
- }), external_exports.object({ rule: NcRuleSchema }), {
26264
- kind: "mutation",
26265
- auth: "admin",
26266
- caller: "required"
26267
- }),
26268
- deleteRule: method(external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ success: external_exports.literal(true) }), {
26269
- kind: "mutation",
26270
- auth: "admin"
26271
- }),
26272
- setRuleEnabled: method(external_exports.object({
26273
- ruleId: external_exports.string(),
26396
+ listTargetKinds: method(external_exports.object({}), external_exports.array(TargetKindSchema)),
26397
+ listTargets: method(external_exports.object({}), external_exports.array(TargetSchema)),
26398
+ discoverTargets: method(external_exports.object({
26399
+ kind: external_exports.string(),
26400
+ config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
26401
+ }), external_exports.array(DiscoveredTargetSchema)),
26402
+ send: method(external_exports.object({
26403
+ targetId: external_exports.string(),
26404
+ notification: NotificationSchema
26405
+ }), SendResultSchema, { kind: "mutation" }),
26406
+ testTarget: method(external_exports.object({
26407
+ targetId: external_exports.string(),
26408
+ sample: NotificationSchema.optional()
26409
+ }), TestResultSchema, { kind: "mutation" }),
26410
+ upsertTarget: method(external_exports.object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }),
26411
+ deleteTarget: method(external_exports.object({ targetId: external_exports.string() }), external_exports.void(), { kind: "mutation" }),
26412
+ setTargetEnabled: method(external_exports.object({
26413
+ targetId: external_exports.string(),
26274
26414
  enabled: external_exports.boolean()
26275
- }), external_exports.object({ success: external_exports.literal(true) }), {
26276
- kind: "mutation",
26277
- auth: "admin"
26278
- }),
26279
- /**
26280
- * Dry-run a rule against recently persisted records (object events for
26281
- * `immediate`, closed tracks for `track-end`). Mutation kind only to
26282
- * carry the full rule object safely; no side effects.
26283
- */
26284
- testRule: method(external_exports.object({
26285
- rule: NcRuleInputSchema,
26286
- lookbackMinutes: external_exports.number().int().min(1).max(1440).default(60)
26287
- }), external_exports.object({ results: external_exports.array(NcTestResultSchema) }), {
26288
- kind: "mutation",
26289
- auth: "admin"
26290
- }),
26291
- getConditionCatalog: method(external_exports.object({}), external_exports.object({ catalog: external_exports.array(NcConditionDescriptorSchema) })),
26292
- /**
26293
- * Queryable delivery history — a read-only view over the durable outbox
26294
- * (fired rule, subject summary, target, status, timestamps, error on a
26295
- * dead row). Newest-first, bounded by `filter.limit`. Retention follows
26296
- * the outbox's own terminal-row prune horizon (no separate history
26297
- * horizon — single collection, single source of truth). Admin-only in
26298
- * P1 (no user dimension); the P2 viewer History screen adds per-caller
26299
- * scoping on the same method.
26300
- */
26301
- getHistory: method(external_exports.object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), external_exports.object({ entries: external_exports.array(NcHistoryEntrySchema) }), { auth: "admin" })
26415
+ }), external_exports.void(), { kind: "mutation" })
26302
26416
  }
26303
26417
  };
26304
26418
  var MethodAccessSchema = external_exports.enum([
@@ -35955,6 +36069,36 @@ var METHOD_ACCESS_MAP = Object.freeze({
35955
36069
  addonId: null,
35956
36070
  access: "create"
35957
36071
  },
36072
+ "terminalSession.close": {
36073
+ capName: "terminal-session",
36074
+ capScope: "system",
36075
+ addonId: null,
36076
+ access: "create"
36077
+ },
36078
+ "terminalSession.listProfiles": {
36079
+ capName: "terminal-session",
36080
+ capScope: "system",
36081
+ addonId: null,
36082
+ access: "view"
36083
+ },
36084
+ "terminalSession.listSessions": {
36085
+ capName: "terminal-session",
36086
+ capScope: "system",
36087
+ addonId: null,
36088
+ access: "view"
36089
+ },
36090
+ "terminalSession.openSession": {
36091
+ capName: "terminal-session",
36092
+ capScope: "system",
36093
+ addonId: null,
36094
+ access: "create"
36095
+ },
36096
+ "terminalSession.resize": {
36097
+ capName: "terminal-session",
36098
+ capScope: "system",
36099
+ addonId: null,
36100
+ access: "create"
36101
+ },
35958
36102
  "toast.onToast": {
35959
36103
  capName: "toast",
35960
36104
  capScope: "system",