@camstack/addon-tailscale 1.2.116 → 1.2.118

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.
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  enumerable: true
25
25
  }) : target, mod));
26
26
  //#endregion
27
- const require_dist = require("../dist-jCCe7qVD.js");
27
+ const require_dist = require("../dist-B5khvoZj.js");
28
28
  let node_child_process = require("node:child_process");
29
29
  let node_util = require("node:util");
30
30
  let node_fs = require("node:fs");
@@ -1294,15 +1294,15 @@ var TailscaleClientAddon = class extends require_dist.BaseAddon {
1294
1294
  type: "boolean",
1295
1295
  key: "acceptRoutes",
1296
1296
  label: "Accept routes from peers",
1297
- description: "Install subnet routes other peers advertise (e.g. a peer router exposing `192.168.x.x/24`).",
1297
+ description: "Install subnet routes other peers advertise (e.g. a peer router exposing `192.168.60.0/24`).",
1298
1298
  default: false
1299
1299
  }),
1300
1300
  this.field({
1301
1301
  type: "text",
1302
1302
  key: "advertisedRoutes",
1303
1303
  label: "Advertise subnet routes",
1304
- description: "Comma-separated CIDRs this host exposes to peers (e.g. `192.168.x.x/24,10.x.x.x/8`). Requires approval in the Tailscale admin console.",
1305
- placeholder: "192.168.x.x/24"
1304
+ description: "Comma-separated CIDRs this host exposes to peers (e.g. `192.168.10.0/24,10.0.0.0/8`). Requires approval in the Tailscale admin console.",
1305
+ placeholder: "192.168.10.0/24"
1306
1306
  }),
1307
1307
  this.field({
1308
1308
  type: "text",
@@ -1,4 +1,4 @@
1
- import { a as EventCategory, i as BaseAddon, n as networkAccessCapability, r as BOOT_RECOVERY_BACKOFF_MS, t as meshNetworkCapability } from "../dist-DOnpd-Z8.mjs";
1
+ import { a as EventCategory, i as BaseAddon, n as networkAccessCapability, r as BOOT_RECOVERY_BACKOFF_MS, t as meshNetworkCapability } from "../dist-CnsQ7vyF.mjs";
2
2
  import { execFile, spawn } from "node:child_process";
3
3
  import { promisify } from "node:util";
4
4
  import * as fs from "node:fs";
@@ -1266,15 +1266,15 @@ var TailscaleClientAddon = class extends BaseAddon {
1266
1266
  type: "boolean",
1267
1267
  key: "acceptRoutes",
1268
1268
  label: "Accept routes from peers",
1269
- description: "Install subnet routes other peers advertise (e.g. a peer router exposing `192.168.x.x/24`).",
1269
+ description: "Install subnet routes other peers advertise (e.g. a peer router exposing `192.168.60.0/24`).",
1270
1270
  default: false
1271
1271
  }),
1272
1272
  this.field({
1273
1273
  type: "text",
1274
1274
  key: "advertisedRoutes",
1275
1275
  label: "Advertise subnet routes",
1276
- description: "Comma-separated CIDRs this host exposes to peers (e.g. `192.168.x.x/24,10.x.x.x/8`). Requires approval in the Tailscale admin console.",
1277
- placeholder: "192.168.x.x/24"
1276
+ description: "Comma-separated CIDRs this host exposes to peers (e.g. `192.168.10.0/24,10.0.0.0/8`). Requires approval in the Tailscale admin console.",
1277
+ placeholder: "192.168.10.0/24"
1278
1278
  }),
1279
1279
  this.field({
1280
1280
  type: "text",
@@ -10554,11 +10554,11 @@ var ModelVariantGroupSchema = object({
10554
10554
  /** Quantization axis. Omit ⇒ the fp32 base build. */
10555
10555
  precision: _enum(["fp32", "int8"]).optional(),
10556
10556
  /**
10557
- * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
10558
- * latency-optimized export (e.g. ReLU-activation variant) — the slot the
10559
- * future performance variants plug into.
10557
+ * Training / latency-variant axis. Omit ⇒ the standard build. `test` marks
10558
+ * an alternate training of the same family (Scrypted `relu_test`); chip
10559
+ * label is "Test".
10560
10560
  */
10561
- optimization: _enum(["standard", "fast"]).optional(),
10561
+ optimization: _enum(["standard", "test"]).optional(),
10562
10562
  /**
10563
10563
  * Input-resolution axis (square input side, px). Omit ⇒ the family's native
10564
10564
  * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
@@ -10581,9 +10581,10 @@ var ModelProviderIdSchema = _enum([
10581
10581
  * are per upstream. A persisted custom-model row written when this field was a
10582
10582
  * string still parses — the string is lifted into `{ weights }`.
10583
10583
  *
10584
- * Every licence surface (the admin "Models and licenses" page,
10585
- * `THIRD_PARTY_MODELS.md`, the Hugging Face model table) is GENERATED from
10586
- * these records by `scripts/gen-model-licenses.ts` (D661).
10584
+ * Every machine licence surface (`THIRD_PARTY_MODELS.md`, the Hugging Face
10585
+ * model table) is GENERATED from these records by
10586
+ * `scripts/gen-model-licenses.ts` (D661). Operator-facing docs live in
10587
+ * `camstack/docs` → `/docs/detection/models`.
10587
10588
  */
10588
10589
  var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { weights: value } : value, object({
10589
10590
  /** SPDX id of the WEIGHTS' terms, a `LicenseRef-*` for non-SPDX terms, or `UNKNOWN`. */
@@ -14112,7 +14113,7 @@ DeviceType.Camera;
14112
14113
  * This matters here more than anywhere else in the repo, because every provider
14113
14114
  * that knows a device's host knows its PASSWORD too: `{ host, port, username,
14114
14115
  * password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
14115
- * and `http://admin:hunter2@192.168.x.x/` is a URL a browser accepts. The
14116
+ * and `http://admin:hunter2@192.168.60.139/` is a URL a browser accepts. The
14116
14117
  * camera's own page will ask for its own login. That is correct, and pre-
14117
14118
  * filling it is the operator's business, not ours.
14118
14119
  *
@@ -17096,7 +17097,7 @@ method(object({
17096
17097
  timeoutMs: 6e5
17097
17098
  });
17098
17099
  method(object({
17099
- nodeId: string(),
17100
+ targetNodeId: string(),
17100
17101
  modelId: string(),
17101
17102
  format: _enum(MODEL_FORMATS),
17102
17103
  entry: ModelCatalogEntrySchema
@@ -19579,8 +19580,9 @@ var NcRuleSchema = NcRuleInputSchema.extend({
19579
19580
  disabledTargetIds: array(string()).default([])
19580
19581
  });
19581
19582
  /**
19582
- * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
19583
- * still-true state. `backups-exports` and `alarm` are not sections — a completed
19583
+ * The state-summary SECTIONS (D647 / D698 / D708): the D637 kind groups that
19584
+ * report a still-true state. `addons` is the crash-looped addons only and
19585
+ * `addon-updates` the addons that are behind (D708 split them). `backups-exports` and `alarm` are not sections — a completed
19584
19586
  * job is not an open state, and the alarm stays instant.
19585
19587
  */
19586
19588
  var NcStateSummarySectionIdSchema = _enum([
@@ -19592,6 +19594,7 @@ var NcStateSummarySectionIdSchema = _enum([
19592
19594
  "detection-blind",
19593
19595
  "nodes",
19594
19596
  "addons",
19597
+ "addon-updates",
19595
19598
  "updates"
19596
19599
  ]);
19597
19600
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
@@ -20072,7 +20075,19 @@ var NcAlarmModeDelaysListSchema = array(object({
20072
20075
  * When this mode may be armed. Absent / null = always allowed (same as a
20073
20076
  * rule with no `schedule`). Evaluated with {@link isScheduleActive}.
20074
20077
  */
20075
- schedule: NcScheduleSchema.optional()
20078
+ schedule: NcScheduleSchema.optional(),
20079
+ /**
20080
+ * D706: while this mode's ENTRY DELAY runs, do the alarm rules' own pushes go out (as an
20081
+ * intrusion naming the locations)? Absent = false — the entry-delay notification is the
20082
+ * only one, and the matches ride the triggered notification's mosaic (D705).
20083
+ */
20084
+ notifyDuringEntryDelay: boolean().optional(),
20085
+ /**
20086
+ * D706: notify "watched again" when a sensor this arm bypassed closes and opens again.
20087
+ * Absent = false (logged, not sent). Even when true it is not sent when that same sensor
20088
+ * is the one that starts the entry delay — the entry-delay notification already names it.
20089
+ */
20090
+ notifySensorRewatched: boolean().optional()
20076
20091
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
20077
20092
  var NcAlarmModeDelaysEditListSchema = array(object({
20078
20093
  mode: AlarmArmModeSchema,
@@ -20081,7 +20096,11 @@ var NcAlarmModeDelaysEditListSchema = array(object({
20081
20096
  triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional(),
20082
20097
  postTriggeredAction: NcAlarmPostTriggeredActionSchema.nullable().optional(),
20083
20098
  /** Set a schedule, or `null` to clear (mode always allowed again). */
20084
- schedule: NcScheduleSchema.nullable().optional()
20099
+ schedule: NcScheduleSchema.nullable().optional(),
20100
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
20101
+ notifyDuringEntryDelay: boolean().nullable().optional(),
20102
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
20103
+ notifySensorRewatched: boolean().nullable().optional()
20085
20104
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
20086
20105
  /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
20087
20106
  var NcAlarmNonBlockingEditSchema = object({
@@ -21042,39 +21061,19 @@ Object.freeze(Object.fromEntries([{
21042
21061
  label: "Face recognition model",
21043
21062
  defaultModelId: "auraface-r100",
21044
21063
  indexName: "the enrolled face gallery",
21045
- options: [
21046
- {
21047
- id: "auraface-r100",
21048
- label: "AuraFace R100"
21049
- },
21050
- {
21051
- id: "arcface-r100",
21052
- label: "ArcFace (ResNet34)"
21053
- },
21054
- {
21055
- id: "inception-resnet-v1",
21056
- label: "Inception ResNet V1"
21057
- }
21058
- ]
21064
+ options: [{
21065
+ id: "auraface-r100",
21066
+ label: "AuraFace R100"
21067
+ }]
21059
21068
  }, {
21060
21069
  stepId: "clip-embedding",
21061
21070
  label: "Semantic search model",
21062
- defaultModelId: "mobileclip-s1",
21071
+ defaultModelId: "siglip2-b16-224",
21063
21072
  indexName: "the object semantic-search index",
21064
- options: [
21065
- {
21066
- id: "mobileclip-s1",
21067
- label: "MobileCLIP S1"
21068
- },
21069
- {
21070
- id: "mobileclip-s2",
21071
- label: "MobileCLIP S2"
21072
- },
21073
- {
21074
- id: "siglip2-b16-224",
21075
- label: "SigLIP2 B/16 (Apache-2.0)"
21076
- }
21077
- ]
21073
+ options: [{
21074
+ id: "siglip2-b16-224",
21075
+ label: "SigLIP2 B/16 (Apache-2.0)"
21076
+ }]
21078
21077
  }].map((s) => [s.stepId, s.defaultModelId])));
21079
21078
  var CLUSTER_STEP_SETTING_FIELDS = [{
21080
21079
  stepId: "face-embedding",
@@ -23922,6 +23921,22 @@ var MotionSourceEnum = _enum([
23922
23921
  "device-activity"
23923
23922
  ]);
23924
23923
  /**
23924
+ * What opened an on-motion detection SESSION: the motion source that passed the
23925
+ * camera's `motionSources` gate, and what raised it (the camera itself, or a
23926
+ * sensor the operator attached — D672). `sensorName` is the attached sensor's
23927
+ * device name as the orchestrator's camera-sensors resolution last read it;
23928
+ * absent when unknown or when the camera itself triggered.
23929
+ *
23930
+ * Carried on the session attach ONLY so the runner's `DetectionPhaseTransition`
23931
+ * can say what started the session (D672 rule 11, amended 2026-10-06). The
23932
+ * runner never decides anything on it.
23933
+ */
23934
+ var SessionTriggerSchema = object({
23935
+ source: MotionSourceEnum,
23936
+ triggeredBy: MotionTriggeredBySchema,
23937
+ sensorName: string().optional()
23938
+ });
23939
+ /**
23925
23940
  * List of motion sources active on a camera. Empty array is valid:
23926
23941
  * "no source" — happens for battery cams without firmware motion when
23927
23942
  * the operator hasn't opted into the SW analyzer (would hold the
@@ -24173,6 +24188,14 @@ var RunnerCameraConfigSchema = object({
24173
24188
  */
24174
24189
  motionSources: MotionSourcesSchema.default(["analyzer"]),
24175
24190
  /**
24191
+ * What opened this attach, on a SESSION attach only (see
24192
+ * {@link SessionTriggerSchema}). OPTIONAL with no default: absent on a
24193
+ * standing attach, an occupancy burst, a re-dispatch with no motion event
24194
+ * behind it, and every attach from an orchestrator older than the field — the
24195
+ * runner then names no source on the phase event.
24196
+ */
24197
+ sessionTrigger: SessionTriggerSchema.optional(),
24198
+ /**
24176
24199
  * WHICH root detector runs for this camera. The detection counterpart of
24177
24200
  * {@link motionSources}, same shape and same discipline: a per-camera list
24178
24201
  * of sources the system understands, vendor-agnostic, owned here.
@@ -30396,7 +30419,7 @@ var LocalInterfaceSchema = object({
30396
30419
  name: string(),
30397
30420
  family: _enum(["IPv4", "IPv6"]),
30398
30421
  address: string(),
30399
- /** CIDR shorthand (e.g. `192.168.x.x/24`). Empty when OS doesn't expose. */
30422
+ /** CIDR shorthand (e.g. `192.168.10.10/24`). Empty when OS doesn't expose. */
30400
30423
  cidr: string(),
30401
30424
  /** OS netmask. Stable across calls so the SDK can hash on it. */
30402
30425
  netmask: string(),
@@ -30450,7 +30473,8 @@ var ConnectionEndpointSchema = object({
30450
30473
  "lan-ipv4",
30451
30474
  "lan-ipv6",
30452
30475
  "public",
30453
- "loopback"
30476
+ "loopback",
30477
+ "app-link"
30454
30478
  ]),
30455
30479
  /**
30456
30480
  * Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
@@ -30519,15 +30543,27 @@ var GetConnectionEndpointsResultSchema = object({
30519
30543
  localPort: AdvertisedLocalPortSchema
30520
30544
  });
30521
30545
  /**
30522
- * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
30523
- * AUTO (resolved from the candidate ranking at send time); `resolved` reports
30524
- * what AUTO currently picks, so the UI can show the effective value either way.
30546
+ * The address flagged for notification links. `baseUrl: null` = AUTO, which
30547
+ * since D709 is the CamStack app link; `resolved` reports the effective value,
30548
+ * so the UI can show it either way.
30525
30549
  */
30526
30550
  var NotificationEndpointSchema = object({
30527
30551
  /** The operator's explicit choice, or null for AUTO. */
30528
30552
  baseUrl: string().nullable(),
30529
- /** What the ranking currently resolves to (null when nothing is reachable). */
30530
- resolved: string().nullable()
30553
+ /**
30554
+ * The effective flagged address: the explicit choice, else (AUTO) the
30555
+ * CamStack app link `https://camstack.zentik.app` (D709). A pre-D709 hub
30556
+ * answered the public tunnel / mesh here, or null.
30557
+ */
30558
+ resolved: string().nullable(),
30559
+ /**
30560
+ * The hub origin an app link carries as `hub=` (D709): the hub's best
30561
+ * public HTTPS address — the tunnel, then the mesh (Tailscale MagicDNS) —
30562
+ * picked from this cap's own palette by `pickAppLinkHub`. `null` when the
30563
+ * hub advertises neither; the app link is then minted without `hub=`.
30564
+ * Absent on a pre-D709 hub.
30565
+ */
30566
+ appLinkHub: string().nullable()
30531
30567
  });
30532
30568
  /**
30533
30569
  * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
@@ -30581,7 +30617,11 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
30581
30617
  * Pass `'https'` when the caller is itself loaded over HTTPS
30582
30618
  * to avoid mixed-content blocks in the browser. The public
30583
30619
  * tunnel always emits `https://` regardless. */
30584
- scheme: _enum(["http", "https"]).optional()
30620
+ scheme: _enum(["http", "https"]).optional(),
30621
+ /** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
30622
+ * an address notification links can be flagged with, not a hub
30623
+ * route. Default `false`: racers and rankers never see it. */
30624
+ includeAppLink: boolean().optional()
30585
30625
  }), 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, {
30586
30626
  kind: "mutation",
30587
30627
  auth: "admin"
@@ -30842,7 +30882,7 @@ var MeshPeerSchema = object({
30842
30882
  tags: array(string()).readonly(),
30843
30883
  /** True when the peer advertises itself as an exit-node candidate. */
30844
30884
  exitNodeOption: boolean(),
30845
- /** Subnet routes the peer advertises (e.g. `["192.168.x.x/24"]`). */
30885
+ /** Subnet routes the peer advertises (e.g. `["192.168.10.0/24"]`). */
30846
30886
  advertisedRoutes: array(string()).readonly(),
30847
30887
  /** Owner account login (e.g. `foo@example.com`). Null for tagged peers. */
30848
30888
  userLogin: string().nullable(),
@@ -31847,7 +31887,13 @@ var OsdStatusSchema = object({ overlays: array(object({
31847
31887
  */
31848
31888
  availablePositions: array(OsdPositionEnum).readonly(),
31849
31889
  /** Firmware does not allow editing the `text` field (e.g. timestamp). */
31850
- readOnly: boolean().optional()
31890
+ readOnly: boolean().optional(),
31891
+ /**
31892
+ * Firmware text ceiling for this overlay. Absent = no known limit.
31893
+ * The OSD manager truncates here on every write — never stores its own
31894
+ * copy on the binding.
31895
+ */
31896
+ maxCharacters: number().int().positive().optional()
31851
31897
  })).readonly() });
31852
31898
  var OsdOverlayPatchSchema = object({
31853
31899
  enabled: boolean().optional(),
@@ -31898,7 +31944,9 @@ var OsdTemplateBindingSchema = object({
31898
31944
  valuePath: string().min(1).max(64),
31899
31945
  /** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
31900
31946
  displayUnit: string().max(16).optional(),
31901
- maxDecimals: number().int().min(0).max(4).default(1)
31947
+ maxDecimals: number().int().min(0).max(4).default(1),
31948
+ /** Raw value → display text (same shape as slot binding). Empty string hides. */
31949
+ valueMap: record(string(), string()).optional()
31902
31950
  });
31903
31951
  /**
31904
31952
  * A named template: bindings (alias → leaf field) + a parser that
@@ -31948,18 +31996,11 @@ var OsdSlotBindingSchema = object({
31948
31996
  source: OsdSourceSchema,
31949
31997
  /** `${value}` and `${unit}` are substituted; every occurrence. */
31950
31998
  template: string().max(96).default("${value}"),
31951
- /** Truncate with an ellipsis past this length. Absent = no limit. */
31952
- maxCharacters: number().int().min(4).max(64).optional(),
31953
31999
  /**
31954
32000
  * Decimal places for a numeric value. `0` yields an integer — the
31955
32001
  * documented workaround for firmwares that reject `.` in overlay text.
31956
32002
  */
31957
32003
  maxDecimals: number().int().min(0).max(4).default(1),
31958
- /**
31959
- * Appended via `${unit}` when no unit conversion produces a label.
31960
- * Prefer `source.displayUnit` for converted device-state values.
31961
- */
31962
- unitLabel: string().max(8).optional(),
31963
32004
  /** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
31964
32005
  valueMap: record(string(), string()).optional(),
31965
32006
  /** Time windows in which the slot is shown. Absent = always. */
@@ -31968,9 +32009,7 @@ var OsdSlotBindingSchema = object({
31968
32009
  * Display gate, in the notification centre's condition vocabulary.
31969
32010
  * Only the keys reported by `getConditionSupport` are accepted.
31970
32011
  */
31971
- conditions: NcConditionsSchema.optional(),
31972
- /** Rendered when the gate is closed or the value unreadable. Empty = hide. */
31973
- fallbackText: string().max(64).default("")
32012
+ conditions: NcConditionsSchema.optional()
31974
32013
  });
31975
32014
  /** One camera slot, as the operator sees it: firmware truth + our binding. */
31976
32015
  var OsdSlotViewSchema = object({
@@ -47647,12 +47686,14 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
47647
47686
  {
47648
47687
  id: "addons",
47649
47688
  family: "system",
47650
- label: "Addons",
47651
- kinds: [
47652
- "addon-crash-loop",
47653
- "addon-update-available",
47654
- "addon-updated"
47655
- ]
47689
+ label: "Addons stopped",
47690
+ kinds: ["addon-crash-loop"]
47691
+ },
47692
+ {
47693
+ id: "addon-updates",
47694
+ family: "system",
47695
+ label: "Addon updates",
47696
+ kinds: ["addon-update-available", "addon-updated"]
47656
47697
  },
47657
47698
  {
47658
47699
  id: "updates",
@@ -47812,13 +47853,15 @@ var STATE_SUMMARY_SECTION_VARS = [
47812
47853
  },
47813
47854
  {
47814
47855
  name: "summaryAddons",
47815
- label: "Summary: addons",
47816
- example: "@camstack/addon-recorder 1.2.3 → 1.2.4",
47817
- kinds: [
47818
- "addon-crash-loop",
47819
- "addon-update-available",
47820
- "addon-updated"
47821
- ]
47856
+ label: "Summary: addons stopped",
47857
+ example: "hub — recorder stopped after repeated crashes",
47858
+ kinds: ["addon-crash-loop"]
47859
+ },
47860
+ {
47861
+ name: "summaryAddonUpdates",
47862
+ label: "Summary: addon updates",
47863
+ example: "hub:\n• @camstack/addon-recorder 1.2.3 → 1.2.4",
47864
+ kinds: ["addon-update-available", "addon-updated"]
47822
47865
  },
47823
47866
  {
47824
47867
  name: "summaryUpdates",
@@ -47902,6 +47945,7 @@ var STATE_SUMMARY_VARS = [
47902
47945
  [...RULE_BODY_ONLY],
47903
47946
  [...RULE_TEXT],
47904
47947
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
47948
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
47905
47949
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
47906
47950
  ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
47907
47951
  ([...RULE_TEXT], [...CONSUMABLE]),
@@ -47942,6 +47986,8 @@ var STATE_SUMMARY_VARS = [
47942
47986
  [...RULE_TEXT],
47943
47987
  [...RULE_TEXT],
47944
47988
  [...RULE_TEXT],
47989
+ [...RULE_TEXT],
47990
+ [...RULE_TEXT],
47945
47991
  ...STATE_SUMMARY_VARS
47946
47992
  ];
47947
47993
  new Set([
@@ -10553,11 +10553,11 @@ var ModelVariantGroupSchema = object({
10553
10553
  /** Quantization axis. Omit ⇒ the fp32 base build. */
10554
10554
  precision: _enum(["fp32", "int8"]).optional(),
10555
10555
  /**
10556
- * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
10557
- * latency-optimized export (e.g. ReLU-activation variant) — the slot the
10558
- * future performance variants plug into.
10556
+ * Training / latency-variant axis. Omit ⇒ the standard build. `test` marks
10557
+ * an alternate training of the same family (Scrypted `relu_test`); chip
10558
+ * label is "Test".
10559
10559
  */
10560
- optimization: _enum(["standard", "fast"]).optional(),
10560
+ optimization: _enum(["standard", "test"]).optional(),
10561
10561
  /**
10562
10562
  * Input-resolution axis (square input side, px). Omit ⇒ the family's native
10563
10563
  * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
@@ -10580,9 +10580,10 @@ var ModelProviderIdSchema = _enum([
10580
10580
  * are per upstream. A persisted custom-model row written when this field was a
10581
10581
  * string still parses — the string is lifted into `{ weights }`.
10582
10582
  *
10583
- * Every licence surface (the admin "Models and licenses" page,
10584
- * `THIRD_PARTY_MODELS.md`, the Hugging Face model table) is GENERATED from
10585
- * these records by `scripts/gen-model-licenses.ts` (D661).
10583
+ * Every machine licence surface (`THIRD_PARTY_MODELS.md`, the Hugging Face
10584
+ * model table) is GENERATED from these records by
10585
+ * `scripts/gen-model-licenses.ts` (D661). Operator-facing docs live in
10586
+ * `camstack/docs` → `/docs/detection/models`.
10586
10587
  */
10587
10588
  var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { weights: value } : value, object({
10588
10589
  /** SPDX id of the WEIGHTS' terms, a `LicenseRef-*` for non-SPDX terms, or `UNKNOWN`. */
@@ -14111,7 +14112,7 @@ DeviceType.Camera;
14111
14112
  * This matters here more than anywhere else in the repo, because every provider
14112
14113
  * that knows a device's host knows its PASSWORD too: `{ host, port, username,
14113
14114
  * password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
14114
- * and `http://admin:hunter2@192.168.x.x/` is a URL a browser accepts. The
14115
+ * and `http://admin:hunter2@192.168.60.139/` is a URL a browser accepts. The
14115
14116
  * camera's own page will ask for its own login. That is correct, and pre-
14116
14117
  * filling it is the operator's business, not ours.
14117
14118
  *
@@ -17095,7 +17096,7 @@ method(object({
17095
17096
  timeoutMs: 6e5
17096
17097
  });
17097
17098
  method(object({
17098
- nodeId: string(),
17099
+ targetNodeId: string(),
17099
17100
  modelId: string(),
17100
17101
  format: _enum(MODEL_FORMATS),
17101
17102
  entry: ModelCatalogEntrySchema
@@ -19578,8 +19579,9 @@ var NcRuleSchema = NcRuleInputSchema.extend({
19578
19579
  disabledTargetIds: array(string()).default([])
19579
19580
  });
19580
19581
  /**
19581
- * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
19582
- * still-true state. `backups-exports` and `alarm` are not sections — a completed
19582
+ * The state-summary SECTIONS (D647 / D698 / D708): the D637 kind groups that
19583
+ * report a still-true state. `addons` is the crash-looped addons only and
19584
+ * `addon-updates` the addons that are behind (D708 split them). `backups-exports` and `alarm` are not sections — a completed
19583
19585
  * job is not an open state, and the alarm stays instant.
19584
19586
  */
19585
19587
  var NcStateSummarySectionIdSchema = _enum([
@@ -19591,6 +19593,7 @@ var NcStateSummarySectionIdSchema = _enum([
19591
19593
  "detection-blind",
19592
19594
  "nodes",
19593
19595
  "addons",
19596
+ "addon-updates",
19594
19597
  "updates"
19595
19598
  ]);
19596
19599
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
@@ -20071,7 +20074,19 @@ var NcAlarmModeDelaysListSchema = array(object({
20071
20074
  * When this mode may be armed. Absent / null = always allowed (same as a
20072
20075
  * rule with no `schedule`). Evaluated with {@link isScheduleActive}.
20073
20076
  */
20074
- schedule: NcScheduleSchema.optional()
20077
+ schedule: NcScheduleSchema.optional(),
20078
+ /**
20079
+ * D706: while this mode's ENTRY DELAY runs, do the alarm rules' own pushes go out (as an
20080
+ * intrusion naming the locations)? Absent = false — the entry-delay notification is the
20081
+ * only one, and the matches ride the triggered notification's mosaic (D705).
20082
+ */
20083
+ notifyDuringEntryDelay: boolean().optional(),
20084
+ /**
20085
+ * D706: notify "watched again" when a sensor this arm bypassed closes and opens again.
20086
+ * Absent = false (logged, not sent). Even when true it is not sent when that same sensor
20087
+ * is the one that starts the entry delay — the entry-delay notification already names it.
20088
+ */
20089
+ notifySensorRewatched: boolean().optional()
20075
20090
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
20076
20091
  var NcAlarmModeDelaysEditListSchema = array(object({
20077
20092
  mode: AlarmArmModeSchema,
@@ -20080,7 +20095,11 @@ var NcAlarmModeDelaysEditListSchema = array(object({
20080
20095
  triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional(),
20081
20096
  postTriggeredAction: NcAlarmPostTriggeredActionSchema.nullable().optional(),
20082
20097
  /** Set a schedule, or `null` to clear (mode always allowed again). */
20083
- schedule: NcScheduleSchema.nullable().optional()
20098
+ schedule: NcScheduleSchema.nullable().optional(),
20099
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
20100
+ notifyDuringEntryDelay: boolean().nullable().optional(),
20101
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
20102
+ notifySensorRewatched: boolean().nullable().optional()
20084
20103
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
20085
20104
  /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
20086
20105
  var NcAlarmNonBlockingEditSchema = object({
@@ -21041,39 +21060,19 @@ Object.freeze(Object.fromEntries([{
21041
21060
  label: "Face recognition model",
21042
21061
  defaultModelId: "auraface-r100",
21043
21062
  indexName: "the enrolled face gallery",
21044
- options: [
21045
- {
21046
- id: "auraface-r100",
21047
- label: "AuraFace R100"
21048
- },
21049
- {
21050
- id: "arcface-r100",
21051
- label: "ArcFace (ResNet34)"
21052
- },
21053
- {
21054
- id: "inception-resnet-v1",
21055
- label: "Inception ResNet V1"
21056
- }
21057
- ]
21063
+ options: [{
21064
+ id: "auraface-r100",
21065
+ label: "AuraFace R100"
21066
+ }]
21058
21067
  }, {
21059
21068
  stepId: "clip-embedding",
21060
21069
  label: "Semantic search model",
21061
- defaultModelId: "mobileclip-s1",
21070
+ defaultModelId: "siglip2-b16-224",
21062
21071
  indexName: "the object semantic-search index",
21063
- options: [
21064
- {
21065
- id: "mobileclip-s1",
21066
- label: "MobileCLIP S1"
21067
- },
21068
- {
21069
- id: "mobileclip-s2",
21070
- label: "MobileCLIP S2"
21071
- },
21072
- {
21073
- id: "siglip2-b16-224",
21074
- label: "SigLIP2 B/16 (Apache-2.0)"
21075
- }
21076
- ]
21072
+ options: [{
21073
+ id: "siglip2-b16-224",
21074
+ label: "SigLIP2 B/16 (Apache-2.0)"
21075
+ }]
21077
21076
  }].map((s) => [s.stepId, s.defaultModelId])));
21078
21077
  var CLUSTER_STEP_SETTING_FIELDS = [{
21079
21078
  stepId: "face-embedding",
@@ -23921,6 +23920,22 @@ var MotionSourceEnum = _enum([
23921
23920
  "device-activity"
23922
23921
  ]);
23923
23922
  /**
23923
+ * What opened an on-motion detection SESSION: the motion source that passed the
23924
+ * camera's `motionSources` gate, and what raised it (the camera itself, or a
23925
+ * sensor the operator attached — D672). `sensorName` is the attached sensor's
23926
+ * device name as the orchestrator's camera-sensors resolution last read it;
23927
+ * absent when unknown or when the camera itself triggered.
23928
+ *
23929
+ * Carried on the session attach ONLY so the runner's `DetectionPhaseTransition`
23930
+ * can say what started the session (D672 rule 11, amended 2026-10-06). The
23931
+ * runner never decides anything on it.
23932
+ */
23933
+ var SessionTriggerSchema = object({
23934
+ source: MotionSourceEnum,
23935
+ triggeredBy: MotionTriggeredBySchema,
23936
+ sensorName: string().optional()
23937
+ });
23938
+ /**
23924
23939
  * List of motion sources active on a camera. Empty array is valid:
23925
23940
  * "no source" — happens for battery cams without firmware motion when
23926
23941
  * the operator hasn't opted into the SW analyzer (would hold the
@@ -24172,6 +24187,14 @@ var RunnerCameraConfigSchema = object({
24172
24187
  */
24173
24188
  motionSources: MotionSourcesSchema.default(["analyzer"]),
24174
24189
  /**
24190
+ * What opened this attach, on a SESSION attach only (see
24191
+ * {@link SessionTriggerSchema}). OPTIONAL with no default: absent on a
24192
+ * standing attach, an occupancy burst, a re-dispatch with no motion event
24193
+ * behind it, and every attach from an orchestrator older than the field — the
24194
+ * runner then names no source on the phase event.
24195
+ */
24196
+ sessionTrigger: SessionTriggerSchema.optional(),
24197
+ /**
24175
24198
  * WHICH root detector runs for this camera. The detection counterpart of
24176
24199
  * {@link motionSources}, same shape and same discipline: a per-camera list
24177
24200
  * of sources the system understands, vendor-agnostic, owned here.
@@ -30395,7 +30418,7 @@ var LocalInterfaceSchema = object({
30395
30418
  name: string(),
30396
30419
  family: _enum(["IPv4", "IPv6"]),
30397
30420
  address: string(),
30398
- /** CIDR shorthand (e.g. `192.168.x.x/24`). Empty when OS doesn't expose. */
30421
+ /** CIDR shorthand (e.g. `192.168.10.10/24`). Empty when OS doesn't expose. */
30399
30422
  cidr: string(),
30400
30423
  /** OS netmask. Stable across calls so the SDK can hash on it. */
30401
30424
  netmask: string(),
@@ -30449,7 +30472,8 @@ var ConnectionEndpointSchema = object({
30449
30472
  "lan-ipv4",
30450
30473
  "lan-ipv6",
30451
30474
  "public",
30452
- "loopback"
30475
+ "loopback",
30476
+ "app-link"
30453
30477
  ]),
30454
30478
  /**
30455
30479
  * Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
@@ -30518,15 +30542,27 @@ var GetConnectionEndpointsResultSchema = object({
30518
30542
  localPort: AdvertisedLocalPortSchema
30519
30543
  });
30520
30544
  /**
30521
- * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
30522
- * AUTO (resolved from the candidate ranking at send time); `resolved` reports
30523
- * what AUTO currently picks, so the UI can show the effective value either way.
30545
+ * The address flagged for notification links. `baseUrl: null` = AUTO, which
30546
+ * since D709 is the CamStack app link; `resolved` reports the effective value,
30547
+ * so the UI can show it either way.
30524
30548
  */
30525
30549
  var NotificationEndpointSchema = object({
30526
30550
  /** The operator's explicit choice, or null for AUTO. */
30527
30551
  baseUrl: string().nullable(),
30528
- /** What the ranking currently resolves to (null when nothing is reachable). */
30529
- resolved: string().nullable()
30552
+ /**
30553
+ * The effective flagged address: the explicit choice, else (AUTO) the
30554
+ * CamStack app link `https://camstack.zentik.app` (D709). A pre-D709 hub
30555
+ * answered the public tunnel / mesh here, or null.
30556
+ */
30557
+ resolved: string().nullable(),
30558
+ /**
30559
+ * The hub origin an app link carries as `hub=` (D709): the hub's best
30560
+ * public HTTPS address — the tunnel, then the mesh (Tailscale MagicDNS) —
30561
+ * picked from this cap's own palette by `pickAppLinkHub`. `null` when the
30562
+ * hub advertises neither; the app link is then minted without `hub=`.
30563
+ * Absent on a pre-D709 hub.
30564
+ */
30565
+ appLinkHub: string().nullable()
30530
30566
  });
30531
30567
  /**
30532
30568
  * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
@@ -30580,7 +30616,11 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
30580
30616
  * Pass `'https'` when the caller is itself loaded over HTTPS
30581
30617
  * to avoid mixed-content blocks in the browser. The public
30582
30618
  * tunnel always emits `https://` regardless. */
30583
- scheme: _enum(["http", "https"]).optional()
30619
+ scheme: _enum(["http", "https"]).optional(),
30620
+ /** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
30621
+ * an address notification links can be flagged with, not a hub
30622
+ * route. Default `false`: racers and rankers never see it. */
30623
+ includeAppLink: boolean().optional()
30584
30624
  }), 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, {
30585
30625
  kind: "mutation",
30586
30626
  auth: "admin"
@@ -30841,7 +30881,7 @@ var MeshPeerSchema = object({
30841
30881
  tags: array(string()).readonly(),
30842
30882
  /** True when the peer advertises itself as an exit-node candidate. */
30843
30883
  exitNodeOption: boolean(),
30844
- /** Subnet routes the peer advertises (e.g. `["192.168.x.x/24"]`). */
30884
+ /** Subnet routes the peer advertises (e.g. `["192.168.10.0/24"]`). */
30845
30885
  advertisedRoutes: array(string()).readonly(),
30846
30886
  /** Owner account login (e.g. `foo@example.com`). Null for tagged peers. */
30847
30887
  userLogin: string().nullable(),
@@ -31846,7 +31886,13 @@ var OsdStatusSchema = object({ overlays: array(object({
31846
31886
  */
31847
31887
  availablePositions: array(OsdPositionEnum).readonly(),
31848
31888
  /** Firmware does not allow editing the `text` field (e.g. timestamp). */
31849
- readOnly: boolean().optional()
31889
+ readOnly: boolean().optional(),
31890
+ /**
31891
+ * Firmware text ceiling for this overlay. Absent = no known limit.
31892
+ * The OSD manager truncates here on every write — never stores its own
31893
+ * copy on the binding.
31894
+ */
31895
+ maxCharacters: number().int().positive().optional()
31850
31896
  })).readonly() });
31851
31897
  var OsdOverlayPatchSchema = object({
31852
31898
  enabled: boolean().optional(),
@@ -31897,7 +31943,9 @@ var OsdTemplateBindingSchema = object({
31897
31943
  valuePath: string().min(1).max(64),
31898
31944
  /** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
31899
31945
  displayUnit: string().max(16).optional(),
31900
- maxDecimals: number().int().min(0).max(4).default(1)
31946
+ maxDecimals: number().int().min(0).max(4).default(1),
31947
+ /** Raw value → display text (same shape as slot binding). Empty string hides. */
31948
+ valueMap: record(string(), string()).optional()
31901
31949
  });
31902
31950
  /**
31903
31951
  * A named template: bindings (alias → leaf field) + a parser that
@@ -31947,18 +31995,11 @@ var OsdSlotBindingSchema = object({
31947
31995
  source: OsdSourceSchema,
31948
31996
  /** `${value}` and `${unit}` are substituted; every occurrence. */
31949
31997
  template: string().max(96).default("${value}"),
31950
- /** Truncate with an ellipsis past this length. Absent = no limit. */
31951
- maxCharacters: number().int().min(4).max(64).optional(),
31952
31998
  /**
31953
31999
  * Decimal places for a numeric value. `0` yields an integer — the
31954
32000
  * documented workaround for firmwares that reject `.` in overlay text.
31955
32001
  */
31956
32002
  maxDecimals: number().int().min(0).max(4).default(1),
31957
- /**
31958
- * Appended via `${unit}` when no unit conversion produces a label.
31959
- * Prefer `source.displayUnit` for converted device-state values.
31960
- */
31961
- unitLabel: string().max(8).optional(),
31962
32003
  /** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
31963
32004
  valueMap: record(string(), string()).optional(),
31964
32005
  /** Time windows in which the slot is shown. Absent = always. */
@@ -31967,9 +32008,7 @@ var OsdSlotBindingSchema = object({
31967
32008
  * Display gate, in the notification centre's condition vocabulary.
31968
32009
  * Only the keys reported by `getConditionSupport` are accepted.
31969
32010
  */
31970
- conditions: NcConditionsSchema.optional(),
31971
- /** Rendered when the gate is closed or the value unreadable. Empty = hide. */
31972
- fallbackText: string().max(64).default("")
32011
+ conditions: NcConditionsSchema.optional()
31973
32012
  });
31974
32013
  /** One camera slot, as the operator sees it: firmware truth + our binding. */
31975
32014
  var OsdSlotViewSchema = object({
@@ -47646,12 +47685,14 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
47646
47685
  {
47647
47686
  id: "addons",
47648
47687
  family: "system",
47649
- label: "Addons",
47650
- kinds: [
47651
- "addon-crash-loop",
47652
- "addon-update-available",
47653
- "addon-updated"
47654
- ]
47688
+ label: "Addons stopped",
47689
+ kinds: ["addon-crash-loop"]
47690
+ },
47691
+ {
47692
+ id: "addon-updates",
47693
+ family: "system",
47694
+ label: "Addon updates",
47695
+ kinds: ["addon-update-available", "addon-updated"]
47655
47696
  },
47656
47697
  {
47657
47698
  id: "updates",
@@ -47811,13 +47852,15 @@ var STATE_SUMMARY_SECTION_VARS = [
47811
47852
  },
47812
47853
  {
47813
47854
  name: "summaryAddons",
47814
- label: "Summary: addons",
47815
- example: "@camstack/addon-recorder 1.2.3 → 1.2.4",
47816
- kinds: [
47817
- "addon-crash-loop",
47818
- "addon-update-available",
47819
- "addon-updated"
47820
- ]
47855
+ label: "Summary: addons stopped",
47856
+ example: "hub — recorder stopped after repeated crashes",
47857
+ kinds: ["addon-crash-loop"]
47858
+ },
47859
+ {
47860
+ name: "summaryAddonUpdates",
47861
+ label: "Summary: addon updates",
47862
+ example: "hub:\n• @camstack/addon-recorder 1.2.3 → 1.2.4",
47863
+ kinds: ["addon-update-available", "addon-updated"]
47821
47864
  },
47822
47865
  {
47823
47866
  name: "summaryUpdates",
@@ -47901,6 +47944,7 @@ var STATE_SUMMARY_VARS = [
47901
47944
  [...RULE_BODY_ONLY],
47902
47945
  [...RULE_TEXT],
47903
47946
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
47947
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
47904
47948
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
47905
47949
  ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
47906
47950
  ([...RULE_TEXT], [...CONSUMABLE]),
@@ -47941,6 +47985,8 @@ var STATE_SUMMARY_VARS = [
47941
47985
  [...RULE_TEXT],
47942
47986
  [...RULE_TEXT],
47943
47987
  [...RULE_TEXT],
47988
+ [...RULE_TEXT],
47989
+ [...RULE_TEXT],
47944
47990
  ...STATE_SUMMARY_VARS
47945
47991
  ];
47946
47992
  new Set([
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-jCCe7qVD.js");
5
+ const require_dist = require("../dist-B5khvoZj.js");
6
6
  let node_crypto = require("node:crypto");
7
7
  let node_child_process = require("node:child_process");
8
8
  //#region src/ingress/tailscale-cli.ts
@@ -1,4 +1,4 @@
1
- import { a as EventCategory, i as BaseAddon, n as networkAccessCapability } from "../dist-DOnpd-Z8.mjs";
1
+ import { a as EventCategory, i as BaseAddon, n as networkAccessCapability } from "../dist-CnsQ7vyF.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { execFile } from "node:child_process";
4
4
  import { promisify } from "node:util";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-tailscale",
3
- "version": "1.2.116",
3
+ "version": "1.2.118",
4
4
  "description": "Tailscale bundle for CamStack — joins the host to a tailnet (client) and exposes the hub via Serve/Funnel (ingress). Multi-entry npm package shipping 2 addons under a single bundle.",
5
5
  "keywords": [
6
6
  "camstack",