@camstack/addon-provider-dreame 0.2.122 → 0.2.124

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.
Files changed (3) hide show
  1. package/dist/addon.js +325 -93
  2. package/dist/addon.mjs +325 -93
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -63368,6 +63368,15 @@ var ClipSearchStrictnessSchema = _enum([
63368
63368
  "balanced",
63369
63369
  "strict"
63370
63370
  ]);
63371
+ /** Steps whose effect a root label can BE. */
63372
+ var LABEL_SUPPLY_STEP_IDS = [
63373
+ "animal-classifier",
63374
+ "vehicle-classifier",
63375
+ "package-detection"
63376
+ ];
63377
+ /** Steps whose box a root detection can BE. */
63378
+ var CROP_SUPPLY_STEP_IDS = ["plate-detection", "face-detection"];
63379
+ [...LABEL_SUPPLY_STEP_IDS, ...CROP_SUPPLY_STEP_IDS];
63371
63380
  var MODEL_FORMATS = [
63372
63381
  "onnx",
63373
63382
  "coreml",
@@ -63437,11 +63446,11 @@ var ModelVariantGroupSchema = object({
63437
63446
  /** Quantization axis. Omit ⇒ the fp32 base build. */
63438
63447
  precision: _enum(["fp32", "int8"]).optional(),
63439
63448
  /**
63440
- * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
63441
- * latency-optimized export (e.g. ReLU-activation variant) — the slot the
63442
- * future performance variants plug into.
63449
+ * Training / latency-variant axis. Omit ⇒ the standard build. `test` marks
63450
+ * an alternate training of the same family (Scrypted `relu_test`); chip
63451
+ * label is "Test".
63443
63452
  */
63444
- optimization: _enum(["standard", "fast"]).optional(),
63453
+ optimization: _enum(["standard", "test"]).optional(),
63445
63454
  /**
63446
63455
  * Input-resolution axis (square input side, px). Omit ⇒ the family's native
63447
63456
  * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
@@ -63464,9 +63473,10 @@ var ModelProviderIdSchema = _enum([
63464
63473
  * are per upstream. A persisted custom-model row written when this field was a
63465
63474
  * string still parses — the string is lifted into `{ weights }`.
63466
63475
  *
63467
- * Every licence surface (the admin "Models and licenses" page,
63468
- * `THIRD_PARTY_MODELS.md`, the Hugging Face model table) is GENERATED from
63469
- * these records by `scripts/gen-model-licenses.ts` (D661).
63476
+ * Every machine licence surface (`THIRD_PARTY_MODELS.md`, the Hugging Face
63477
+ * model table) is GENERATED from these records by
63478
+ * `scripts/gen-model-licenses.ts` (D661). Operator-facing docs live in
63479
+ * `camstack/docs` → `/docs/detection/models`.
63470
63480
  */
63471
63481
  var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { weights: value } : value, object({
63472
63482
  /** SPDX id of the WEIGHTS' terms, a `LicenseRef-*` for non-SPDX terms, or `UNKNOWN`. */
@@ -63499,6 +63509,35 @@ var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { wei
63499
63509
  /** True when the weights or their data forbid commercial use. */
63500
63510
  nonCommercial: boolean().optional()
63501
63511
  }));
63512
+ /**
63513
+ * One step a ROOT detector can supply from its own output (D730) — see
63514
+ * `ModelCatalogEntry.suppliesSteps`.
63515
+ *
63516
+ * - `label`: the root's raw class IS the step's answer. `labelToEffect` maps
63517
+ * the root's label to the step's vocabulary; `uncovered` is the vocabulary
63518
+ * the root cannot produce (shown as unavailable, never a blocker).
63519
+ * - `crop`: the root draws the box the step would have found (`license_plate`
63520
+ * for plate-detection, `face` for face-detection). The box becomes a detail
63521
+ * child of the `parentMacro` detection that contains it — never a track.
63522
+ */
63523
+ var RootStepSupplySchema = discriminatedUnion("kind", [object({
63524
+ kind: literal("label"),
63525
+ stepId: _enum(LABEL_SUPPLY_STEP_IDS),
63526
+ labelToEffect: record(string(), string()),
63527
+ uncovered: array(string()).readonly()
63528
+ }), object({
63529
+ kind: literal("crop"),
63530
+ stepId: _enum(CROP_SUPPLY_STEP_IDS),
63531
+ rootLabels: array(string()).readonly(),
63532
+ parentMacro: _enum(["vehicle", "person"]),
63533
+ childClass: _enum(["plate", "face"]),
63534
+ /**
63535
+ * The root's smaller input side, stamped on read by `rootSuppliesOf` from
63536
+ * the model entry — what a step's `minRootInputPx` gate judges. Never
63537
+ * stored by an import.
63538
+ */
63539
+ rootInputPx: number().int().positive().optional()
63540
+ })]);
63502
63541
  var ModelCatalogEntrySchema = object({
63503
63542
  id: string(),
63504
63543
  name: string(),
@@ -63625,6 +63664,16 @@ var ModelCatalogEntrySchema = object({
63625
63664
  */
63626
63665
  classMap: DetectionCatalogClassMapSchema.optional(),
63627
63666
  /**
63667
+ * The pipeline steps this ROOT detector can supply from its own output
63668
+ * (D730): its labels name a step's effect (`deer` for `animal-classifier`)
63669
+ * or draw a step's box (`license_plate` for `plate-detection`). Computed at
63670
+ * import from THIS model's labelmap by `deriveRootSupplies`, never written
63671
+ * by hand and never assumed per family; a row without it is derived on read
63672
+ * (`rootSuppliesOf`). Whether a camera uses it is the step's own
63673
+ * `useRootModel` flag (`resolveRootSupply`).
63674
+ */
63675
+ suppliesSteps: array(RootStepSupplySchema).readonly().optional(),
63676
+ /**
63628
63677
  * The model's CLIP feature-space contract (text encoder, dimension, vector
63629
63678
  * index, token window, search floor) — see `clip-model.ts`. Present ONLY on a
63630
63679
  * CLIP image encoder; its absence is what "not a CLIP model" means to the
@@ -67097,7 +67146,7 @@ DeviceType.Camera;
67097
67146
  * This matters here more than anywhere else in the repo, because every provider
67098
67147
  * that knows a device's host knows its PASSWORD too: `{ host, port, username,
67099
67148
  * password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
67100
- * and `http://admin:hunter2@192.168.x.x/` is a URL a browser accepts. The
67149
+ * and `http://admin:hunter2@192.168.60.139/` is a URL a browser accepts. The
67101
67150
  * camera's own page will ask for its own login. That is correct, and pre-
67102
67151
  * filling it is the operator's business, not ours.
67103
67152
  *
@@ -69090,6 +69139,18 @@ method(_void(), array(string()).readonly(), { auth: "admin" }), method(object({
69090
69139
  kind: "mutation",
69091
69140
  auth: "admin"
69092
69141
  });
69142
+ function hostnameOf(baseUrl) {
69143
+ try {
69144
+ const host = new URL(baseUrl).hostname;
69145
+ return host.length > 0 ? host : null;
69146
+ } catch {
69147
+ return null;
69148
+ }
69149
+ }
69150
+ /** The bare hostname of a base URL, or `null` when it does not parse. */
69151
+ function hubAddressHostname(baseUrl) {
69152
+ return baseUrl === null ? null : hostnameOf(baseUrl);
69153
+ }
69093
69154
  /**
69094
69155
  * Transport bounds for AI capability methods.
69095
69156
  *
@@ -70286,7 +70347,7 @@ method(object({
70286
70347
  timeoutMs: 6e5
70287
70348
  });
70288
70349
  method(object({
70289
- nodeId: string(),
70350
+ targetNodeId: string(),
70290
70351
  modelId: string(),
70291
70352
  format: _enum(MODEL_FORMATS),
70292
70353
  entry: ModelCatalogEntrySchema
@@ -71692,6 +71753,8 @@ var NcSystemEventKindSchema = _enum([
71692
71753
  "stream-offline",
71693
71754
  "node-online",
71694
71755
  "node-offline",
71756
+ "node-restarting",
71757
+ "node-updating",
71695
71758
  "node-inference-unavailable",
71696
71759
  "detection-blind",
71697
71760
  "addon-crash-loop",
@@ -72790,8 +72853,9 @@ var NcRuleSchema = NcRuleInputSchema.extend({
72790
72853
  disabledTargetIds: array(string()).default([])
72791
72854
  });
72792
72855
  /**
72793
- * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
72794
- * still-true state. `backups-exports` and `alarm` are not sections — a completed
72856
+ * The state-summary SECTIONS (D647 / D698 / D708): the D637 kind groups that
72857
+ * report a still-true state. `addons` is the crash-looped addons only and
72858
+ * `addon-updates` the addons that are behind (D708 split them). `backups-exports` and `alarm` are not sections — a completed
72795
72859
  * job is not an open state, and the alarm stays instant.
72796
72860
  */
72797
72861
  var NcStateSummarySectionIdSchema = _enum([
@@ -72803,6 +72867,7 @@ var NcStateSummarySectionIdSchema = _enum([
72803
72867
  "detection-blind",
72804
72868
  "nodes",
72805
72869
  "addons",
72870
+ "addon-updates",
72806
72871
  "updates"
72807
72872
  ]);
72808
72873
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
@@ -73283,7 +73348,19 @@ var NcAlarmModeDelaysListSchema = array(object({
73283
73348
  * When this mode may be armed. Absent / null = always allowed (same as a
73284
73349
  * rule with no `schedule`). Evaluated with {@link isScheduleActive}.
73285
73350
  */
73286
- schedule: NcScheduleSchema.optional()
73351
+ schedule: NcScheduleSchema.optional(),
73352
+ /**
73353
+ * D706: while this mode's ENTRY DELAY runs, do the alarm rules' own pushes go out (as an
73354
+ * intrusion naming the locations)? Absent = false — the entry-delay notification is the
73355
+ * only one, and the matches ride the triggered notification's mosaic (D705).
73356
+ */
73357
+ notifyDuringEntryDelay: boolean().optional(),
73358
+ /**
73359
+ * D706: notify "watched again" when a sensor this arm bypassed closes and opens again.
73360
+ * Absent = false (logged, not sent). Even when true it is not sent when that same sensor
73361
+ * is the one that starts the entry delay — the entry-delay notification already names it.
73362
+ */
73363
+ notifySensorRewatched: boolean().optional()
73287
73364
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
73288
73365
  var NcAlarmModeDelaysEditListSchema = array(object({
73289
73366
  mode: AlarmArmModeSchema,
@@ -73292,7 +73369,11 @@ var NcAlarmModeDelaysEditListSchema = array(object({
73292
73369
  triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional(),
73293
73370
  postTriggeredAction: NcAlarmPostTriggeredActionSchema.nullable().optional(),
73294
73371
  /** Set a schedule, or `null` to clear (mode always allowed again). */
73295
- schedule: NcScheduleSchema.nullable().optional()
73372
+ schedule: NcScheduleSchema.nullable().optional(),
73373
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
73374
+ notifyDuringEntryDelay: boolean().nullable().optional(),
73375
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
73376
+ notifySensorRewatched: boolean().nullable().optional()
73296
73377
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
73297
73378
  /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
73298
73379
  var NcAlarmNonBlockingEditSchema = object({
@@ -74253,39 +74334,19 @@ Object.freeze(Object.fromEntries([{
74253
74334
  label: "Face recognition model",
74254
74335
  defaultModelId: "auraface-r100",
74255
74336
  indexName: "the enrolled face gallery",
74256
- options: [
74257
- {
74258
- id: "auraface-r100",
74259
- label: "AuraFace R100"
74260
- },
74261
- {
74262
- id: "arcface-r100",
74263
- label: "ArcFace (ResNet34)"
74264
- },
74265
- {
74266
- id: "inception-resnet-v1",
74267
- label: "Inception ResNet V1"
74268
- }
74269
- ]
74337
+ options: [{
74338
+ id: "auraface-r100",
74339
+ label: "AuraFace R100"
74340
+ }]
74270
74341
  }, {
74271
74342
  stepId: "clip-embedding",
74272
74343
  label: "Semantic search model",
74273
- defaultModelId: "mobileclip-s1",
74344
+ defaultModelId: "siglip2-b16-224",
74274
74345
  indexName: "the object semantic-search index",
74275
- options: [
74276
- {
74277
- id: "mobileclip-s1",
74278
- label: "MobileCLIP S1"
74279
- },
74280
- {
74281
- id: "mobileclip-s2",
74282
- label: "MobileCLIP S2"
74283
- },
74284
- {
74285
- id: "siglip2-b16-224",
74286
- label: "SigLIP2 B/16 (Apache-2.0)"
74287
- }
74288
- ]
74346
+ options: [{
74347
+ id: "siglip2-b16-224",
74348
+ label: "SigLIP2 B/16 (Apache-2.0)"
74349
+ }]
74289
74350
  }].map((s) => [s.stepId, s.defaultModelId])));
74290
74351
  var CLUSTER_STEP_SETTING_FIELDS = [{
74291
74352
  stepId: "face-embedding",
@@ -76211,6 +76272,34 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
76211
76272
  timestamp: number()
76212
76273
  });
76213
76274
  /**
76275
+ * A root model's boxes standing in for one crop-kind step's own inference
76276
+ * (D730) — see `runPipeline.suppliedRoot`.
76277
+ */
76278
+ var SuppliedRootOutputSchema = object({
76279
+ stepId: string(),
76280
+ /** The root model that drew the boxes — stamped on the result. */
76281
+ modelId: string(),
76282
+ detections: array(object({
76283
+ /** `[x1, y1, x2, y2]` in the pixels of the call's image. */
76284
+ bbox: tuple([
76285
+ number(),
76286
+ number(),
76287
+ number(),
76288
+ number()
76289
+ ]),
76290
+ score: number(),
76291
+ /**
76292
+ * Five facial landmarks in the same pixels — a face the step's own
76293
+ * detector already re-found inside a root face box (D730), so its
76294
+ * embedder child aligns it exactly as it aligns any detected face.
76295
+ */
76296
+ landmarks: array(object({
76297
+ x: number(),
76298
+ y: number()
76299
+ })).optional()
76300
+ })).min(1)
76301
+ });
76302
+ /**
76214
76303
  * Reference to the frame's retained NATIVE surface + the parent crop's placement
76215
76304
  * within the frame, so the executor can re-cut a leaf child ROI at native
76216
76305
  * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
@@ -76340,7 +76429,8 @@ var PipelineModelOptionSchema = object({
76340
76429
  group: ModelVariantGroupSchema.optional(),
76341
76430
  legacy: boolean().optional(),
76342
76431
  provider: ModelProviderIdSchema.optional(),
76343
- defaultConfidence: number().optional()
76432
+ defaultConfidence: number().optional(),
76433
+ suppliesSteps: array(RootStepSupplySchema).readonly().optional()
76344
76434
  });
76345
76435
  var ConfigFieldBridge = custom();
76346
76436
  var PipelineAddonSchemaSchema = object({
@@ -76608,7 +76698,16 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
76608
76698
  * `device-not-servable`), and nothing reaches the camera's live
76609
76699
  * trace stream. Absent ⇒ unchanged behaviour.
76610
76700
  */
76611
- replay: literal(true).optional()
76701
+ replay: literal(true).optional(),
76702
+ /**
76703
+ * Root-supplied detections for the tree's ROOT step (D730). Set by
76704
+ * `runDetailSubtree` when the camera's root model supplies a crop-kind
76705
+ * step (a Frigate+ `license_plate` for `plate-detection`): the step is
76706
+ * NOT inferred, these boxes are its output, and its children (the plate
76707
+ * reader) run on them through their own crop policy. Boxes are in the
76708
+ * PIXELS of the image this call carries. Absent ⇒ the root step runs.
76709
+ */
76710
+ suppliedRoot: SuppliedRootOutputSchema.optional()
76612
76711
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
76613
76712
  steps: array(PipelineStepInputSchema).min(1),
76614
76713
  frames: array(FrameInputSchema).min(1).max(255),
@@ -77173,6 +77272,22 @@ var MotionSourceEnum = _enum([
77173
77272
  "device-activity"
77174
77273
  ]);
77175
77274
  /**
77275
+ * What opened an on-motion detection SESSION: the motion source that passed the
77276
+ * camera's `motionSources` gate, and what raised it (the camera itself, or a
77277
+ * sensor the operator attached — D672). `sensorName` is the attached sensor's
77278
+ * device name as the orchestrator's camera-sensors resolution last read it;
77279
+ * absent when unknown or when the camera itself triggered.
77280
+ *
77281
+ * Carried on the session attach ONLY so the runner's `DetectionPhaseTransition`
77282
+ * can say what started the session (D672 rule 11, amended 2026-10-06). The
77283
+ * runner never decides anything on it.
77284
+ */
77285
+ var SessionTriggerSchema = object({
77286
+ source: MotionSourceEnum,
77287
+ triggeredBy: MotionTriggeredBySchema,
77288
+ sensorName: string().optional()
77289
+ });
77290
+ /**
77176
77291
  * List of motion sources active on a camera. Empty array is valid:
77177
77292
  * "no source" — happens for battery cams without firmware motion when
77178
77293
  * the operator hasn't opted into the SW analyzer (would hold the
@@ -77424,6 +77539,14 @@ var RunnerCameraConfigSchema = object({
77424
77539
  */
77425
77540
  motionSources: MotionSourcesSchema.default(["analyzer"]),
77426
77541
  /**
77542
+ * What opened this attach, on a SESSION attach only (see
77543
+ * {@link SessionTriggerSchema}). OPTIONAL with no default: absent on a
77544
+ * standing attach, an occupancy burst, a re-dispatch with no motion event
77545
+ * behind it, and every attach from an orchestrator older than the field — the
77546
+ * runner then names no source on the phase event.
77547
+ */
77548
+ sessionTrigger: SessionTriggerSchema.optional(),
77549
+ /**
77427
77550
  * WHICH root detector runs for this camera. The detection counterpart of
77428
77551
  * {@link motionSources}, same shape and same discipline: a per-camera list
77429
77552
  * of sources the system understands, vendor-agnostic, owned here.
@@ -77726,7 +77849,22 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
77726
77849
  */
77727
77850
  cropJpeg: string().optional(),
77728
77851
  parent: DetailParentSchema,
77729
- steps: array(string()).optional()
77852
+ steps: array(string()).optional(),
77853
+ /**
77854
+ * The camera's root model already drew this step's boxes (D730): a
77855
+ * `license_plate` inside the parent vehicle. The named step is NOT
77856
+ * inferred — these FRAME-space boxes are its output — and its
77857
+ * requested children (the plate reader) run on them. Absent ⇒ every
77858
+ * requested step infers as before.
77859
+ */
77860
+ supplied: object({
77861
+ stepId: string(),
77862
+ modelId: string(),
77863
+ boxes: array(object({
77864
+ bbox: NativeCropBboxSchema,
77865
+ score: number()
77866
+ })).min(1)
77867
+ }).optional()
77730
77868
  }), object({ details: array(DetailResultSchema) }).nullable(), { kind: "mutation" }), method(object({
77731
77869
  /** Catalog step id, e.g. `clip-embedding`. */
77732
77870
  stepId: string(),
@@ -84837,7 +84975,7 @@ var LocalInterfaceSchema = object({
84837
84975
  name: string(),
84838
84976
  family: _enum(["IPv4", "IPv6"]),
84839
84977
  address: string(),
84840
- /** CIDR shorthand (e.g. `192.168.x.x/24`). Empty when OS doesn't expose. */
84978
+ /** CIDR shorthand (e.g. `192.168.10.10/24`). Empty when OS doesn't expose. */
84841
84979
  cidr: string(),
84842
84980
  /** OS netmask. Stable across calls so the SDK can hash on it. */
84843
84981
  netmask: string(),
@@ -84891,7 +85029,8 @@ var ConnectionEndpointSchema = object({
84891
85029
  "lan-ipv4",
84892
85030
  "lan-ipv6",
84893
85031
  "public",
84894
- "loopback"
85032
+ "loopback",
85033
+ "app-link"
84895
85034
  ]),
84896
85035
  /**
84897
85036
  * Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
@@ -84960,15 +85099,27 @@ var GetConnectionEndpointsResultSchema = object({
84960
85099
  localPort: AdvertisedLocalPortSchema
84961
85100
  });
84962
85101
  /**
84963
- * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
84964
- * AUTO (resolved from the candidate ranking at send time); `resolved` reports
84965
- * what AUTO currently picks, so the UI can show the effective value either way.
85102
+ * The address flagged for notification links. `baseUrl: null` = AUTO, which
85103
+ * since D709 is the CamStack app link; `resolved` reports the effective value,
85104
+ * so the UI can show it either way.
84966
85105
  */
84967
85106
  var NotificationEndpointSchema = object({
84968
85107
  /** The operator's explicit choice, or null for AUTO. */
84969
85108
  baseUrl: string().nullable(),
84970
- /** What the ranking currently resolves to (null when nothing is reachable). */
84971
- resolved: string().nullable()
85109
+ /**
85110
+ * The effective flagged address: the explicit choice, else (AUTO) the
85111
+ * CamStack app link `https://camstack.zentik.app` (D709). A pre-D709 hub
85112
+ * answered the public tunnel / mesh here, or null.
85113
+ */
85114
+ resolved: string().nullable(),
85115
+ /**
85116
+ * The hub origin an app link carries as `hub=` (D709): the hub's best
85117
+ * public HTTPS address — the tunnel, then the mesh (Tailscale MagicDNS) —
85118
+ * picked from this cap's own palette by `pickAppLinkHub`. `null` when the
85119
+ * hub advertises neither; the app link is then minted without `hub=`.
85120
+ * Absent on a pre-D709 hub.
85121
+ */
85122
+ appLinkHub: string().nullable()
84972
85123
  });
84973
85124
  /**
84974
85125
  * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
@@ -84981,6 +85132,27 @@ var ViewerEndpointsSchema = object({
84981
85132
  /** What the ranking currently resolves to (may be empty if nothing is up). */
84982
85133
  resolved: array(string()).readonly()
84983
85134
  });
85135
+ /**
85136
+ * One audience's hub address (see `hub-address.ts`): the operator's stored
85137
+ * override, the address in use, and where it came from.
85138
+ */
85139
+ var HubAddressSnapshotSchema = object({
85140
+ /** The operator's override, or null for automatic. */
85141
+ override: string().nullable(),
85142
+ /** The address in use, or null when nothing qualifies (never loopback). */
85143
+ resolved: string().nullable(),
85144
+ source: _enum([
85145
+ "override",
85146
+ "auto",
85147
+ "none"
85148
+ ])
85149
+ });
85150
+ var HubAddressesSchema = object({
85151
+ /** "Hub address for agents" — agents' bundle/model pulls, LAN devices. */
85152
+ cluster: HubAddressSnapshotSchema,
85153
+ /** "Public address for sign-in links" — e-mail links, OIDC, WebAuthn, OAuth2. */
85154
+ browser: HubAddressSnapshotSchema
85155
+ });
84984
85156
  var AllowedAddressesSchema = object({
84985
85157
  /**
84986
85158
  * Allowlist of interface addresses operators have explicitly opted
@@ -85022,8 +85194,18 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
85022
85194
  * Pass `'https'` when the caller is itself loaded over HTTPS
85023
85195
  * to avoid mixed-content blocks in the browser. The public
85024
85196
  * tunnel always emits `https://` regardless. */
85025
- scheme: _enum(["http", "https"]).optional()
85026
- }), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), ViewerEndpointsSchema), method(object({ baseUrls: array(string()).readonly() }), ViewerEndpointsSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" }), method(_void(), TlsStatusSchema), method(object({ reason: string().optional() }), TlsStatusSchema, {
85197
+ scheme: _enum(["http", "https"]).optional(),
85198
+ /** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
85199
+ * an address notification links can be flagged with, not a hub
85200
+ * route. Default `false`: racers and rankers never see it. */
85201
+ includeAppLink: boolean().optional()
85202
+ }), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), ViewerEndpointsSchema), method(object({ baseUrls: array(string()).readonly() }), ViewerEndpointsSchema, { kind: "mutation" }), method(_void(), HubAddressesSchema), method(object({
85203
+ audience: _enum(["cluster", "browser"]),
85204
+ baseUrl: string().nullable()
85205
+ }), HubAddressesSchema, {
85206
+ kind: "mutation",
85207
+ auth: "admin"
85208
+ }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" }), method(_void(), TlsStatusSchema), method(object({ reason: string().optional() }), TlsStatusSchema, {
85027
85209
  kind: "mutation",
85028
85210
  auth: "admin"
85029
85211
  }), method(object({
@@ -85351,7 +85533,7 @@ var MeshPeerSchema = object({
85351
85533
  tags: array(string()).readonly(),
85352
85534
  /** True when the peer advertises itself as an exit-node candidate. */
85353
85535
  exitNodeOption: boolean(),
85354
- /** Subnet routes the peer advertises (e.g. `["192.168.x.x/24"]`). */
85536
+ /** Subnet routes the peer advertises (e.g. `["192.168.10.0/24"]`). */
85355
85537
  advertisedRoutes: array(string()).readonly(),
85356
85538
  /** Owner account login (e.g. `foo@example.com`). Null for tagged peers. */
85357
85539
  userLogin: string().nullable(),
@@ -85481,7 +85663,8 @@ var MotionStatusSchema = object({
85481
85663
  * writes this slice (not even a boot seed), and neither does the runner's
85482
85664
  * phase machine: its phase is "a session is attached" for every on-motion
85483
85665
  * session, not motion, and it now has its own `DetectionPhaseTransition`.
85484
- * A provider's `isDetected` answers from the hub's slice.
85666
+ * A provider's `isDetected` and `getStatus` answer from the hub's slice
85667
+ * (`hubMotionNativeProvider`).
85485
85668
  * - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
85486
85669
  * imports) — the provider pushes both edges itself, from the upstream
85487
85670
  * system's own state change. That is the sensor's own fact.
@@ -86827,7 +87010,13 @@ var OsdStatusSchema = object({ overlays: array(object({
86827
87010
  */
86828
87011
  availablePositions: array(OsdPositionEnum).readonly(),
86829
87012
  /** Firmware does not allow editing the `text` field (e.g. timestamp). */
86830
- readOnly: boolean().optional()
87013
+ readOnly: boolean().optional(),
87014
+ /**
87015
+ * Firmware text ceiling for this overlay. Absent = no known limit.
87016
+ * The OSD manager truncates here on every write — never stores its own
87017
+ * copy on the binding.
87018
+ */
87019
+ maxCharacters: number().int().positive().optional()
86831
87020
  })).readonly() });
86832
87021
  var OsdOverlayPatchSchema = object({
86833
87022
  enabled: boolean().optional(),
@@ -86878,7 +87067,9 @@ var OsdTemplateBindingSchema = object({
86878
87067
  valuePath: string().min(1).max(64),
86879
87068
  /** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
86880
87069
  displayUnit: string().max(16).optional(),
86881
- maxDecimals: number().int().min(0).max(4).default(1)
87070
+ maxDecimals: number().int().min(0).max(4).default(1),
87071
+ /** Raw value → display text (same shape as slot binding). Empty string hides. */
87072
+ valueMap: record(string(), string()).optional()
86882
87073
  });
86883
87074
  /**
86884
87075
  * A named template: bindings (alias → leaf field) + a parser that
@@ -86928,18 +87119,11 @@ var OsdSlotBindingSchema = object({
86928
87119
  source: OsdSourceSchema,
86929
87120
  /** `${value}` and `${unit}` are substituted; every occurrence. */
86930
87121
  template: string().max(96).default("${value}"),
86931
- /** Truncate with an ellipsis past this length. Absent = no limit. */
86932
- maxCharacters: number().int().min(4).max(64).optional(),
86933
87122
  /**
86934
87123
  * Decimal places for a numeric value. `0` yields an integer — the
86935
87124
  * documented workaround for firmwares that reject `.` in overlay text.
86936
87125
  */
86937
87126
  maxDecimals: number().int().min(0).max(4).default(1),
86938
- /**
86939
- * Appended via `${unit}` when no unit conversion produces a label.
86940
- * Prefer `source.displayUnit` for converted device-state values.
86941
- */
86942
- unitLabel: string().max(8).optional(),
86943
87127
  /** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
86944
87128
  valueMap: record(string(), string()).optional(),
86945
87129
  /** Time windows in which the slot is shown. Absent = always. */
@@ -86948,9 +87132,7 @@ var OsdSlotBindingSchema = object({
86948
87132
  * Display gate, in the notification centre's condition vocabulary.
86949
87133
  * Only the keys reported by `getConditionSupport` are accepted.
86950
87134
  */
86951
- conditions: NcConditionsSchema.optional(),
86952
- /** Rendered when the gate is closed or the value unreadable. Empty = hide. */
86953
- fallbackText: string().max(64).default("")
87135
+ conditions: NcConditionsSchema.optional()
86954
87136
  });
86955
87137
  /** One camera slot, as the operator sees it: firmware truth + our binding. */
86956
87138
  var OsdSlotViewSchema = object({
@@ -96647,6 +96829,12 @@ Object.freeze({
96647
96829
  addonId: null,
96648
96830
  access: "view"
96649
96831
  },
96832
+ "localNetwork.getHubAddresses": {
96833
+ capName: "local-network",
96834
+ capScope: "system",
96835
+ addonId: null,
96836
+ access: "view"
96837
+ },
96650
96838
  "localNetwork.getNotificationEndpoint": {
96651
96839
  capName: "local-network",
96652
96840
  capScope: "system",
@@ -96701,6 +96889,12 @@ Object.freeze({
96701
96889
  addonId: null,
96702
96890
  access: "create"
96703
96891
  },
96892
+ "localNetwork.setHubAddress": {
96893
+ capName: "local-network",
96894
+ capScope: "system",
96895
+ addonId: null,
96896
+ access: "create"
96897
+ },
96704
96898
  "localNetwork.setNotificationEndpoint": {
96705
96899
  capName: "local-network",
96706
96900
  capScope: "system",
@@ -105100,6 +105294,8 @@ new Map(Object.entries({
105100
105294
  "detection-blind": "device-events",
105101
105295
  "node-online": "system",
105102
105296
  "node-offline": "system",
105297
+ "node-restarting": "system",
105298
+ "node-updating": "system",
105103
105299
  "node-inference-unavailable": "system",
105104
105300
  "addon-crash-loop": "system",
105105
105301
  "addon-update-available": "system",
@@ -105177,18 +105373,21 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
105177
105373
  kinds: [
105178
105374
  "node-offline",
105179
105375
  "node-online",
105376
+ "node-restarting",
105180
105377
  "node-inference-unavailable"
105181
105378
  ]
105182
105379
  },
105183
105380
  {
105184
105381
  id: "addons",
105185
105382
  family: "system",
105186
- label: "Addons",
105187
- kinds: [
105188
- "addon-crash-loop",
105189
- "addon-update-available",
105190
- "addon-updated"
105191
- ]
105383
+ label: "Addons stopped",
105384
+ kinds: ["addon-crash-loop"]
105385
+ },
105386
+ {
105387
+ id: "addon-updates",
105388
+ family: "system",
105389
+ label: "Addon updates",
105390
+ kinds: ["addon-update-available", "addon-updated"]
105192
105391
  },
105193
105392
  {
105194
105393
  id: "updates",
@@ -105196,6 +105395,7 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
105196
105395
  label: "Server & container updates",
105197
105396
  kinds: [
105198
105397
  "server-update-available",
105398
+ "node-updating",
105199
105399
  "server-updated",
105200
105400
  "wrapper-update-available"
105201
105401
  ]
@@ -105343,18 +105543,21 @@ var STATE_SUMMARY_SECTION_VARS = [
105343
105543
  kinds: [
105344
105544
  "node-offline",
105345
105545
  "node-online",
105546
+ "node-restarting",
105346
105547
  "node-inference-unavailable"
105347
105548
  ]
105348
105549
  },
105349
105550
  {
105350
105551
  name: "summaryAddons",
105351
- label: "Summary: addons",
105352
- example: "@camstack/addon-recorder 1.2.3 → 1.2.4",
105353
- kinds: [
105354
- "addon-crash-loop",
105355
- "addon-update-available",
105356
- "addon-updated"
105357
- ]
105552
+ label: "Summary: addons stopped",
105553
+ example: "hub — recorder stopped after repeated crashes",
105554
+ kinds: ["addon-crash-loop"]
105555
+ },
105556
+ {
105557
+ name: "summaryAddonUpdates",
105558
+ label: "Summary: addon updates",
105559
+ example: "hub:\n• @camstack/addon-recorder 1.2.3 → 1.2.4",
105560
+ kinds: ["addon-update-available", "addon-updated"]
105358
105561
  },
105359
105562
  {
105360
105563
  name: "summaryUpdates",
@@ -105362,6 +105565,7 @@ var STATE_SUMMARY_SECTION_VARS = [
105362
105565
  example: "server 1.2.377 → 1.2.378",
105363
105566
  kinds: [
105364
105567
  "server-update-available",
105568
+ "node-updating",
105365
105569
  "server-updated",
105366
105570
  "wrapper-update-available"
105367
105571
  ]
@@ -105394,7 +105598,7 @@ var STATE_SUMMARY_VARS = [
105394
105598
  {
105395
105599
  name: "summarySection",
105396
105600
  label: "Summary: this section",
105397
- description: "The section a per-section summary row is about (D698); on a summary sent as one message, every section it reports, comma-separated.",
105601
+ description: "The section a per-section summary row is about; on a summary sent as one message, every section it reports, comma-separated.",
105398
105602
  example: "Batterie scariche",
105399
105603
  group: "system",
105400
105604
  families: ["rule"],
@@ -105438,6 +105642,7 @@ var STATE_SUMMARY_VARS = [
105438
105642
  [...RULE_BODY_ONLY],
105439
105643
  [...RULE_TEXT],
105440
105644
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
105645
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
105441
105646
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
105442
105647
  ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
105443
105648
  ([...RULE_TEXT], [...CONSUMABLE]),
@@ -105478,6 +105683,8 @@ var STATE_SUMMARY_VARS = [
105478
105683
  [...RULE_TEXT],
105479
105684
  [...RULE_TEXT],
105480
105685
  [...RULE_TEXT],
105686
+ [...RULE_TEXT],
105687
+ [...RULE_TEXT],
105481
105688
  ...STATE_SUMMARY_VARS
105482
105689
  ];
105483
105690
  new Set([
@@ -108392,6 +108599,18 @@ function nextVacuumControlSlice(prev, next) {
108392
108599
  }
108393
108600
  //#endregion
108394
108601
  //#region src/devices/dreame-camera-stream.ts
108602
+ /**
108603
+ * The robot camera as a stream the broker can pull — the pure facts.
108604
+ *
108605
+ * A Dreame relay carries video only between `open()` and `close()`, and the
108606
+ * robot runs on a battery, so the camera cannot sit in the catalog with a
108607
+ * URL the way a mains RTSP camera does. The seam is the broker's own battery
108608
+ * path: `battery.wakeForStream` opens the monitor, and the catalog then
108609
+ * names the local RFC 4571 server (`dreame-rfc4571-server.ts`) the broker
108610
+ * pulls. While the monitor is CLOSED the
108611
+ * catalog carries the broker's lazy sentinel (Reolink's shape): the stream is
108612
+ * listed, and the first dial asks the publisher to materialize it.
108613
+ */
108395
108614
  /** The one stream the robot camera offers. Half of the broker id (`<deviceId>/live`). */
108396
108615
  var CAMERA_STREAM_ID = "live";
108397
108616
  /** The plugin's cap: a monitor never outlives this, whoever is watching. */
@@ -108471,17 +108690,13 @@ function cameraStreamCatalog(session) {
108471
108690
  /**
108472
108691
  * The host the broker is told to dial. The provider and the ingest owner
108473
108692
  * share the hub today, but a broker on another node reaches the hub only
108474
- * through its public address — which is what `CAMSTACK_HUB_PUBLIC_URL` is
108475
- * for. Loopback when nothing better is known.
108693
+ * through the hub's address for agents — `local-network.getHubAddresses()
108694
+ * .cluster` (the Network page's override, else the detected LAN address).
108695
+ * Loopback when that names nothing: then the only broker that can dial at
108696
+ * all is this node's own.
108476
108697
  */
108477
- function advertisedRfcHost(hubPublicUrl) {
108478
- if (hubPublicUrl === void 0 || hubPublicUrl.length === 0) return "127.0.0.1";
108479
- try {
108480
- const host = new URL(hubPublicUrl).hostname;
108481
- return host.length > 0 ? host : "127.0.0.1";
108482
- } catch {
108483
- return "127.0.0.1";
108484
- }
108698
+ function advertisedRfcHost(clusterBaseUrl) {
108699
+ return hubAddressHostname(clusterBaseUrl) ?? "127.0.0.1";
108485
108700
  }
108486
108701
  function rfcUrl(host, port) {
108487
108702
  return `tcp://${host}:${String(port)}`;
@@ -154871,6 +155086,23 @@ var DreameCameraDevice = class extends DreameChildDevice {
154871
155086
  }
154872
155087
  unsubscribeRefresh = null;
154873
155088
  /** Register the stream on the broker — the server, or the lazy sentinel (push semantics; the catalog is the durable copy). */
155089
+ /**
155090
+ * The hub's address for agents (`local-network.getHubAddresses().cluster`:
155091
+ * the Network page's override, else the detected LAN address). A failed
155092
+ * read is `null` — `advertisedRfcHost` then answers loopback, which this
155093
+ * node's own broker can still dial.
155094
+ */
155095
+ async hubClusterAddress() {
155096
+ try {
155097
+ return (await this.ctx.api.localNetwork.getHubAddresses.query()).cluster.resolved;
155098
+ } catch (err) {
155099
+ this.ctx.logger.warn("robot camera: hub address unavailable — advertising loopback", {
155100
+ tags: { deviceId: this.id },
155101
+ meta: { error: err instanceof Error ? err.message : String(err) }
155102
+ });
155103
+ return null;
155104
+ }
155105
+ }
154874
155106
  async publishRelay(session) {
154875
155107
  const [descriptor] = cameraStreamCatalog(session);
154876
155108
  if (descriptor === void 0) return;
@@ -154956,7 +155188,7 @@ var DreameCameraDevice = class extends DreameChildDevice {
154956
155188
  rfc
154957
155189
  };
154958
155190
  this.session = {
154959
- url: rfcUrl(advertisedRfcHost(process.env.CAMSTACK_HUB_PUBLIC_URL), rfc.port),
155191
+ url: rfcUrl(advertisedRfcHost(await this.hubClusterAddress()), rfc.port),
154960
155192
  sdp: rfc.sdp
154961
155193
  };
154962
155194
  this.sessionOpenedAt = Date.now();