@camstack/addon-notifiers 1.2.126 → 1.2.128

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 +127 -81
  2. package/dist/addon.mjs +127 -81
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -10689,11 +10689,11 @@ var ModelVariantGroupSchema = object({
10689
10689
  /** Quantization axis. Omit ⇒ the fp32 base build. */
10690
10690
  precision: _enum(["fp32", "int8"]).optional(),
10691
10691
  /**
10692
- * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
10693
- * latency-optimized export (e.g. ReLU-activation variant) — the slot the
10694
- * future performance variants plug into.
10692
+ * Training / latency-variant axis. Omit ⇒ the standard build. `test` marks
10693
+ * an alternate training of the same family (Scrypted `relu_test`); chip
10694
+ * label is "Test".
10695
10695
  */
10696
- optimization: _enum(["standard", "fast"]).optional(),
10696
+ optimization: _enum(["standard", "test"]).optional(),
10697
10697
  /**
10698
10698
  * Input-resolution axis (square input side, px). Omit ⇒ the family's native
10699
10699
  * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
@@ -10716,9 +10716,10 @@ var ModelProviderIdSchema = _enum([
10716
10716
  * are per upstream. A persisted custom-model row written when this field was a
10717
10717
  * string still parses — the string is lifted into `{ weights }`.
10718
10718
  *
10719
- * Every licence surface (the admin "Models and licenses" page,
10720
- * `THIRD_PARTY_MODELS.md`, the Hugging Face model table) is GENERATED from
10721
- * these records by `scripts/gen-model-licenses.ts` (D661).
10719
+ * Every machine licence surface (`THIRD_PARTY_MODELS.md`, the Hugging Face
10720
+ * model table) is GENERATED from these records by
10721
+ * `scripts/gen-model-licenses.ts` (D661). Operator-facing docs live in
10722
+ * `camstack/docs` → `/docs/detection/models`.
10722
10723
  */
10723
10724
  var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { weights: value } : value, object({
10724
10725
  /** SPDX id of the WEIGHTS' terms, a `LicenseRef-*` for non-SPDX terms, or `UNKNOWN`. */
@@ -14266,7 +14267,7 @@ DeviceType.Camera;
14266
14267
  * This matters here more than anywhere else in the repo, because every provider
14267
14268
  * that knows a device's host knows its PASSWORD too: `{ host, port, username,
14268
14269
  * password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
14269
- * and `http://admin:hunter2@192.168.x.x/` is a URL a browser accepts. The
14270
+ * and `http://admin:hunter2@192.168.60.139/` is a URL a browser accepts. The
14270
14271
  * camera's own page will ask for its own login. That is correct, and pre-
14271
14272
  * filling it is the operator's business, not ours.
14272
14273
  *
@@ -17250,7 +17251,7 @@ method(object({
17250
17251
  timeoutMs: 6e5
17251
17252
  });
17252
17253
  method(object({
17253
- nodeId: string(),
17254
+ targetNodeId: string(),
17254
17255
  modelId: string(),
17255
17256
  format: _enum(MODEL_FORMATS),
17256
17257
  entry: ModelCatalogEntrySchema
@@ -19738,8 +19739,9 @@ var NcRuleSchema = NcRuleInputSchema.extend({
19738
19739
  disabledTargetIds: array(string()).default([])
19739
19740
  });
19740
19741
  /**
19741
- * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
19742
- * still-true state. `backups-exports` and `alarm` are not sections — a completed
19742
+ * The state-summary SECTIONS (D647 / D698 / D708): the D637 kind groups that
19743
+ * report a still-true state. `addons` is the crash-looped addons only and
19744
+ * `addon-updates` the addons that are behind (D708 split them). `backups-exports` and `alarm` are not sections — a completed
19743
19745
  * job is not an open state, and the alarm stays instant.
19744
19746
  */
19745
19747
  var NcStateSummarySectionIdSchema = _enum([
@@ -19751,6 +19753,7 @@ var NcStateSummarySectionIdSchema = _enum([
19751
19753
  "detection-blind",
19752
19754
  "nodes",
19753
19755
  "addons",
19756
+ "addon-updates",
19754
19757
  "updates"
19755
19758
  ]);
19756
19759
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
@@ -20231,7 +20234,19 @@ var NcAlarmModeDelaysListSchema = array(object({
20231
20234
  * When this mode may be armed. Absent / null = always allowed (same as a
20232
20235
  * rule with no `schedule`). Evaluated with {@link isScheduleActive}.
20233
20236
  */
20234
- schedule: NcScheduleSchema.optional()
20237
+ schedule: NcScheduleSchema.optional(),
20238
+ /**
20239
+ * D706: while this mode's ENTRY DELAY runs, do the alarm rules' own pushes go out (as an
20240
+ * intrusion naming the locations)? Absent = false — the entry-delay notification is the
20241
+ * only one, and the matches ride the triggered notification's mosaic (D705).
20242
+ */
20243
+ notifyDuringEntryDelay: boolean().optional(),
20244
+ /**
20245
+ * D706: notify "watched again" when a sensor this arm bypassed closes and opens again.
20246
+ * Absent = false (logged, not sent). Even when true it is not sent when that same sensor
20247
+ * is the one that starts the entry delay — the entry-delay notification already names it.
20248
+ */
20249
+ notifySensorRewatched: boolean().optional()
20235
20250
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
20236
20251
  var NcAlarmModeDelaysEditListSchema = array(object({
20237
20252
  mode: AlarmArmModeSchema,
@@ -20240,7 +20255,11 @@ var NcAlarmModeDelaysEditListSchema = array(object({
20240
20255
  triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional(),
20241
20256
  postTriggeredAction: NcAlarmPostTriggeredActionSchema.nullable().optional(),
20242
20257
  /** Set a schedule, or `null` to clear (mode always allowed again). */
20243
- schedule: NcScheduleSchema.nullable().optional()
20258
+ schedule: NcScheduleSchema.nullable().optional(),
20259
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
20260
+ notifyDuringEntryDelay: boolean().nullable().optional(),
20261
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
20262
+ notifySensorRewatched: boolean().nullable().optional()
20244
20263
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
20245
20264
  /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
20246
20265
  var NcAlarmNonBlockingEditSchema = object({
@@ -21201,39 +21220,19 @@ Object.freeze(Object.fromEntries([{
21201
21220
  label: "Face recognition model",
21202
21221
  defaultModelId: "auraface-r100",
21203
21222
  indexName: "the enrolled face gallery",
21204
- options: [
21205
- {
21206
- id: "auraface-r100",
21207
- label: "AuraFace R100"
21208
- },
21209
- {
21210
- id: "arcface-r100",
21211
- label: "ArcFace (ResNet34)"
21212
- },
21213
- {
21214
- id: "inception-resnet-v1",
21215
- label: "Inception ResNet V1"
21216
- }
21217
- ]
21223
+ options: [{
21224
+ id: "auraface-r100",
21225
+ label: "AuraFace R100"
21226
+ }]
21218
21227
  }, {
21219
21228
  stepId: "clip-embedding",
21220
21229
  label: "Semantic search model",
21221
- defaultModelId: "mobileclip-s1",
21230
+ defaultModelId: "siglip2-b16-224",
21222
21231
  indexName: "the object semantic-search index",
21223
- options: [
21224
- {
21225
- id: "mobileclip-s1",
21226
- label: "MobileCLIP S1"
21227
- },
21228
- {
21229
- id: "mobileclip-s2",
21230
- label: "MobileCLIP S2"
21231
- },
21232
- {
21233
- id: "siglip2-b16-224",
21234
- label: "SigLIP2 B/16 (Apache-2.0)"
21235
- }
21236
- ]
21232
+ options: [{
21233
+ id: "siglip2-b16-224",
21234
+ label: "SigLIP2 B/16 (Apache-2.0)"
21235
+ }]
21237
21236
  }].map((s) => [s.stepId, s.defaultModelId])));
21238
21237
  var CLUSTER_STEP_SETTING_FIELDS = [{
21239
21238
  stepId: "face-embedding",
@@ -24081,6 +24080,22 @@ var MotionSourceEnum = _enum([
24081
24080
  "device-activity"
24082
24081
  ]);
24083
24082
  /**
24083
+ * What opened an on-motion detection SESSION: the motion source that passed the
24084
+ * camera's `motionSources` gate, and what raised it (the camera itself, or a
24085
+ * sensor the operator attached — D672). `sensorName` is the attached sensor's
24086
+ * device name as the orchestrator's camera-sensors resolution last read it;
24087
+ * absent when unknown or when the camera itself triggered.
24088
+ *
24089
+ * Carried on the session attach ONLY so the runner's `DetectionPhaseTransition`
24090
+ * can say what started the session (D672 rule 11, amended 2026-10-06). The
24091
+ * runner never decides anything on it.
24092
+ */
24093
+ var SessionTriggerSchema = object({
24094
+ source: MotionSourceEnum,
24095
+ triggeredBy: MotionTriggeredBySchema,
24096
+ sensorName: string().optional()
24097
+ });
24098
+ /**
24084
24099
  * List of motion sources active on a camera. Empty array is valid:
24085
24100
  * "no source" — happens for battery cams without firmware motion when
24086
24101
  * the operator hasn't opted into the SW analyzer (would hold the
@@ -24332,6 +24347,14 @@ var RunnerCameraConfigSchema = object({
24332
24347
  */
24333
24348
  motionSources: MotionSourcesSchema.default(["analyzer"]),
24334
24349
  /**
24350
+ * What opened this attach, on a SESSION attach only (see
24351
+ * {@link SessionTriggerSchema}). OPTIONAL with no default: absent on a
24352
+ * standing attach, an occupancy burst, a re-dispatch with no motion event
24353
+ * behind it, and every attach from an orchestrator older than the field — the
24354
+ * runner then names no source on the phase event.
24355
+ */
24356
+ sessionTrigger: SessionTriggerSchema.optional(),
24357
+ /**
24335
24358
  * WHICH root detector runs for this camera. The detection counterpart of
24336
24359
  * {@link motionSources}, same shape and same discipline: a per-camera list
24337
24360
  * of sources the system understands, vendor-agnostic, owned here.
@@ -30555,7 +30578,7 @@ var LocalInterfaceSchema = object({
30555
30578
  name: string(),
30556
30579
  family: _enum(["IPv4", "IPv6"]),
30557
30580
  address: string(),
30558
- /** CIDR shorthand (e.g. `192.168.x.x/24`). Empty when OS doesn't expose. */
30581
+ /** CIDR shorthand (e.g. `192.168.10.10/24`). Empty when OS doesn't expose. */
30559
30582
  cidr: string(),
30560
30583
  /** OS netmask. Stable across calls so the SDK can hash on it. */
30561
30584
  netmask: string(),
@@ -30609,7 +30632,8 @@ var ConnectionEndpointSchema = object({
30609
30632
  "lan-ipv4",
30610
30633
  "lan-ipv6",
30611
30634
  "public",
30612
- "loopback"
30635
+ "loopback",
30636
+ "app-link"
30613
30637
  ]),
30614
30638
  /**
30615
30639
  * Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
@@ -30678,15 +30702,27 @@ var GetConnectionEndpointsResultSchema = object({
30678
30702
  localPort: AdvertisedLocalPortSchema
30679
30703
  });
30680
30704
  /**
30681
- * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
30682
- * AUTO (resolved from the candidate ranking at send time); `resolved` reports
30683
- * what AUTO currently picks, so the UI can show the effective value either way.
30705
+ * The address flagged for notification links. `baseUrl: null` = AUTO, which
30706
+ * since D709 is the CamStack app link; `resolved` reports the effective value,
30707
+ * so the UI can show it either way.
30684
30708
  */
30685
30709
  var NotificationEndpointSchema = object({
30686
30710
  /** The operator's explicit choice, or null for AUTO. */
30687
30711
  baseUrl: string().nullable(),
30688
- /** What the ranking currently resolves to (null when nothing is reachable). */
30689
- resolved: string().nullable()
30712
+ /**
30713
+ * The effective flagged address: the explicit choice, else (AUTO) the
30714
+ * CamStack app link `https://camstack.zentik.app` (D709). A pre-D709 hub
30715
+ * answered the public tunnel / mesh here, or null.
30716
+ */
30717
+ resolved: string().nullable(),
30718
+ /**
30719
+ * The hub origin an app link carries as `hub=` (D709): the hub's best
30720
+ * public HTTPS address — the tunnel, then the mesh (Tailscale MagicDNS) —
30721
+ * picked from this cap's own palette by `pickAppLinkHub`. `null` when the
30722
+ * hub advertises neither; the app link is then minted without `hub=`.
30723
+ * Absent on a pre-D709 hub.
30724
+ */
30725
+ appLinkHub: string().nullable()
30690
30726
  });
30691
30727
  /**
30692
30728
  * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
@@ -30740,7 +30776,11 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
30740
30776
  * Pass `'https'` when the caller is itself loaded over HTTPS
30741
30777
  * to avoid mixed-content blocks in the browser. The public
30742
30778
  * tunnel always emits `https://` regardless. */
30743
- scheme: _enum(["http", "https"]).optional()
30779
+ scheme: _enum(["http", "https"]).optional(),
30780
+ /** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
30781
+ * an address notification links can be flagged with, not a hub
30782
+ * route. Default `false`: racers and rankers never see it. */
30783
+ includeAppLink: boolean().optional()
30744
30784
  }), 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, {
30745
30785
  kind: "mutation",
30746
30786
  auth: "admin"
@@ -31001,7 +31041,7 @@ var MeshPeerSchema = object({
31001
31041
  tags: array(string()).readonly(),
31002
31042
  /** True when the peer advertises itself as an exit-node candidate. */
31003
31043
  exitNodeOption: boolean(),
31004
- /** Subnet routes the peer advertises (e.g. `["192.168.x.x/24"]`). */
31044
+ /** Subnet routes the peer advertises (e.g. `["192.168.10.0/24"]`). */
31005
31045
  advertisedRoutes: array(string()).readonly(),
31006
31046
  /** Owner account login (e.g. `foo@example.com`). Null for tagged peers. */
31007
31047
  userLogin: string().nullable(),
@@ -31945,7 +31985,13 @@ var OsdStatusSchema = object({ overlays: array(object({
31945
31985
  */
31946
31986
  availablePositions: array(OsdPositionEnum).readonly(),
31947
31987
  /** Firmware does not allow editing the `text` field (e.g. timestamp). */
31948
- readOnly: boolean().optional()
31988
+ readOnly: boolean().optional(),
31989
+ /**
31990
+ * Firmware text ceiling for this overlay. Absent = no known limit.
31991
+ * The OSD manager truncates here on every write — never stores its own
31992
+ * copy on the binding.
31993
+ */
31994
+ maxCharacters: number().int().positive().optional()
31949
31995
  })).readonly() });
31950
31996
  var OsdOverlayPatchSchema = object({
31951
31997
  enabled: boolean().optional(),
@@ -31996,7 +32042,9 @@ var OsdTemplateBindingSchema = object({
31996
32042
  valuePath: string().min(1).max(64),
31997
32043
  /** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
31998
32044
  displayUnit: string().max(16).optional(),
31999
- maxDecimals: number().int().min(0).max(4).default(1)
32045
+ maxDecimals: number().int().min(0).max(4).default(1),
32046
+ /** Raw value → display text (same shape as slot binding). Empty string hides. */
32047
+ valueMap: record(string(), string()).optional()
32000
32048
  });
32001
32049
  /**
32002
32050
  * A named template: bindings (alias → leaf field) + a parser that
@@ -32046,18 +32094,11 @@ var OsdSlotBindingSchema = object({
32046
32094
  source: OsdSourceSchema,
32047
32095
  /** `${value}` and `${unit}` are substituted; every occurrence. */
32048
32096
  template: string().max(96).default("${value}"),
32049
- /** Truncate with an ellipsis past this length. Absent = no limit. */
32050
- maxCharacters: number().int().min(4).max(64).optional(),
32051
32097
  /**
32052
32098
  * Decimal places for a numeric value. `0` yields an integer — the
32053
32099
  * documented workaround for firmwares that reject `.` in overlay text.
32054
32100
  */
32055
32101
  maxDecimals: number().int().min(0).max(4).default(1),
32056
- /**
32057
- * Appended via `${unit}` when no unit conversion produces a label.
32058
- * Prefer `source.displayUnit` for converted device-state values.
32059
- */
32060
- unitLabel: string().max(8).optional(),
32061
32102
  /** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
32062
32103
  valueMap: record(string(), string()).optional(),
32063
32104
  /** Time windows in which the slot is shown. Absent = always. */
@@ -32066,9 +32107,7 @@ var OsdSlotBindingSchema = object({
32066
32107
  * Display gate, in the notification centre's condition vocabulary.
32067
32108
  * Only the keys reported by `getConditionSupport` are accepted.
32068
32109
  */
32069
- conditions: NcConditionsSchema.optional(),
32070
- /** Rendered when the gate is closed or the value unreadable. Empty = hide. */
32071
- fallbackText: string().max(64).default("")
32110
+ conditions: NcConditionsSchema.optional()
32072
32111
  });
32073
32112
  /** One camera slot, as the operator sees it: firmware truth + our binding. */
32074
32113
  var OsdSlotViewSchema = object({
@@ -47952,12 +47991,14 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
47952
47991
  {
47953
47992
  id: "addons",
47954
47993
  family: "system",
47955
- label: "Addons",
47956
- kinds: [
47957
- "addon-crash-loop",
47958
- "addon-update-available",
47959
- "addon-updated"
47960
- ]
47994
+ label: "Addons stopped",
47995
+ kinds: ["addon-crash-loop"]
47996
+ },
47997
+ {
47998
+ id: "addon-updates",
47999
+ family: "system",
48000
+ label: "Addon updates",
48001
+ kinds: ["addon-update-available", "addon-updated"]
47961
48002
  },
47962
48003
  {
47963
48004
  id: "updates",
@@ -48117,13 +48158,15 @@ var STATE_SUMMARY_SECTION_VARS = [
48117
48158
  },
48118
48159
  {
48119
48160
  name: "summaryAddons",
48120
- label: "Summary: addons",
48121
- example: "@camstack/addon-recorder 1.2.3 → 1.2.4",
48122
- kinds: [
48123
- "addon-crash-loop",
48124
- "addon-update-available",
48125
- "addon-updated"
48126
- ]
48161
+ label: "Summary: addons stopped",
48162
+ example: "hub — recorder stopped after repeated crashes",
48163
+ kinds: ["addon-crash-loop"]
48164
+ },
48165
+ {
48166
+ name: "summaryAddonUpdates",
48167
+ label: "Summary: addon updates",
48168
+ example: "hub:\n• @camstack/addon-recorder 1.2.3 → 1.2.4",
48169
+ kinds: ["addon-update-available", "addon-updated"]
48127
48170
  },
48128
48171
  {
48129
48172
  name: "summaryUpdates",
@@ -48207,6 +48250,7 @@ var STATE_SUMMARY_VARS = [
48207
48250
  [...RULE_BODY_ONLY],
48208
48251
  [...RULE_TEXT],
48209
48252
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
48253
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
48210
48254
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
48211
48255
  ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
48212
48256
  ([...RULE_TEXT], [...CONSUMABLE]),
@@ -48247,6 +48291,8 @@ var STATE_SUMMARY_VARS = [
48247
48291
  [...RULE_TEXT],
48248
48292
  [...RULE_TEXT],
48249
48293
  [...RULE_TEXT],
48294
+ [...RULE_TEXT],
48295
+ [...RULE_TEXT],
48250
48296
  ...STATE_SUMMARY_VARS
48251
48297
  ];
48252
48298
  new Set([
@@ -50623,12 +50669,12 @@ var BUCKETS_PATH = "/api/v1/buckets";
50623
50669
  /** Zentik `deliveryType` enum — mirrors our declared level ids 1:1. */
50624
50670
  var DEFAULT_DELIVERY_TYPE = "NORMAL";
50625
50671
  /**
50626
- * Backend `CreateMessageDto` limits (verbatim from
50627
- * `zentik-notifier/backend/.../create-message.dto.ts`): `title` is REQUIRED and
50628
- * `@MaxLength(100)`, `body` is optional `@MaxLength(500)`. Over-length → 400.
50672
+ * Backend `CreateMessageDto` title is REQUIRED and `@MaxLength(100)`.
50673
+ * Body has no CamStack truncate — model "no cap" like webhook (positive int).
50629
50674
  */
50630
50675
  var TITLE_MAX_LEN = 100;
50631
- var BODY_MAX_LEN = 500;
50676
+ /** Positive int required by caps; large enough that degrade never truncates. */
50677
+ var NO_BODY_CAP = 1e6;
50632
50678
  /** Non-empty fallback title when a notification carries no title. */
50633
50679
  var FALLBACK_TITLE = "CamStack";
50634
50680
  /** url-mode attachment ceiling (no hard doc limit; matrix §5 → 5). */
@@ -50981,7 +51027,7 @@ var NOTIFIER_ADAPTERS = [
50981
51027
  actionIcons: true,
50982
51028
  sound: true,
50983
51029
  ttl: true,
50984
- bodyMaxLen: BODY_MAX_LEN
51030
+ bodyMaxLen: NO_BODY_CAP
50985
51031
  }
50986
51032
  },
50987
51033
  destinationKeys: [
package/dist/addon.mjs CHANGED
@@ -10662,11 +10662,11 @@ var ModelVariantGroupSchema = object({
10662
10662
  /** Quantization axis. Omit ⇒ the fp32 base build. */
10663
10663
  precision: _enum(["fp32", "int8"]).optional(),
10664
10664
  /**
10665
- * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
10666
- * latency-optimized export (e.g. ReLU-activation variant) — the slot the
10667
- * future performance variants plug into.
10665
+ * Training / latency-variant axis. Omit ⇒ the standard build. `test` marks
10666
+ * an alternate training of the same family (Scrypted `relu_test`); chip
10667
+ * label is "Test".
10668
10668
  */
10669
- optimization: _enum(["standard", "fast"]).optional(),
10669
+ optimization: _enum(["standard", "test"]).optional(),
10670
10670
  /**
10671
10671
  * Input-resolution axis (square input side, px). Omit ⇒ the family's native
10672
10672
  * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
@@ -10689,9 +10689,10 @@ var ModelProviderIdSchema = _enum([
10689
10689
  * are per upstream. A persisted custom-model row written when this field was a
10690
10690
  * string still parses — the string is lifted into `{ weights }`.
10691
10691
  *
10692
- * Every licence surface (the admin "Models and licenses" page,
10693
- * `THIRD_PARTY_MODELS.md`, the Hugging Face model table) is GENERATED from
10694
- * these records by `scripts/gen-model-licenses.ts` (D661).
10692
+ * Every machine licence surface (`THIRD_PARTY_MODELS.md`, the Hugging Face
10693
+ * model table) is GENERATED from these records by
10694
+ * `scripts/gen-model-licenses.ts` (D661). Operator-facing docs live in
10695
+ * `camstack/docs` → `/docs/detection/models`.
10695
10696
  */
10696
10697
  var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { weights: value } : value, object({
10697
10698
  /** SPDX id of the WEIGHTS' terms, a `LicenseRef-*` for non-SPDX terms, or `UNKNOWN`. */
@@ -14239,7 +14240,7 @@ DeviceType.Camera;
14239
14240
  * This matters here more than anywhere else in the repo, because every provider
14240
14241
  * that knows a device's host knows its PASSWORD too: `{ host, port, username,
14241
14242
  * password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
14242
- * and `http://admin:hunter2@192.168.x.x/` is a URL a browser accepts. The
14243
+ * and `http://admin:hunter2@192.168.60.139/` is a URL a browser accepts. The
14243
14244
  * camera's own page will ask for its own login. That is correct, and pre-
14244
14245
  * filling it is the operator's business, not ours.
14245
14246
  *
@@ -17223,7 +17224,7 @@ method(object({
17223
17224
  timeoutMs: 6e5
17224
17225
  });
17225
17226
  method(object({
17226
- nodeId: string(),
17227
+ targetNodeId: string(),
17227
17228
  modelId: string(),
17228
17229
  format: _enum(MODEL_FORMATS),
17229
17230
  entry: ModelCatalogEntrySchema
@@ -19711,8 +19712,9 @@ var NcRuleSchema = NcRuleInputSchema.extend({
19711
19712
  disabledTargetIds: array(string()).default([])
19712
19713
  });
19713
19714
  /**
19714
- * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
19715
- * still-true state. `backups-exports` and `alarm` are not sections — a completed
19715
+ * The state-summary SECTIONS (D647 / D698 / D708): the D637 kind groups that
19716
+ * report a still-true state. `addons` is the crash-looped addons only and
19717
+ * `addon-updates` the addons that are behind (D708 split them). `backups-exports` and `alarm` are not sections — a completed
19716
19718
  * job is not an open state, and the alarm stays instant.
19717
19719
  */
19718
19720
  var NcStateSummarySectionIdSchema = _enum([
@@ -19724,6 +19726,7 @@ var NcStateSummarySectionIdSchema = _enum([
19724
19726
  "detection-blind",
19725
19727
  "nodes",
19726
19728
  "addons",
19729
+ "addon-updates",
19727
19730
  "updates"
19728
19731
  ]);
19729
19732
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
@@ -20204,7 +20207,19 @@ var NcAlarmModeDelaysListSchema = array(object({
20204
20207
  * When this mode may be armed. Absent / null = always allowed (same as a
20205
20208
  * rule with no `schedule`). Evaluated with {@link isScheduleActive}.
20206
20209
  */
20207
- schedule: NcScheduleSchema.optional()
20210
+ schedule: NcScheduleSchema.optional(),
20211
+ /**
20212
+ * D706: while this mode's ENTRY DELAY runs, do the alarm rules' own pushes go out (as an
20213
+ * intrusion naming the locations)? Absent = false — the entry-delay notification is the
20214
+ * only one, and the matches ride the triggered notification's mosaic (D705).
20215
+ */
20216
+ notifyDuringEntryDelay: boolean().optional(),
20217
+ /**
20218
+ * D706: notify "watched again" when a sensor this arm bypassed closes and opens again.
20219
+ * Absent = false (logged, not sent). Even when true it is not sent when that same sensor
20220
+ * is the one that starts the entry delay — the entry-delay notification already names it.
20221
+ */
20222
+ notifySensorRewatched: boolean().optional()
20208
20223
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
20209
20224
  var NcAlarmModeDelaysEditListSchema = array(object({
20210
20225
  mode: AlarmArmModeSchema,
@@ -20213,7 +20228,11 @@ var NcAlarmModeDelaysEditListSchema = array(object({
20213
20228
  triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional(),
20214
20229
  postTriggeredAction: NcAlarmPostTriggeredActionSchema.nullable().optional(),
20215
20230
  /** Set a schedule, or `null` to clear (mode always allowed again). */
20216
- schedule: NcScheduleSchema.nullable().optional()
20231
+ schedule: NcScheduleSchema.nullable().optional(),
20232
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
20233
+ notifyDuringEntryDelay: boolean().nullable().optional(),
20234
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
20235
+ notifySensorRewatched: boolean().nullable().optional()
20217
20236
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
20218
20237
  /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
20219
20238
  var NcAlarmNonBlockingEditSchema = object({
@@ -21174,39 +21193,19 @@ Object.freeze(Object.fromEntries([{
21174
21193
  label: "Face recognition model",
21175
21194
  defaultModelId: "auraface-r100",
21176
21195
  indexName: "the enrolled face gallery",
21177
- options: [
21178
- {
21179
- id: "auraface-r100",
21180
- label: "AuraFace R100"
21181
- },
21182
- {
21183
- id: "arcface-r100",
21184
- label: "ArcFace (ResNet34)"
21185
- },
21186
- {
21187
- id: "inception-resnet-v1",
21188
- label: "Inception ResNet V1"
21189
- }
21190
- ]
21196
+ options: [{
21197
+ id: "auraface-r100",
21198
+ label: "AuraFace R100"
21199
+ }]
21191
21200
  }, {
21192
21201
  stepId: "clip-embedding",
21193
21202
  label: "Semantic search model",
21194
- defaultModelId: "mobileclip-s1",
21203
+ defaultModelId: "siglip2-b16-224",
21195
21204
  indexName: "the object semantic-search index",
21196
- options: [
21197
- {
21198
- id: "mobileclip-s1",
21199
- label: "MobileCLIP S1"
21200
- },
21201
- {
21202
- id: "mobileclip-s2",
21203
- label: "MobileCLIP S2"
21204
- },
21205
- {
21206
- id: "siglip2-b16-224",
21207
- label: "SigLIP2 B/16 (Apache-2.0)"
21208
- }
21209
- ]
21205
+ options: [{
21206
+ id: "siglip2-b16-224",
21207
+ label: "SigLIP2 B/16 (Apache-2.0)"
21208
+ }]
21210
21209
  }].map((s) => [s.stepId, s.defaultModelId])));
21211
21210
  var CLUSTER_STEP_SETTING_FIELDS = [{
21212
21211
  stepId: "face-embedding",
@@ -24054,6 +24053,22 @@ var MotionSourceEnum = _enum([
24054
24053
  "device-activity"
24055
24054
  ]);
24056
24055
  /**
24056
+ * What opened an on-motion detection SESSION: the motion source that passed the
24057
+ * camera's `motionSources` gate, and what raised it (the camera itself, or a
24058
+ * sensor the operator attached — D672). `sensorName` is the attached sensor's
24059
+ * device name as the orchestrator's camera-sensors resolution last read it;
24060
+ * absent when unknown or when the camera itself triggered.
24061
+ *
24062
+ * Carried on the session attach ONLY so the runner's `DetectionPhaseTransition`
24063
+ * can say what started the session (D672 rule 11, amended 2026-10-06). The
24064
+ * runner never decides anything on it.
24065
+ */
24066
+ var SessionTriggerSchema = object({
24067
+ source: MotionSourceEnum,
24068
+ triggeredBy: MotionTriggeredBySchema,
24069
+ sensorName: string().optional()
24070
+ });
24071
+ /**
24057
24072
  * List of motion sources active on a camera. Empty array is valid:
24058
24073
  * "no source" — happens for battery cams without firmware motion when
24059
24074
  * the operator hasn't opted into the SW analyzer (would hold the
@@ -24305,6 +24320,14 @@ var RunnerCameraConfigSchema = object({
24305
24320
  */
24306
24321
  motionSources: MotionSourcesSchema.default(["analyzer"]),
24307
24322
  /**
24323
+ * What opened this attach, on a SESSION attach only (see
24324
+ * {@link SessionTriggerSchema}). OPTIONAL with no default: absent on a
24325
+ * standing attach, an occupancy burst, a re-dispatch with no motion event
24326
+ * behind it, and every attach from an orchestrator older than the field — the
24327
+ * runner then names no source on the phase event.
24328
+ */
24329
+ sessionTrigger: SessionTriggerSchema.optional(),
24330
+ /**
24308
24331
  * WHICH root detector runs for this camera. The detection counterpart of
24309
24332
  * {@link motionSources}, same shape and same discipline: a per-camera list
24310
24333
  * of sources the system understands, vendor-agnostic, owned here.
@@ -30528,7 +30551,7 @@ var LocalInterfaceSchema = object({
30528
30551
  name: string(),
30529
30552
  family: _enum(["IPv4", "IPv6"]),
30530
30553
  address: string(),
30531
- /** CIDR shorthand (e.g. `192.168.x.x/24`). Empty when OS doesn't expose. */
30554
+ /** CIDR shorthand (e.g. `192.168.10.10/24`). Empty when OS doesn't expose. */
30532
30555
  cidr: string(),
30533
30556
  /** OS netmask. Stable across calls so the SDK can hash on it. */
30534
30557
  netmask: string(),
@@ -30582,7 +30605,8 @@ var ConnectionEndpointSchema = object({
30582
30605
  "lan-ipv4",
30583
30606
  "lan-ipv6",
30584
30607
  "public",
30585
- "loopback"
30608
+ "loopback",
30609
+ "app-link"
30586
30610
  ]),
30587
30611
  /**
30588
30612
  * Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
@@ -30651,15 +30675,27 @@ var GetConnectionEndpointsResultSchema = object({
30651
30675
  localPort: AdvertisedLocalPortSchema
30652
30676
  });
30653
30677
  /**
30654
- * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
30655
- * AUTO (resolved from the candidate ranking at send time); `resolved` reports
30656
- * what AUTO currently picks, so the UI can show the effective value either way.
30678
+ * The address flagged for notification links. `baseUrl: null` = AUTO, which
30679
+ * since D709 is the CamStack app link; `resolved` reports the effective value,
30680
+ * so the UI can show it either way.
30657
30681
  */
30658
30682
  var NotificationEndpointSchema = object({
30659
30683
  /** The operator's explicit choice, or null for AUTO. */
30660
30684
  baseUrl: string().nullable(),
30661
- /** What the ranking currently resolves to (null when nothing is reachable). */
30662
- resolved: string().nullable()
30685
+ /**
30686
+ * The effective flagged address: the explicit choice, else (AUTO) the
30687
+ * CamStack app link `https://camstack.zentik.app` (D709). A pre-D709 hub
30688
+ * answered the public tunnel / mesh here, or null.
30689
+ */
30690
+ resolved: string().nullable(),
30691
+ /**
30692
+ * The hub origin an app link carries as `hub=` (D709): the hub's best
30693
+ * public HTTPS address — the tunnel, then the mesh (Tailscale MagicDNS) —
30694
+ * picked from this cap's own palette by `pickAppLinkHub`. `null` when the
30695
+ * hub advertises neither; the app link is then minted without `hub=`.
30696
+ * Absent on a pre-D709 hub.
30697
+ */
30698
+ appLinkHub: string().nullable()
30663
30699
  });
30664
30700
  /**
30665
30701
  * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
@@ -30713,7 +30749,11 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
30713
30749
  * Pass `'https'` when the caller is itself loaded over HTTPS
30714
30750
  * to avoid mixed-content blocks in the browser. The public
30715
30751
  * tunnel always emits `https://` regardless. */
30716
- scheme: _enum(["http", "https"]).optional()
30752
+ scheme: _enum(["http", "https"]).optional(),
30753
+ /** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
30754
+ * an address notification links can be flagged with, not a hub
30755
+ * route. Default `false`: racers and rankers never see it. */
30756
+ includeAppLink: boolean().optional()
30717
30757
  }), 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, {
30718
30758
  kind: "mutation",
30719
30759
  auth: "admin"
@@ -30974,7 +31014,7 @@ var MeshPeerSchema = object({
30974
31014
  tags: array(string()).readonly(),
30975
31015
  /** True when the peer advertises itself as an exit-node candidate. */
30976
31016
  exitNodeOption: boolean(),
30977
- /** Subnet routes the peer advertises (e.g. `["192.168.x.x/24"]`). */
31017
+ /** Subnet routes the peer advertises (e.g. `["192.168.10.0/24"]`). */
30978
31018
  advertisedRoutes: array(string()).readonly(),
30979
31019
  /** Owner account login (e.g. `foo@example.com`). Null for tagged peers. */
30980
31020
  userLogin: string().nullable(),
@@ -31918,7 +31958,13 @@ var OsdStatusSchema = object({ overlays: array(object({
31918
31958
  */
31919
31959
  availablePositions: array(OsdPositionEnum).readonly(),
31920
31960
  /** Firmware does not allow editing the `text` field (e.g. timestamp). */
31921
- readOnly: boolean().optional()
31961
+ readOnly: boolean().optional(),
31962
+ /**
31963
+ * Firmware text ceiling for this overlay. Absent = no known limit.
31964
+ * The OSD manager truncates here on every write — never stores its own
31965
+ * copy on the binding.
31966
+ */
31967
+ maxCharacters: number().int().positive().optional()
31922
31968
  })).readonly() });
31923
31969
  var OsdOverlayPatchSchema = object({
31924
31970
  enabled: boolean().optional(),
@@ -31969,7 +32015,9 @@ var OsdTemplateBindingSchema = object({
31969
32015
  valuePath: string().min(1).max(64),
31970
32016
  /** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
31971
32017
  displayUnit: string().max(16).optional(),
31972
- maxDecimals: number().int().min(0).max(4).default(1)
32018
+ maxDecimals: number().int().min(0).max(4).default(1),
32019
+ /** Raw value → display text (same shape as slot binding). Empty string hides. */
32020
+ valueMap: record(string(), string()).optional()
31973
32021
  });
31974
32022
  /**
31975
32023
  * A named template: bindings (alias → leaf field) + a parser that
@@ -32019,18 +32067,11 @@ var OsdSlotBindingSchema = object({
32019
32067
  source: OsdSourceSchema,
32020
32068
  /** `${value}` and `${unit}` are substituted; every occurrence. */
32021
32069
  template: string().max(96).default("${value}"),
32022
- /** Truncate with an ellipsis past this length. Absent = no limit. */
32023
- maxCharacters: number().int().min(4).max(64).optional(),
32024
32070
  /**
32025
32071
  * Decimal places for a numeric value. `0` yields an integer — the
32026
32072
  * documented workaround for firmwares that reject `.` in overlay text.
32027
32073
  */
32028
32074
  maxDecimals: number().int().min(0).max(4).default(1),
32029
- /**
32030
- * Appended via `${unit}` when no unit conversion produces a label.
32031
- * Prefer `source.displayUnit` for converted device-state values.
32032
- */
32033
- unitLabel: string().max(8).optional(),
32034
32075
  /** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
32035
32076
  valueMap: record(string(), string()).optional(),
32036
32077
  /** Time windows in which the slot is shown. Absent = always. */
@@ -32039,9 +32080,7 @@ var OsdSlotBindingSchema = object({
32039
32080
  * Display gate, in the notification centre's condition vocabulary.
32040
32081
  * Only the keys reported by `getConditionSupport` are accepted.
32041
32082
  */
32042
- conditions: NcConditionsSchema.optional(),
32043
- /** Rendered when the gate is closed or the value unreadable. Empty = hide. */
32044
- fallbackText: string().max(64).default("")
32083
+ conditions: NcConditionsSchema.optional()
32045
32084
  });
32046
32085
  /** One camera slot, as the operator sees it: firmware truth + our binding. */
32047
32086
  var OsdSlotViewSchema = object({
@@ -47925,12 +47964,14 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
47925
47964
  {
47926
47965
  id: "addons",
47927
47966
  family: "system",
47928
- label: "Addons",
47929
- kinds: [
47930
- "addon-crash-loop",
47931
- "addon-update-available",
47932
- "addon-updated"
47933
- ]
47967
+ label: "Addons stopped",
47968
+ kinds: ["addon-crash-loop"]
47969
+ },
47970
+ {
47971
+ id: "addon-updates",
47972
+ family: "system",
47973
+ label: "Addon updates",
47974
+ kinds: ["addon-update-available", "addon-updated"]
47934
47975
  },
47935
47976
  {
47936
47977
  id: "updates",
@@ -48090,13 +48131,15 @@ var STATE_SUMMARY_SECTION_VARS = [
48090
48131
  },
48091
48132
  {
48092
48133
  name: "summaryAddons",
48093
- label: "Summary: addons",
48094
- example: "@camstack/addon-recorder 1.2.3 → 1.2.4",
48095
- kinds: [
48096
- "addon-crash-loop",
48097
- "addon-update-available",
48098
- "addon-updated"
48099
- ]
48134
+ label: "Summary: addons stopped",
48135
+ example: "hub — recorder stopped after repeated crashes",
48136
+ kinds: ["addon-crash-loop"]
48137
+ },
48138
+ {
48139
+ name: "summaryAddonUpdates",
48140
+ label: "Summary: addon updates",
48141
+ example: "hub:\n• @camstack/addon-recorder 1.2.3 → 1.2.4",
48142
+ kinds: ["addon-update-available", "addon-updated"]
48100
48143
  },
48101
48144
  {
48102
48145
  name: "summaryUpdates",
@@ -48180,6 +48223,7 @@ var STATE_SUMMARY_VARS = [
48180
48223
  [...RULE_BODY_ONLY],
48181
48224
  [...RULE_TEXT],
48182
48225
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
48226
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
48183
48227
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
48184
48228
  ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
48185
48229
  ([...RULE_TEXT], [...CONSUMABLE]),
@@ -48220,6 +48264,8 @@ var STATE_SUMMARY_VARS = [
48220
48264
  [...RULE_TEXT],
48221
48265
  [...RULE_TEXT],
48222
48266
  [...RULE_TEXT],
48267
+ [...RULE_TEXT],
48268
+ [...RULE_TEXT],
48223
48269
  ...STATE_SUMMARY_VARS
48224
48270
  ];
48225
48271
  new Set([
@@ -50596,12 +50642,12 @@ var BUCKETS_PATH = "/api/v1/buckets";
50596
50642
  /** Zentik `deliveryType` enum — mirrors our declared level ids 1:1. */
50597
50643
  var DEFAULT_DELIVERY_TYPE = "NORMAL";
50598
50644
  /**
50599
- * Backend `CreateMessageDto` limits (verbatim from
50600
- * `zentik-notifier/backend/.../create-message.dto.ts`): `title` is REQUIRED and
50601
- * `@MaxLength(100)`, `body` is optional `@MaxLength(500)`. Over-length → 400.
50645
+ * Backend `CreateMessageDto` title is REQUIRED and `@MaxLength(100)`.
50646
+ * Body has no CamStack truncate — model "no cap" like webhook (positive int).
50602
50647
  */
50603
50648
  var TITLE_MAX_LEN = 100;
50604
- var BODY_MAX_LEN = 500;
50649
+ /** Positive int required by caps; large enough that degrade never truncates. */
50650
+ var NO_BODY_CAP = 1e6;
50605
50651
  /** Non-empty fallback title when a notification carries no title. */
50606
50652
  var FALLBACK_TITLE = "CamStack";
50607
50653
  /** url-mode attachment ceiling (no hard doc limit; matrix §5 → 5). */
@@ -50954,7 +51000,7 @@ var NOTIFIER_ADAPTERS = [
50954
51000
  actionIcons: true,
50955
51001
  sound: true,
50956
51002
  ttl: true,
50957
- bodyMaxLen: BODY_MAX_LEN
51003
+ bodyMaxLen: NO_BODY_CAP
50958
51004
  }
50959
51005
  },
50960
51006
  destinationKeys: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-notifiers",
3
- "version": "1.2.126",
3
+ "version": "1.2.128",
4
4
  "description": "System notifiers addon for CamStack — a `notification-output` collection provider hosting per-kind notifier adapters (ntfy, pushover, gotify, telegram, discord, webhook, zentik).",
5
5
  "keywords": [
6
6
  "camstack",