@camstack/system 1.2.112 → 1.2.114

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 (52) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  9. package/dist/builtins/console-logging/index.js +1 -1
  10. package/dist/builtins/console-logging/index.mjs +1 -1
  11. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  12. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  13. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  14. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  15. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  16. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  17. package/dist/builtins/hub-forwarder/index.js +1 -1
  18. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  19. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  20. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  21. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  22. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  23. package/dist/builtins/local-network/local-network.addon.d.ts +33 -1
  24. package/dist/builtins/local-network/local-network.addon.js +87 -2
  25. package/dist/builtins/local-network/local-network.addon.mjs +87 -3
  26. package/dist/builtins/loki-logging/index.js +1 -1
  27. package/dist/builtins/loki-logging/index.mjs +1 -1
  28. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  29. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  30. package/dist/builtins/platform-probe/index.js +1 -1
  31. package/dist/builtins/platform-probe/index.mjs +1 -1
  32. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  34. package/dist/builtins/snapshot/index.js +1 -1
  35. package/dist/builtins/snapshot/index.mjs +1 -1
  36. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  38. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +2 -2
  39. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
  40. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  41. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  42. package/dist/builtins/system-config/system-config.addon.js +1 -1
  43. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  44. package/dist/builtins/winston-logging/index.js +1 -1
  45. package/dist/builtins/winston-logging/index.mjs +1 -1
  46. package/dist/{dist-IPLFQ_-A.js → dist-C1Aea3c6.js} +176 -8
  47. package/dist/{dist-bkg2-2g3.mjs → dist-CJ8RRSvQ.mjs} +176 -8
  48. package/dist/index.js +1 -1
  49. package/dist/index.mjs +1 -1
  50. package/dist/{retired-settings-keys-B-I7w1Ta.js → retired-settings-keys-B52KsNz8.js} +1 -1
  51. package/dist/{retired-settings-keys-C7rRSzbP.mjs → retired-settings-keys-C1efPfyW.mjs} +1 -1
  52. package/package.json +1 -1
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-IPLFQ_-A.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -1,4 +1,4 @@
1
- import { dt as storageCapability, ft as storageMigrationCapability, g as StorageMigrationJobSchema, h as StorageLocationTypeSchema, lt as settingsStoreCapability, yt as BaseAddon, zt as parseJsonObject } from "../../dist-bkg2-2g3.mjs";
1
+ import { dt as storageCapability, ft as storageMigrationCapability, g as StorageMigrationJobSchema, h as StorageLocationTypeSchema, lt as settingsStoreCapability, yt as BaseAddon, zt as parseJsonObject } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-IPLFQ_-A.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  //#region src/builtins/system-config/system-config.addon.ts
8
8
  /**
9
9
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -1,4 +1,4 @@
1
- import { It as hydrateSchema, vt as errMsg, yt as BaseAddon } from "../../dist-bkg2-2g3.mjs";
1
+ import { It as hydrateSchema, vt as errMsg, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  //#region src/builtins/system-config/system-config.addon.ts
3
3
  /**
4
4
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-IPLFQ_-A.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  let node_path = require("node:path");
9
9
  node_path = require_chunk.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-bkg2-2g3.mjs";
1
+ import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  import * as path$1 from "node:path";
4
4
  import path from "node:path";
@@ -3669,7 +3669,7 @@ var RecordingBandSchema = zod.z.object({
3669
3669
  postBufferSec: zod.z.number().min(0).optional()
3670
3670
  });
3671
3671
  ({
3672
- preBufferSec: 10,
3672
+ preBufferSec: 15,
3673
3673
  postBufferSec: 30
3674
3674
  }).postBufferSec * 1e3;
3675
3675
  /**
@@ -14363,7 +14363,7 @@ var TrackEnvelopeSchema = zod.z.object({
14363
14363
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
14364
14364
  * keeps every scalar the list surfaces actually render (ids, class(es),
14365
14365
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
14366
- * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
14366
+ * zonesVisited, bestEventId, envelope, hasFace, hasEmbeddedFace, hasRider) and returns `positions` /
14367
14367
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
14368
14368
  * `getTrack`. Mirrors the event-store `projection` convention
14369
14369
  * (`getObjectEvents` et al.).
@@ -14658,6 +14658,35 @@ var TrackSchema = zod.z.object({
14658
14658
  */
14659
14659
  hasFace: zod.z.boolean().optional(),
14660
14660
  /**
14661
+ * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
14662
+ * face an operator could ASSIGN to an identity.
14663
+ *
14664
+ * The STRICT twin of {@link hasFace}, and the pair only earns its keep
14665
+ * because the two disagree. `hasFace` is stamped at the TOP of the face
14666
+ * branch, before every gate, and means no more than "a face detector produced
14667
+ * a face detail". This one is stamped at the single moment the gallery row
14668
+ * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
14669
+ * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
14670
+ * candidate gate, the imageless-track drop (no crop was ever captured) and
14671
+ * the crop-store drop. Everything between the detector and that insert can
14672
+ * legitimately refuse the face, so a flag written any earlier promises the
14673
+ * operator something to assign and delivers nothing.
14674
+ *
14675
+ * **Independent of recognition.** A face collected but never auto-matched is
14676
+ * still assignable — it is in fact the face an operator most wants to reach —
14677
+ * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
14678
+ * `subLabel`; this says only that the raw material exists.
14679
+ *
14680
+ * **Set once, never cleared.** A track that produced a gallery row produced
14681
+ * one; deleting the row later is the gallery's business, not this flag's.
14682
+ *
14683
+ * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
14684
+ * before the column omits it, and so does every server that predates the
14685
+ * field. A consumer must test `=== true` and render nothing otherwise —
14686
+ * never infer "no assignable face".
14687
+ */
14688
+ hasEmbeddedFace: zod.z.boolean().optional(),
14689
+ /**
14661
14690
  * This subject CONTAINS a folded rider — a person the rider-pairing step
14662
14691
  * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
14663
14692
  * so the passage is tracked once and as a VEHICLE.
@@ -16615,6 +16644,26 @@ var DetailResultSchema = zod.z.object({
16615
16644
  * the consuming gate treats as accept — never as reject.
16616
16645
  */
16617
16646
  embeddingMagnitude: zod.z.number().optional(),
16647
+ /**
16648
+ * Model that produced `embedding` — the SAME contract `labelModelId` has for
16649
+ * `label`, and for the same reason: the producing node is the only side that
16650
+ * knows which model actually ran, and re-deriving it downstream is exactly
16651
+ * the inference this model exists to forbid.
16652
+ *
16653
+ * Additive and optional, so a runner that predates it degrades to
16654
+ * "unmeasurable" rather than to a wrong answer — the consumer falls back to
16655
+ * its previous assumption instead of refusing.
16656
+ *
16657
+ * Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
16658
+ * to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
16659
+ * with nothing on the wire it took the id from the GALLERY, so the gate
16660
+ * compared the gallery against itself and could never fire. That turned the
16661
+ * one guard protecting the face feature space into a tautology, and it is
16662
+ * why swapping `face-embedding`'s model would NOT have stranded the enrolled
16663
+ * gallery as the catalog comment predicts — it would have scored the new
16664
+ * model's vectors against the old model's samples in one cosine space.
16665
+ */
16666
+ embeddingModelId: zod.z.string().optional(),
16618
16667
  label: zod.z.string().optional(),
16619
16668
  /**
16620
16669
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -17660,6 +17709,12 @@ var CameraStatusSchema = zod.z.object({
17660
17709
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
17661
17710
  fetchedAt: zod.z.number()
17662
17711
  });
17712
+ var InferenceDeviceExclusionReasonSchema = zod.z.enum([
17713
+ "disabled",
17714
+ "unavailable",
17715
+ "cannot-host-camera-root",
17716
+ "accelerator-preferred"
17717
+ ]);
17663
17718
  var NodeInferenceDeviceSchema = zod.z.object({
17664
17719
  /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17665
17720
  key: zod.z.string(),
@@ -17690,7 +17745,17 @@ var NodeInferenceDeviceSchema = zod.z.object({
17690
17745
  * available per format; this is the stored selection that becomes the
17691
17746
  * default for EVERY camera landing on this accelerator.
17692
17747
  */
17693
- steps: zod.z.record(zod.z.string(), DeviceStepConfigSchema).optional()
17748
+ steps: zod.z.record(zod.z.string(), DeviceStepConfigSchema).optional(),
17749
+ /**
17750
+ * `null` when the device IS a camera-root candidate on this node; otherwise
17751
+ * the reason the dispatcher drops it. Computed by the SAME
17752
+ * `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
17753
+ * never disagree with the election — deriving it in the UI from
17754
+ * `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
17755
+ * the node's model catalog) and `accelerator-preferred` (needs the node-wide
17756
+ * "an accelerator is serving" predicate).
17757
+ */
17758
+ exclusion: InferenceDeviceExclusionReasonSchema.nullable()
17694
17759
  });
17695
17760
  var NodeInferenceDevicesSchema = zod.z.object({
17696
17761
  nodeId: zod.z.string(),
@@ -23791,7 +23856,41 @@ var ConnectionEndpointSchema = zod.z.object({
23791
23856
  */
23792
23857
  priority: zod.z.number()
23793
23858
  });
23794
- var GetConnectionEndpointsResultSchema = zod.z.object({ endpoints: zod.z.array(ConnectionEndpointSchema).readonly() });
23859
+ /**
23860
+ * Where the advertised local port came from. Ordered most → least
23861
+ * authoritative, and the whole point of returning it: a client must be able to
23862
+ * tell a FACT about the hub's socket from an echo of its own guess.
23863
+ */
23864
+ var LocalPortSourceEnum = zod.z.enum([
23865
+ "server-config",
23866
+ "server-env",
23867
+ "caller-hint",
23868
+ "default"
23869
+ ]);
23870
+ /** The port every LAN/loopback `baseUrl` in the same result was built with. */
23871
+ var AdvertisedLocalPortSchema = zod.z.object({
23872
+ port: zod.z.number().int().min(1).max(65535),
23873
+ source: LocalPortSourceEnum
23874
+ });
23875
+ var GetConnectionEndpointsResultSchema = zod.z.object({
23876
+ endpoints: zod.z.array(ConnectionEndpointSchema).readonly(),
23877
+ /**
23878
+ * The port the hub built the LAN/loopback URLs with, and where that number
23879
+ * came from.
23880
+ *
23881
+ * Returned rather than merely applied, because "the URL is right" and "the
23882
+ * client can KNOW the URL is right" are different properties. A client that
23883
+ * only sees a corrected URL cannot distinguish a hub that fixed the port from
23884
+ * a hub that echoed the port the client sent, so it cannot decide whether to
23885
+ * race the candidate or discard it. With `source` it can: anything but
23886
+ * `caller-hint` is the hub's own socket.
23887
+ *
23888
+ * Absent on hubs predating this field — a client that finds it missing is
23889
+ * talking to an echoing hub and must degrade exactly as it does for
23890
+ * `caller-hint`.
23891
+ */
23892
+ localPort: AdvertisedLocalPortSchema
23893
+ });
23795
23894
  /**
23796
23895
  * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
23797
23896
  * AUTO (resolved from the candidate ranking at send time); `resolved` reports
@@ -23837,10 +23936,35 @@ var localNetworkCapability = {
23837
23936
  * Honours `getAllowedAddresses()` when set — addresses outside
23838
23937
  * the allowlist are dropped (the public tunnel + loopback are
23839
23938
  * always included as escape hatches).
23939
+ *
23940
+ * **The port is the hub's, not the caller's** (D62 — a function's fact
23941
+ * belongs to whoever already owns it). This method used to take a `port`
23942
+ * and echo it onto every LAN and loopback URL it advertised, which made
23943
+ * every client a second authority on a socket only the hub binds. A viewer
23944
+ * configured with a port-less public URL (`https://camstack.example.top`)
23945
+ * infers 443 from the scheme, asks with 443, and was handed
23946
+ * `https://192.168.1.9:443` for a hub listening on 4443. That candidate
23947
+ * cannot ever answer, and losing its race reads in the log exactly like
23948
+ * "the LAN was tried and it was slower" — so an operator sitting on his own
23949
+ * WiFi stayed on the public tunnel for weeks, with the LAN candidate born
23950
+ * dead on every single connect.
23951
+ *
23952
+ * The hub now resolves its own listen port and reports it as `localPort`,
23953
+ * with the SOURCE of the number, so a client can tell a fact from an echo.
23954
+ *
23955
+ * Out of scope on purpose: a LAN URL that is not the hub's own socket (a
23956
+ * reverse proxy in front of it on another port) is not this method's to
23957
+ * invent either — pin it with `setNotificationEndpoint` / a configured
23958
+ * origin, which is an operator statement rather than a guess.
23840
23959
  */
23841
23960
  getConnectionEndpoints: method(zod.z.object({
23842
- /** Local hub HTTP port to use in base URLs. */
23843
- port: zod.z.number().int().min(1).max(65535),
23961
+ /**
23962
+ * LEGACY HINT — do not send from new code. Kept optional so clients
23963
+ * written against the echoing contract keep working; the hub uses it
23964
+ * only when it cannot read its own port, and says so via
23965
+ * `localPort.source === 'caller-hint'`.
23966
+ */
23967
+ port: zod.z.number().int().min(1).max(65535).optional(),
23844
23968
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
23845
23969
  * candidate. Default `true`. */
23846
23970
  includeLoopback: zod.z.boolean().optional(),
@@ -38007,6 +38131,39 @@ TimelapseRuleInputSchema.extend({
38007
38131
  createdAt: zod.z.number(),
38008
38132
  updatedAt: zod.z.number()
38009
38133
  });
38134
+ Object.freeze(Object.fromEntries([{
38135
+ stepId: "face-embedding",
38136
+ label: "Face recognition model",
38137
+ defaultModelId: "arcface-r100",
38138
+ indexName: "the enrolled face gallery",
38139
+ options: [
38140
+ {
38141
+ id: "arcface-r100",
38142
+ label: "ArcFace (ResNet34)"
38143
+ },
38144
+ {
38145
+ id: "auraface-r100",
38146
+ label: "AuraFace R100"
38147
+ },
38148
+ {
38149
+ id: "inception-resnet-v1",
38150
+ label: "Inception ResNet V1"
38151
+ }
38152
+ ]
38153
+ }, {
38154
+ stepId: "clip-embedding",
38155
+ label: "Semantic search model",
38156
+ defaultModelId: "mobileclip-s1",
38157
+ indexName: "the object semantic-search index",
38158
+ options: [{
38159
+ id: "mobileclip-s1",
38160
+ label: "MobileCLIP S1"
38161
+ }, {
38162
+ id: "mobileclip-s2",
38163
+ label: "MobileCLIP S2"
38164
+ }]
38165
+ }].map((s) => [s.stepId, s.defaultModelId])));
38166
+ zod.z.string().min(1);
38010
38167
  zod.z.object({
38011
38168
  /**
38012
38169
  * Fraction of the box's own size added on EACH side before cutting.
@@ -38027,10 +38184,21 @@ zod.z.object({
38027
38184
  */
38028
38185
  square: zod.z.boolean()
38029
38186
  });
38030
- ({
38187
+ /**
38188
+ * The convention in force when nobody has configured one — byte-for-byte the
38189
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
38190
+ */
38191
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
38031
38192
  paddingRatio: .15,
38032
38193
  square: false
38033
- }).paddingRatio;
38194
+ };
38195
+ Object.freeze({
38196
+ x: 0,
38197
+ y: 0,
38198
+ w: 1,
38199
+ h: 1
38200
+ });
38201
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
38034
38202
  /**
38035
38203
  * WHICH delivered frames the decode worker retains a native copy of.
38036
38204
  *
@@ -3669,7 +3669,7 @@ var RecordingBandSchema = z.object({
3669
3669
  postBufferSec: z.number().min(0).optional()
3670
3670
  });
3671
3671
  ({
3672
- preBufferSec: 10,
3672
+ preBufferSec: 15,
3673
3673
  postBufferSec: 30
3674
3674
  }).postBufferSec * 1e3;
3675
3675
  /**
@@ -14363,7 +14363,7 @@ var TrackEnvelopeSchema = z.object({
14363
14363
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
14364
14364
  * keeps every scalar the list surfaces actually render (ids, class(es),
14365
14365
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
14366
- * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
14366
+ * zonesVisited, bestEventId, envelope, hasFace, hasEmbeddedFace, hasRider) and returns `positions` /
14367
14367
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
14368
14368
  * `getTrack`. Mirrors the event-store `projection` convention
14369
14369
  * (`getObjectEvents` et al.).
@@ -14658,6 +14658,35 @@ var TrackSchema = z.object({
14658
14658
  */
14659
14659
  hasFace: z.boolean().optional(),
14660
14660
  /**
14661
+ * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
14662
+ * face an operator could ASSIGN to an identity.
14663
+ *
14664
+ * The STRICT twin of {@link hasFace}, and the pair only earns its keep
14665
+ * because the two disagree. `hasFace` is stamped at the TOP of the face
14666
+ * branch, before every gate, and means no more than "a face detector produced
14667
+ * a face detail". This one is stamped at the single moment the gallery row
14668
+ * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
14669
+ * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
14670
+ * candidate gate, the imageless-track drop (no crop was ever captured) and
14671
+ * the crop-store drop. Everything between the detector and that insert can
14672
+ * legitimately refuse the face, so a flag written any earlier promises the
14673
+ * operator something to assign and delivers nothing.
14674
+ *
14675
+ * **Independent of recognition.** A face collected but never auto-matched is
14676
+ * still assignable — it is in fact the face an operator most wants to reach —
14677
+ * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
14678
+ * `subLabel`; this says only that the raw material exists.
14679
+ *
14680
+ * **Set once, never cleared.** A track that produced a gallery row produced
14681
+ * one; deleting the row later is the gallery's business, not this flag's.
14682
+ *
14683
+ * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
14684
+ * before the column omits it, and so does every server that predates the
14685
+ * field. A consumer must test `=== true` and render nothing otherwise —
14686
+ * never infer "no assignable face".
14687
+ */
14688
+ hasEmbeddedFace: z.boolean().optional(),
14689
+ /**
14661
14690
  * This subject CONTAINS a folded rider — a person the rider-pairing step
14662
14691
  * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
14663
14692
  * so the passage is tracked once and as a VEHICLE.
@@ -16615,6 +16644,26 @@ var DetailResultSchema = z.object({
16615
16644
  * the consuming gate treats as accept — never as reject.
16616
16645
  */
16617
16646
  embeddingMagnitude: z.number().optional(),
16647
+ /**
16648
+ * Model that produced `embedding` — the SAME contract `labelModelId` has for
16649
+ * `label`, and for the same reason: the producing node is the only side that
16650
+ * knows which model actually ran, and re-deriving it downstream is exactly
16651
+ * the inference this model exists to forbid.
16652
+ *
16653
+ * Additive and optional, so a runner that predates it degrades to
16654
+ * "unmeasurable" rather than to a wrong answer — the consumer falls back to
16655
+ * its previous assumption instead of refusing.
16656
+ *
16657
+ * Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
16658
+ * to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
16659
+ * with nothing on the wire it took the id from the GALLERY, so the gate
16660
+ * compared the gallery against itself and could never fire. That turned the
16661
+ * one guard protecting the face feature space into a tautology, and it is
16662
+ * why swapping `face-embedding`'s model would NOT have stranded the enrolled
16663
+ * gallery as the catalog comment predicts — it would have scored the new
16664
+ * model's vectors against the old model's samples in one cosine space.
16665
+ */
16666
+ embeddingModelId: z.string().optional(),
16618
16667
  label: z.string().optional(),
16619
16668
  /**
16620
16669
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -17660,6 +17709,12 @@ var CameraStatusSchema = z.object({
17660
17709
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
17661
17710
  fetchedAt: z.number()
17662
17711
  });
17712
+ var InferenceDeviceExclusionReasonSchema = z.enum([
17713
+ "disabled",
17714
+ "unavailable",
17715
+ "cannot-host-camera-root",
17716
+ "accelerator-preferred"
17717
+ ]);
17663
17718
  var NodeInferenceDeviceSchema = z.object({
17664
17719
  /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17665
17720
  key: z.string(),
@@ -17690,7 +17745,17 @@ var NodeInferenceDeviceSchema = z.object({
17690
17745
  * available per format; this is the stored selection that becomes the
17691
17746
  * default for EVERY camera landing on this accelerator.
17692
17747
  */
17693
- steps: z.record(z.string(), DeviceStepConfigSchema).optional()
17748
+ steps: z.record(z.string(), DeviceStepConfigSchema).optional(),
17749
+ /**
17750
+ * `null` when the device IS a camera-root candidate on this node; otherwise
17751
+ * the reason the dispatcher drops it. Computed by the SAME
17752
+ * `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
17753
+ * never disagree with the election — deriving it in the UI from
17754
+ * `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
17755
+ * the node's model catalog) and `accelerator-preferred` (needs the node-wide
17756
+ * "an accelerator is serving" predicate).
17757
+ */
17758
+ exclusion: InferenceDeviceExclusionReasonSchema.nullable()
17694
17759
  });
17695
17760
  var NodeInferenceDevicesSchema = z.object({
17696
17761
  nodeId: z.string(),
@@ -23791,7 +23856,41 @@ var ConnectionEndpointSchema = z.object({
23791
23856
  */
23792
23857
  priority: z.number()
23793
23858
  });
23794
- var GetConnectionEndpointsResultSchema = z.object({ endpoints: z.array(ConnectionEndpointSchema).readonly() });
23859
+ /**
23860
+ * Where the advertised local port came from. Ordered most → least
23861
+ * authoritative, and the whole point of returning it: a client must be able to
23862
+ * tell a FACT about the hub's socket from an echo of its own guess.
23863
+ */
23864
+ var LocalPortSourceEnum = z.enum([
23865
+ "server-config",
23866
+ "server-env",
23867
+ "caller-hint",
23868
+ "default"
23869
+ ]);
23870
+ /** The port every LAN/loopback `baseUrl` in the same result was built with. */
23871
+ var AdvertisedLocalPortSchema = z.object({
23872
+ port: z.number().int().min(1).max(65535),
23873
+ source: LocalPortSourceEnum
23874
+ });
23875
+ var GetConnectionEndpointsResultSchema = z.object({
23876
+ endpoints: z.array(ConnectionEndpointSchema).readonly(),
23877
+ /**
23878
+ * The port the hub built the LAN/loopback URLs with, and where that number
23879
+ * came from.
23880
+ *
23881
+ * Returned rather than merely applied, because "the URL is right" and "the
23882
+ * client can KNOW the URL is right" are different properties. A client that
23883
+ * only sees a corrected URL cannot distinguish a hub that fixed the port from
23884
+ * a hub that echoed the port the client sent, so it cannot decide whether to
23885
+ * race the candidate or discard it. With `source` it can: anything but
23886
+ * `caller-hint` is the hub's own socket.
23887
+ *
23888
+ * Absent on hubs predating this field — a client that finds it missing is
23889
+ * talking to an echoing hub and must degrade exactly as it does for
23890
+ * `caller-hint`.
23891
+ */
23892
+ localPort: AdvertisedLocalPortSchema
23893
+ });
23795
23894
  /**
23796
23895
  * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
23797
23896
  * AUTO (resolved from the candidate ranking at send time); `resolved` reports
@@ -23837,10 +23936,35 @@ var localNetworkCapability = {
23837
23936
  * Honours `getAllowedAddresses()` when set — addresses outside
23838
23937
  * the allowlist are dropped (the public tunnel + loopback are
23839
23938
  * always included as escape hatches).
23939
+ *
23940
+ * **The port is the hub's, not the caller's** (D62 — a function's fact
23941
+ * belongs to whoever already owns it). This method used to take a `port`
23942
+ * and echo it onto every LAN and loopback URL it advertised, which made
23943
+ * every client a second authority on a socket only the hub binds. A viewer
23944
+ * configured with a port-less public URL (`https://camstack.example.top`)
23945
+ * infers 443 from the scheme, asks with 443, and was handed
23946
+ * `https://192.168.1.9:443` for a hub listening on 4443. That candidate
23947
+ * cannot ever answer, and losing its race reads in the log exactly like
23948
+ * "the LAN was tried and it was slower" — so an operator sitting on his own
23949
+ * WiFi stayed on the public tunnel for weeks, with the LAN candidate born
23950
+ * dead on every single connect.
23951
+ *
23952
+ * The hub now resolves its own listen port and reports it as `localPort`,
23953
+ * with the SOURCE of the number, so a client can tell a fact from an echo.
23954
+ *
23955
+ * Out of scope on purpose: a LAN URL that is not the hub's own socket (a
23956
+ * reverse proxy in front of it on another port) is not this method's to
23957
+ * invent either — pin it with `setNotificationEndpoint` / a configured
23958
+ * origin, which is an operator statement rather than a guess.
23840
23959
  */
23841
23960
  getConnectionEndpoints: method(z.object({
23842
- /** Local hub HTTP port to use in base URLs. */
23843
- port: z.number().int().min(1).max(65535),
23961
+ /**
23962
+ * LEGACY HINT — do not send from new code. Kept optional so clients
23963
+ * written against the echoing contract keep working; the hub uses it
23964
+ * only when it cannot read its own port, and says so via
23965
+ * `localPort.source === 'caller-hint'`.
23966
+ */
23967
+ port: z.number().int().min(1).max(65535).optional(),
23844
23968
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
23845
23969
  * candidate. Default `true`. */
23846
23970
  includeLoopback: z.boolean().optional(),
@@ -38007,6 +38131,39 @@ TimelapseRuleInputSchema.extend({
38007
38131
  createdAt: z.number(),
38008
38132
  updatedAt: z.number()
38009
38133
  });
38134
+ Object.freeze(Object.fromEntries([{
38135
+ stepId: "face-embedding",
38136
+ label: "Face recognition model",
38137
+ defaultModelId: "arcface-r100",
38138
+ indexName: "the enrolled face gallery",
38139
+ options: [
38140
+ {
38141
+ id: "arcface-r100",
38142
+ label: "ArcFace (ResNet34)"
38143
+ },
38144
+ {
38145
+ id: "auraface-r100",
38146
+ label: "AuraFace R100"
38147
+ },
38148
+ {
38149
+ id: "inception-resnet-v1",
38150
+ label: "Inception ResNet V1"
38151
+ }
38152
+ ]
38153
+ }, {
38154
+ stepId: "clip-embedding",
38155
+ label: "Semantic search model",
38156
+ defaultModelId: "mobileclip-s1",
38157
+ indexName: "the object semantic-search index",
38158
+ options: [{
38159
+ id: "mobileclip-s1",
38160
+ label: "MobileCLIP S1"
38161
+ }, {
38162
+ id: "mobileclip-s2",
38163
+ label: "MobileCLIP S2"
38164
+ }]
38165
+ }].map((s) => [s.stepId, s.defaultModelId])));
38166
+ z.string().min(1);
38010
38167
  z.object({
38011
38168
  /**
38012
38169
  * Fraction of the box's own size added on EACH side before cutting.
@@ -38027,10 +38184,21 @@ z.object({
38027
38184
  */
38028
38185
  square: z.boolean()
38029
38186
  });
38030
- ({
38187
+ /**
38188
+ * The convention in force when nobody has configured one — byte-for-byte the
38189
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
38190
+ */
38191
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
38031
38192
  paddingRatio: .15,
38032
38193
  square: false
38033
- }).paddingRatio;
38194
+ };
38195
+ Object.freeze({
38196
+ x: 0,
38197
+ y: 0,
38198
+ w: 1,
38199
+ h: 1
38200
+ });
38201
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
38034
38202
  /**
38035
38203
  * WHICH delivered frames the decode worker retains a native copy of.
38036
38204
  *
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-Cek0wNdY.js");
3
- const require_dist = require("./dist-IPLFQ_-A.js");
3
+ const require_dist = require("./dist-C1Aea3c6.js");
4
4
  const require_builtins_alerts_alerts_addon = require("./builtins/alerts/alerts.addon.js");
5
5
  require("./builtins/alerts/index.js");
6
6
  const require_formatter = require("./formatter-DqAKDlvN.js");
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM$1, r as __exportAll, t as __commonJSMin$1 } from "./chunk-CNf5ZN-e.mjs";
2
- import { At as asNumber, B as extractNestedAddonId, Bt as parseJsonUnknown$1, Dt as ReadinessTimeoutError, Et as ReadinessRegistry, Ft as expandCapMethods, Gt as EventCategory$1, H as isArrayOutputSchema, Ht as resolveCapMount, J as lifecycleJobSchema, K as isVoidInput, Mt as createEvent, Nt as emitDownForOwnedCaps, Q as looseSchema, St as DEVICE_STATUS_METHOD, U as isCollectionArrayMethod, Ut as scopeKey, Vt as readinessKey, W as isObjectInput, Z as logLevelAtMost, bt as DATAPLANE_SECRET_HEADER$1, d as METHOD_ACCESS_MAP, f as RUNTIME_DEFAULTS, it as procedureAuthKey, jt as asString$1, kt as asJsonObject$1, ot as scopesAllowAddon, q as kebabToCamel, st as scopesAllowDeviceCap, t as ALL_CAPABILITY_DEFINITIONS, tt as objectInputDeclaresAddonId, vt as errMsg$1, xt as DEVICE_SETTINGS_CONTRIBUTION_METHODS, y as addonSettingsCapability, zt as parseJsonObject } from "./dist-bkg2-2g3.mjs";
2
+ import { At as asNumber, B as extractNestedAddonId, Bt as parseJsonUnknown$1, Dt as ReadinessTimeoutError, Et as ReadinessRegistry, Ft as expandCapMethods, Gt as EventCategory$1, H as isArrayOutputSchema, Ht as resolveCapMount, J as lifecycleJobSchema, K as isVoidInput, Mt as createEvent, Nt as emitDownForOwnedCaps, Q as looseSchema, St as DEVICE_STATUS_METHOD, U as isCollectionArrayMethod, Ut as scopeKey, Vt as readinessKey, W as isObjectInput, Z as logLevelAtMost, bt as DATAPLANE_SECRET_HEADER$1, d as METHOD_ACCESS_MAP, f as RUNTIME_DEFAULTS, it as procedureAuthKey, jt as asString$1, kt as asJsonObject$1, ot as scopesAllowAddon, q as kebabToCamel, st as scopesAllowDeviceCap, t as ALL_CAPABILITY_DEFINITIONS, tt as objectInputDeclaresAddonId, vt as errMsg$1, xt as DEVICE_SETTINGS_CONTRIBUTION_METHODS, y as addonSettingsCapability, zt as parseJsonObject } from "./dist-CJ8RRSvQ.mjs";
3
3
  import { AlertCenterAddon } from "./builtins/alerts/alerts.addon.mjs";
4
4
  import "./builtins/alerts/index.mjs";
5
5
  import { t as formatLogLine } from "./formatter-B7qW8bPJ.mjs";
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-IPLFQ_-A.js");
1
+ const require_dist = require("./dist-C1Aea3c6.js");
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
@@ -1,4 +1,4 @@
1
- import { kt as asJsonObject } from "./dist-bkg2-2g3.mjs";
1
+ import { kt as asJsonObject } from "./dist-CJ8RRSvQ.mjs";
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.112",
3
+ "version": "1.2.114",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",