@camstack/addon-provider-onvif 1.2.25 → 1.2.27

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 +432 -50
  2. package/dist/addon.mjs +432 -50
  3. package/package.json +1 -1
package/dist/addon.mjs CHANGED
@@ -5805,6 +5805,13 @@ var BaseAddon = class {
5805
5805
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5806
5806
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5807
5807
  _registeredCapNames = [];
5808
+ /**
5809
+ * True only after `readAddonStore` actually answered. Constructor
5810
+ * defaults look like stored config when the store is down — a forked
5811
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5812
+ * mode, 2026-08-25) is not "the operator chose this".
5813
+ */
5814
+ settingsStoreReady = false;
5808
5815
  /** Default config values. Provided via constructor. */
5809
5816
  defaults;
5810
5817
  constructor(defaults) {
@@ -6205,7 +6212,9 @@ var BaseAddon = class {
6205
6212
  ];
6206
6213
  let lastErr;
6207
6214
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6208
- return await settings.readAddonStore() ?? {};
6215
+ const stored = await settings.readAddonStore() ?? {};
6216
+ this.settingsStoreReady = true;
6217
+ return stored;
6209
6218
  } catch (err) {
6210
6219
  lastErr = err;
6211
6220
  const msg = err instanceof Error ? err.message : String(err);
@@ -6213,6 +6222,7 @@ var BaseAddon = class {
6213
6222
  if (attempt === delaysMs.length) break;
6214
6223
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6215
6224
  }
6225
+ this.settingsStoreReady = false;
6216
6226
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6217
6227
  return {};
6218
6228
  }
@@ -8020,6 +8030,15 @@ var LabelDefinitionSchema = object({
8020
8030
  description: string().optional(),
8021
8031
  icon: string().optional()
8022
8032
  });
8033
+ var ClassMapDefinitionSchema = object({
8034
+ mapping: record(string(), _enum([
8035
+ "person",
8036
+ "vehicle",
8037
+ "animal",
8038
+ "package"
8039
+ ])),
8040
+ preserveOriginal: boolean()
8041
+ });
8023
8042
  var MODEL_FORMATS = [
8024
8043
  "onnx",
8025
8044
  "coreml",
@@ -8103,6 +8122,12 @@ var ModelVariantGroupSchema = object({
8103
8122
  */
8104
8123
  resolution: number().int().positive().optional()
8105
8124
  });
8125
+ var ModelProviderIdSchema = _enum([
8126
+ "camstack",
8127
+ "frigate",
8128
+ "scrypted",
8129
+ "custom"
8130
+ ]);
8106
8131
  var ModelCatalogEntrySchema = object({
8107
8132
  id: string(),
8108
8133
  name: string(),
@@ -8198,7 +8223,19 @@ var ModelCatalogEntrySchema = object({
8198
8223
  * `id` stays the source of truth for resolution/download/persistence; grouping
8199
8224
  * is a presentation overlay resolved back to an `id`.
8200
8225
  */
8201
- group: ModelVariantGroupSchema.optional()
8226
+ group: ModelVariantGroupSchema.optional(),
8227
+ /**
8228
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8229
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8230
+ * persisted before this field existed (`inferModelProvider` fills those).
8231
+ */
8232
+ provider: ModelProviderIdSchema.optional(),
8233
+ /**
8234
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8235
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8236
+ * labels already ARE the CamStack macros (Scrypted identity map).
8237
+ */
8238
+ classMap: ClassMapDefinitionSchema.optional()
8202
8239
  });
8203
8240
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8204
8241
  format: literal("openvino"),
@@ -8227,7 +8264,8 @@ var ModelConvertMetadataSchema = object({
8227
8264
  "ocr",
8228
8265
  "segmentation"
8229
8266
  ]),
8230
- faceAlignment: boolean().optional()
8267
+ faceAlignment: boolean().optional(),
8268
+ classMap: ClassMapDefinitionSchema.optional()
8231
8269
  });
8232
8270
  var ConvertResultSchema = object({
8233
8271
  entry: ModelCatalogEntrySchema,
@@ -11775,6 +11813,27 @@ var LinkedDeviceSchema = object({
11775
11813
  features: array(string()),
11776
11814
  producesTrackedEvents: boolean().optional()
11777
11815
  });
11816
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
11817
+ * The batch answer needs the tag; the single-device answer already has it
11818
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
11819
+ var LinkedDevicesForDeviceSchema = object({
11820
+ deviceId: number(),
11821
+ mode: LinkedDevicesModeSchema,
11822
+ devices: array(LinkedDeviceSchema)
11823
+ });
11824
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
11825
+ * `getAllBindings` all answer in. Declared once: three copies of the same
11826
+ * object literal is exactly how the three drift apart. */
11827
+ var DeviceBindingsForDeviceSchema = object({
11828
+ deviceId: number(),
11829
+ entries: array(object({
11830
+ capName: string(),
11831
+ kind: _enum(["native", "wrapped"]),
11832
+ providerAddonId: string(),
11833
+ providerNodeId: string(),
11834
+ nativeAddonId: string()
11835
+ }))
11836
+ });
11778
11837
  var SavedDeviceRowSchema = object({
11779
11838
  /** Numeric id reserved at allocateDeviceId time. */
11780
11839
  id: number(),
@@ -12000,11 +12059,25 @@ method(object({
12000
12059
  projection: _enum(["full", "slim"]).optional(),
12001
12060
  /** Return only camera devices. Filtering server-side instead of
12002
12061
  * shipping 293 rows to find 12. */
12003
- isCamera: boolean().optional()
12062
+ isCamera: boolean().optional(),
12063
+ /**
12064
+ * Return only these device ids. For the caller that already KNOWS the
12065
+ * handful it wants and needs a field the id-bearing answer does not
12066
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12067
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12068
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12069
+ * refetches on the reconcile interval, on a phone.
12070
+ *
12071
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12072
+ * keys rather than rejecting them (verified against the live hub
12073
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12074
+ * it answers today and the caller filters as it already does.
12075
+ */
12076
+ deviceIds: array(number()).optional()
12004
12077
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12005
12078
  mode: LinkedDevicesModeSchema,
12006
12079
  devices: array(LinkedDeviceSchema)
12007
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12080
+ })), method(object({ deviceIds: array(number()) }), array(LinkedDevicesForDeviceSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12008
12081
  deviceId: number(),
12009
12082
  values: record(string(), unknown())
12010
12083
  }), object({ success: literal(true) }), {
@@ -12031,25 +12104,7 @@ method(object({
12031
12104
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
12032
12105
  kind: "mutation",
12033
12106
  auth: "admin"
12034
- }), method(object({ deviceId: number() }), object({
12035
- deviceId: number(),
12036
- entries: array(object({
12037
- capName: string(),
12038
- kind: _enum(["native", "wrapped"]),
12039
- providerAddonId: string(),
12040
- providerNodeId: string(),
12041
- nativeAddonId: string()
12042
- }))
12043
- })), method(object({}), array(object({
12044
- deviceId: number(),
12045
- entries: array(object({
12046
- capName: string(),
12047
- kind: _enum(["native", "wrapped"]),
12048
- providerAddonId: string(),
12049
- providerNodeId: string(),
12050
- nativeAddonId: string()
12051
- }))
12052
- }))), method(object({
12107
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12053
12108
  deviceId: number(),
12054
12109
  capName: string(),
12055
12110
  wrapperAddonId: string(),
@@ -14421,12 +14476,15 @@ var NcOccupancyConditionSchema = object({
14421
14476
  * there is no second switch that can disagree with the first and every rule
14422
14477
  * authored before the decision migrates for free (`audioModeOf`):
14423
14478
  *
14424
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14425
- * classifier labels with one of them. No window, no percentage:
14426
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14427
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14428
- * the analyzer's (`classificationMinScore`, per device) a label only
14429
- * reaches this condition if the classifier was already confident enough.
14479
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14480
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14481
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14482
+ * frames is the wrong question for a classifier that labels 1–3 frames
14483
+ * per episode. The count window is the brake that drops a single-frame
14484
+ * false positive; the rule's own `throttle` cooldown is the other. The
14485
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14486
+ * per device) — a label only reaches this condition if the classifier was
14487
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14430
14488
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14431
14489
  * the condition: at least `hitPercent`% of the samples over
14432
14490
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14453,14 +14511,22 @@ var NcOccupancyConditionSchema = object({
14453
14511
  * an operator who typed `dog` mean the same thing.
14454
14512
  */
14455
14513
  var NcAudioConditionSchema = object({
14456
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14514
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14457
14515
  labels: array(string().min(1)).min(1).optional(),
14458
14516
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14459
14517
  dbThreshold: number().min(-96).max(0).optional(),
14460
14518
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14461
14519
  hitPercent: number().int().min(1).max(100).default(60),
14462
14520
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14463
- samplingSeconds: number().int().min(1).max(300).default(10)
14521
+ samplingSeconds: number().int().min(1).max(300).default(10),
14522
+ /**
14523
+ * LABEL MODE: how many labelled frames must land inside
14524
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14525
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14526
+ */
14527
+ confirmHits: number().int().min(1).max(20).optional(),
14528
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14529
+ confirmWindowSec: number().int().min(1).max(60).optional()
14464
14530
  });
14465
14531
  /**
14466
14532
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -16834,6 +16900,46 @@ var RecentTracksPageSchema = object({
16834
16900
  /** Cursor for the next page, or null when this page is the last. */
16835
16901
  nextCursor: string().nullable()
16836
16902
  });
16903
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
16904
+ var LIST_GROUPS_MAX_LIMIT = 100;
16905
+ var AnalyticsGroupRecordSchema = object({
16906
+ id: string(),
16907
+ deviceId: number().int(),
16908
+ openedAt: number().int(),
16909
+ closedAt: number().int(),
16910
+ timestamp: number().int(),
16911
+ memberCount: number().int(),
16912
+ memberTrackIds: array(string()).readonly(),
16913
+ className: string(),
16914
+ classes: array(string()).readonly(),
16915
+ /** Relative event-media path, or null when the group has no picture yet. */
16916
+ mediaUrl: string().nullable(),
16917
+ singleton: boolean()
16918
+ });
16919
+ var AnalyticsGroupMemberSchema = object({
16920
+ trackId: string(),
16921
+ deviceId: number().int(),
16922
+ className: string(),
16923
+ firstSeen: number().int(),
16924
+ lastSeen: number().int(),
16925
+ mediaUrl: string().nullable()
16926
+ });
16927
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
16928
+ var ListGroupsQueryInput = object({
16929
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
16930
+ deviceIds: array(number()),
16931
+ /** Window lower bound on `closedAt` (inclusive). */
16932
+ since: number().optional(),
16933
+ /** Window upper bound on `openedAt` (inclusive). */
16934
+ until: number().optional(),
16935
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
16936
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
16937
+ cursor: string().optional()
16938
+ });
16939
+ var ListGroupsPageSchema = object({
16940
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
16941
+ nextCursor: string().nullable()
16942
+ });
16837
16943
  var KeyEventQueryInput = object({
16838
16944
  deviceId: number(),
16839
16945
  /** Window lower bound (track firstSeen ≥ since). */
@@ -16909,7 +17015,9 @@ var TrackCascadeCountsSchema = object({
16909
17015
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
16910
17016
  plates: number().int(),
16911
17017
  /** Per-track CLIP search vectors removed (best-effort). */
16912
- embeddings: number().int()
17018
+ embeddings: number().int(),
17019
+ /** Group membership + group rows removed with their last member (best-effort). */
17020
+ groups: number().int()
16913
17021
  });
16914
17022
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
16915
17023
  var DiskReconcileCountsSchema = object({
@@ -17055,7 +17163,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17055
17163
  * stationary registry). Default false: the timeline lists passages,
17056
17164
  * not parking records (operator decision, 2026-08-15). */
17057
17165
  includeStationary: boolean().optional()
17058
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17166
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17167
+ deviceId: number(),
17168
+ groupId: string().min(1)
17169
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17059
17170
  kind: "mutation",
17060
17171
  auth: "admin"
17061
17172
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -17273,6 +17384,33 @@ var NativeCropRefSchema = object({
17273
17384
  h: number()
17274
17385
  })
17275
17386
  });
17387
+ object({
17388
+ crop: object({
17389
+ left: number(),
17390
+ top: number(),
17391
+ width: number().positive(),
17392
+ height: number().positive()
17393
+ }).optional(),
17394
+ content: object({
17395
+ width: number().int().positive(),
17396
+ height: number().int().positive()
17397
+ }),
17398
+ fit: _enum(["stretch", "contain"]),
17399
+ format: _enum([
17400
+ "rgb",
17401
+ "gray",
17402
+ "jpeg"
17403
+ ])
17404
+ });
17405
+ var FrameRefSchema = object({
17406
+ registryId: string().min(1),
17407
+ id: string().min(1),
17408
+ width: number().int().positive(),
17409
+ height: number().int().positive(),
17410
+ format: _enum(["rgb", "gray"]),
17411
+ timestamp: number(),
17412
+ capturedAt: number().optional()
17413
+ });
17276
17414
  var ModelFormatSchema$1 = _enum([
17277
17415
  "onnx",
17278
17416
  "coreml",
@@ -17338,7 +17476,8 @@ var PipelineModelOptionSchema = object({
17338
17476
  sizeMB: number()
17339
17477
  })),
17340
17478
  group: ModelVariantGroupSchema.optional(),
17341
- legacy: boolean().optional()
17479
+ legacy: boolean().optional(),
17480
+ provider: ModelProviderIdSchema.optional()
17342
17481
  });
17343
17482
  var ConfigFieldBridge = custom();
17344
17483
  var PipelineAddonSchemaSchema = object({
@@ -17517,6 +17656,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
17517
17656
  steps: array(PipelineStepInputSchema).min(1),
17518
17657
  frame: FrameInputSchema.optional(),
17519
17658
  /**
17659
+ * Process-local lazy frame. Valid only when caller and provider resolve
17660
+ * in the same execution-group process; split/cross-node callers use
17661
+ * `frame`/`image` inline compatibility instead.
17662
+ */
17663
+ frameRef: FrameRefSchema.optional(),
17664
+ /**
17520
17665
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
17521
17666
  * the decoded pixels live in. One more member of the one-of
17522
17667
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -17772,7 +17917,10 @@ var NativeCropResultSchema = object({
17772
17917
  * Which source served this crop, so a quality-sensitive consumer (the native
17773
17918
  * `keyFrame`) can reject a degraded fallback:
17774
17919
  * - `native` — cut from the decode worker's retained NATIVE surface (the
17775
- * quality path).
17920
+ * quality path). A subject-tile serve is also native-resolution and stays
17921
+ * `native` here: the public enum cannot name `tile` without a breaking cap
17922
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
17923
+ * internal crop result (`nativeHits` vs `tileHits`).
17776
17924
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
17777
17925
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
17778
17926
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -18263,12 +18411,41 @@ var RunnerLocalLoadSchema = object({
18263
18411
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
18264
18412
  * working unchanged when they switch to reading from the runner cap.
18265
18413
  */
18414
+ var FrameLazyCountersSchema = object({
18415
+ framesDecoded: number(),
18416
+ framesAdmitted: number(),
18417
+ framesDroppedPixelFree: number(),
18418
+ viewsMaterialized: number(),
18419
+ viewsSkipped: number(),
18420
+ workerToRunnerBytes: number(),
18421
+ runnerToPoolRawBytes: number(),
18422
+ runnerToPoolJpegBytes: number(),
18423
+ onDemandFullFrameRequests: number(),
18424
+ onDemandCropRequests: number(),
18425
+ nativeHits: number(),
18426
+ nativeMisses: number(),
18427
+ tileHits: number(),
18428
+ tileMisses: number(),
18429
+ fallbackHits: number(),
18430
+ fallbackMisses: number(),
18431
+ retainedWritesAvoided: number(),
18432
+ residentRefs: number(),
18433
+ residentBytes: number(),
18434
+ releases: number(),
18435
+ evictions: number(),
18436
+ staleMisses: number()
18437
+ });
18438
+ var FrameLazyMetricsSchema = object({
18439
+ node: FrameLazyCountersSchema,
18440
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
18441
+ });
18266
18442
  var RunnerLocalMetricsSchema = object({
18267
18443
  nodeId: string(),
18268
18444
  activeCameras: number(),
18269
18445
  throttledCameras: number(),
18270
18446
  avgInferenceTimeMs: number(),
18271
- queueDepth: number()
18447
+ queueDepth: number(),
18448
+ frameLazy: FrameLazyMetricsSchema.optional()
18272
18449
  });
18273
18450
  method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly()), method(object({
18274
18451
  handle: FrameHandleSchema,
@@ -19672,6 +19849,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
19672
19849
  location: StorageLocationSchema,
19673
19850
  relativePath: string()
19674
19851
  }), _void(), { kind: "mutation" }), method(object({ location: StorageLocationSchema }), number().nullable()), method(BeginUploadInputSchema, BeginUploadResultSchema, { kind: "mutation" }), method(WriteChunkInputSchema, _void(), { kind: "mutation" }), method(FinalizeUploadInputSchema, _void(), { kind: "mutation" }), method(AbortUploadInputSchema, _void(), { kind: "mutation" }), method(BeginDownloadInputSchema, BeginDownloadResultSchema, { kind: "mutation" }), method(ReadChunkInputSchema, _instanceof(Uint8Array)), method(EndDownloadInputSchema, _void(), { kind: "mutation" });
19852
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
19853
+ var ProfileSettingsSchemaBridge = unknown().nullable();
19854
+ var ProfileSettingsBagSchema = record(string(), unknown());
19675
19855
  /**
19676
19856
  * A live terminal session hosted by the provider addon. Output and input do
19677
19857
  * NOT flow through the capability — they use the addon data plane
@@ -19701,7 +19881,14 @@ var TerminalSessionInfoSchema = object({
19701
19881
  var TerminalProfileInfoSchema = object({
19702
19882
  profileId: string(),
19703
19883
  label: string(),
19704
- description: string().optional()
19884
+ description: string().optional(),
19885
+ /** Spawn defaults the instance form copies on create. */
19886
+ executable: string().optional(),
19887
+ args: array(string()).readonly().optional(),
19888
+ cwd: string().optional(),
19889
+ environment: array(string()).readonly().optional(),
19890
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
19891
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
19705
19892
  });
19706
19893
  /**
19707
19894
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -19714,7 +19901,12 @@ var TerminalInstanceInfoSchema = object({
19714
19901
  profileId: string(),
19715
19902
  profileLabel: string(),
19716
19903
  name: string(),
19717
- enabled: boolean()
19904
+ enabled: boolean(),
19905
+ executable: string(),
19906
+ args: array(string()).readonly(),
19907
+ cwd: string(),
19908
+ environment: array(string()).readonly(),
19909
+ profileSettings: ProfileSettingsBagSchema
19718
19910
  });
19719
19911
  var TerminalLegacyCameraSchema = object({
19720
19912
  stableId: string(),
@@ -19744,7 +19936,23 @@ var TerminalOutputBatchSchema = object({
19744
19936
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
19745
19937
  targetNodeId: string().min(1),
19746
19938
  profileId: string().min(1),
19747
- name: string().trim().min(1).max(160).optional()
19939
+ name: string().trim().min(1).max(160).optional(),
19940
+ executable: string().max(1024).optional(),
19941
+ args: array(string().max(2048)).max(64).optional(),
19942
+ cwd: string().max(1024).optional(),
19943
+ environment: array(string().max(4096)).max(64).optional(),
19944
+ profileSettings: ProfileSettingsBagSchema.optional()
19945
+ }), TerminalInstanceInfoSchema, {
19946
+ kind: "mutation",
19947
+ auth: "admin"
19948
+ }), method(object({
19949
+ instanceId: string().min(1),
19950
+ name: string().trim().min(1).max(160).optional(),
19951
+ executable: string().max(1024).optional(),
19952
+ args: array(string().max(2048)).max(64).optional(),
19953
+ cwd: string().max(1024).optional(),
19954
+ environment: array(string().max(4096)).max(64).optional(),
19955
+ profileSettings: ProfileSettingsBagSchema.optional()
19748
19956
  }), TerminalInstanceInfoSchema, {
19749
19957
  kind: "mutation",
19750
19958
  auth: "admin"
@@ -19766,7 +19974,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
19766
19974
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
19767
19975
  profileId: string(),
19768
19976
  cols: number().int().positive(),
19769
- rows: number().int().positive()
19977
+ rows: number().int().positive(),
19978
+ executable: string().max(1024).optional(),
19979
+ args: array(string().max(2048)).max(64).optional(),
19980
+ cwd: string().max(1024).optional(),
19981
+ environment: array(string().max(4096)).max(64).optional()
19770
19982
  }), TerminalSessionInfoSchema, {
19771
19983
  kind: "mutation",
19772
19984
  auth: "admin"
@@ -22548,10 +22760,10 @@ DeviceType.LawnMower, method(object({ deviceId: number().int().nonnegative() }),
22548
22760
  *
22549
22761
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
22550
22762
  * to receive an ordered list of candidate base URLs it should race
22551
- * on connect — LAN IPv4 first (lowest latency when on same network),
22552
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
22553
- * race them with short timeouts and stick with the winner for the
22554
- * session.
22763
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
22764
+ * when on the same network), then public hostname (if a tunnel is
22765
+ * up). The SDK can race them with short timeouts and stick with the
22766
+ * winner for the session.
22555
22767
  *
22556
22768
  * Why hub-only: agents are not directly addressable by the operator's
22557
22769
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -22706,6 +22918,17 @@ var NotificationEndpointSchema = object({
22706
22918
  /** What the ranking currently resolves to (null when nothing is reachable). */
22707
22919
  resolved: string().nullable()
22708
22920
  });
22921
+ /**
22922
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
22923
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
22924
+ * currently expands to, so the UI can show the effective set either way.
22925
+ */
22926
+ var ViewerEndpointsSchema = object({
22927
+ /** The operator's explicit race set, or empty for AUTO. */
22928
+ baseUrls: array(string()).readonly(),
22929
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
22930
+ resolved: array(string()).readonly()
22931
+ });
22709
22932
  var AllowedAddressesSchema = object({
22710
22933
  /**
22711
22934
  * Allowlist of interface addresses operators have explicitly opted
@@ -22714,6 +22937,20 @@ var AllowedAddressesSchema = object({
22714
22937
  * Network Addresses admin page and persisted by the addon.
22715
22938
  */
22716
22939
  addresses: array(string()).readonly() });
22940
+ var TlsStatusSchema = object({
22941
+ mode: _enum([
22942
+ "generated",
22943
+ "uploaded",
22944
+ "disabled"
22945
+ ]),
22946
+ leafFingerprintSha256: string().nullable(),
22947
+ caFingerprintSha256: string().nullable(),
22948
+ validTo: string().nullable(),
22949
+ sans: array(string()),
22950
+ caCertPem: string().nullable(),
22951
+ reissueError: string().nullable(),
22952
+ restartRequired: boolean()
22953
+ });
22717
22954
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
22718
22955
  /**
22719
22956
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -22723,17 +22960,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
22723
22960
  */
22724
22961
  port: number().int().min(1).max(65535).optional(),
22725
22962
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
22726
- * candidate. Default `true`. */
22963
+ * candidate. Default `false` — loopback is not a client route. */
22727
22964
  includeLoopback: boolean().optional(),
22728
- /** Skip IPv6 entries. Some legacy clients can't parse them.
22729
- * Default `false`. */
22965
+ /** Skip IPv6 entries. Default `false` the palette includes stable
22966
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
22967
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
22730
22968
  ipv4Only: boolean().optional(),
22731
22969
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
22732
22970
  * Pass `'https'` when the caller is itself loaded over HTTPS
22733
22971
  * to avoid mixed-content blocks in the browser. The public
22734
22972
  * tunnel always emits `https://` regardless. */
22735
22973
  scheme: _enum(["http", "https"]).optional()
22736
- }), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" });
22974
+ }), 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, {
22975
+ kind: "mutation",
22976
+ auth: "admin"
22977
+ }), method(object({
22978
+ certPem: string().min(1),
22979
+ keyPem: string().min(1),
22980
+ caPem: string().optional()
22981
+ }), TlsStatusSchema, {
22982
+ kind: "mutation",
22983
+ auth: "admin"
22984
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
22985
+ kind: "mutation",
22986
+ auth: "admin"
22987
+ });
22737
22988
  object({
22738
22989
  /** Lifecycle state of the lock. `jammed` means the motor reported
22739
22990
  * failure to reach the target — operator intervention required. */
@@ -23914,7 +24165,12 @@ var PlateInfoSchema = object({
23914
24165
  plateBbox: BoundingBoxSchema.optional(),
23915
24166
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
23916
24167
  keyFrameMediaKey: string().optional(),
23917
- base64: string().optional()
24168
+ base64: string().optional(),
24169
+ /**
24170
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
24171
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
24172
+ */
24173
+ cropUrl: string().optional()
23918
24174
  });
23919
24175
  var MediaFileLiteSchema = object({
23920
24176
  key: string(),
@@ -28006,6 +28262,12 @@ Object.freeze({
28006
28262
  addonId: null,
28007
28263
  access: "view"
28008
28264
  },
28265
+ "deviceManager.getBindingsBatch": {
28266
+ capName: "device-manager",
28267
+ capScope: "system",
28268
+ addonId: null,
28269
+ access: "view"
28270
+ },
28009
28271
  "deviceManager.getChildren": {
28010
28272
  capName: "device-manager",
28011
28273
  capScope: "system",
@@ -28066,6 +28328,12 @@ Object.freeze({
28066
28328
  addonId: null,
28067
28329
  access: "view"
28068
28330
  },
28331
+ "deviceManager.getLinkedDevicesBatch": {
28332
+ capName: "device-manager",
28333
+ capScope: "system",
28334
+ addonId: null,
28335
+ access: "view"
28336
+ },
28069
28337
  "deviceManager.getRoleDisplayDefaults": {
28070
28338
  capName: "device-manager",
28071
28339
  capScope: "system",
@@ -28948,6 +29216,12 @@ Object.freeze({
28948
29216
  addonId: null,
28949
29217
  access: "create"
28950
29218
  },
29219
+ "localNetwork.downloadCa": {
29220
+ capName: "local-network",
29221
+ capScope: "system",
29222
+ addonId: null,
29223
+ access: "view"
29224
+ },
28951
29225
  "localNetwork.getAllowedAddresses": {
28952
29226
  capName: "local-network",
28953
29227
  capScope: "system",
@@ -28972,18 +29246,42 @@ Object.freeze({
28972
29246
  addonId: null,
28973
29247
  access: "view"
28974
29248
  },
29249
+ "localNetwork.getTlsStatus": {
29250
+ capName: "local-network",
29251
+ capScope: "system",
29252
+ addonId: null,
29253
+ access: "view"
29254
+ },
29255
+ "localNetwork.getViewerEndpoints": {
29256
+ capName: "local-network",
29257
+ capScope: "system",
29258
+ addonId: null,
29259
+ access: "view"
29260
+ },
28975
29261
  "localNetwork.list": {
28976
29262
  capName: "local-network",
28977
29263
  capScope: "system",
28978
29264
  addonId: null,
28979
29265
  access: "view"
28980
29266
  },
29267
+ "localNetwork.regenerateCertificate": {
29268
+ capName: "local-network",
29269
+ capScope: "system",
29270
+ addonId: null,
29271
+ access: "create"
29272
+ },
28981
29273
  "localNetwork.resetAllowlistToBestMatch": {
28982
29274
  capName: "local-network",
28983
29275
  capScope: "system",
28984
29276
  addonId: null,
28985
29277
  access: "delete"
28986
29278
  },
29279
+ "localNetwork.revertToGeneratedCertificate": {
29280
+ capName: "local-network",
29281
+ capScope: "system",
29282
+ addonId: null,
29283
+ access: "create"
29284
+ },
28987
29285
  "localNetwork.setAllowedAddresses": {
28988
29286
  capName: "local-network",
28989
29287
  capScope: "system",
@@ -28996,6 +29294,18 @@ Object.freeze({
28996
29294
  addonId: null,
28997
29295
  access: "create"
28998
29296
  },
29297
+ "localNetwork.setViewerEndpoints": {
29298
+ capName: "local-network",
29299
+ capScope: "system",
29300
+ addonId: null,
29301
+ access: "create"
29302
+ },
29303
+ "localNetwork.uploadCertificate": {
29304
+ capName: "local-network",
29305
+ capScope: "system",
29306
+ addonId: null,
29307
+ access: "create"
29308
+ },
28999
29309
  "lockControl.lock": {
29000
29310
  capName: "lock-control",
29001
29311
  capScope: "device",
@@ -29794,6 +30104,12 @@ Object.freeze({
29794
30104
  addonId: null,
29795
30105
  access: "view"
29796
30106
  },
30107
+ "pipelineAnalytics.getGroup": {
30108
+ capName: "pipeline-analytics",
30109
+ capScope: "device",
30110
+ addonId: null,
30111
+ access: "view"
30112
+ },
29797
30113
  "pipelineAnalytics.getKeyEvents": {
29798
30114
  capName: "pipeline-analytics",
29799
30115
  capScope: "device",
@@ -29878,6 +30194,12 @@ Object.freeze({
29878
30194
  addonId: null,
29879
30195
  access: "view"
29880
30196
  },
30197
+ "pipelineAnalytics.listGroups": {
30198
+ capName: "pipeline-analytics",
30199
+ capScope: "device",
30200
+ addonId: null,
30201
+ access: "view"
30202
+ },
29881
30203
  "pipelineAnalytics.listOpsLog": {
29882
30204
  capName: "pipeline-analytics",
29883
30205
  capScope: "device",
@@ -31876,6 +32198,12 @@ Object.freeze({
31876
32198
  addonId: null,
31877
32199
  access: "create"
31878
32200
  },
32201
+ "terminalSession.updateInstance": {
32202
+ capName: "terminal-session",
32203
+ capScope: "system",
32204
+ addonId: null,
32205
+ access: "create"
32206
+ },
31879
32207
  "terminalSession.writeInput": {
31880
32208
  capName: "terminal-session",
31881
32209
  capScope: "system",
@@ -32653,6 +32981,11 @@ Object.freeze({
32653
32981
  form: "single",
32654
32982
  optional: false
32655
32983
  }],
32984
+ "deviceManager.getBindingsBatch": [{
32985
+ name: "deviceIds",
32986
+ form: "array",
32987
+ optional: false
32988
+ }],
32656
32989
  "deviceManager.getChildren": [{
32657
32990
  name: "parentDeviceId",
32658
32991
  form: "single",
@@ -32698,6 +33031,11 @@ Object.freeze({
32698
33031
  form: "single",
32699
33032
  optional: false
32700
33033
  }],
33034
+ "deviceManager.getLinkedDevicesBatch": [{
33035
+ name: "deviceIds",
33036
+ form: "array",
33037
+ optional: false
33038
+ }],
32701
33039
  "deviceManager.getSettingsSchema": [{
32702
33040
  name: "deviceId",
32703
33041
  form: "single",
@@ -32718,6 +33056,11 @@ Object.freeze({
32718
33056
  form: "single",
32719
33057
  optional: false
32720
33058
  }],
33059
+ "deviceManager.listAll": [{
33060
+ name: "deviceIds",
33061
+ form: "array",
33062
+ optional: true
33063
+ }],
32721
33064
  "deviceManager.loadConfig": [{
32722
33065
  name: "deviceId",
32723
33066
  form: "single",
@@ -33291,6 +33634,11 @@ Object.freeze({
33291
33634
  form: "single",
33292
33635
  optional: false
33293
33636
  }],
33637
+ "pipelineAnalytics.getGroup": [{
33638
+ name: "deviceId",
33639
+ form: "single",
33640
+ optional: false
33641
+ }],
33294
33642
  "pipelineAnalytics.getKeyEvents": [{
33295
33643
  name: "deviceId",
33296
33644
  form: "single",
@@ -33346,6 +33694,11 @@ Object.freeze({
33346
33694
  form: "array",
33347
33695
  optional: false
33348
33696
  }],
33697
+ "pipelineAnalytics.listGroups": [{
33698
+ name: "deviceIds",
33699
+ form: "array",
33700
+ optional: false
33701
+ }],
33349
33702
  "pipelineAnalytics.listOpsLog": [{
33350
33703
  name: "deviceId",
33351
33704
  form: "single",
@@ -34363,6 +34716,35 @@ Object.freeze(Object.fromEntries([{
34363
34716
  }]
34364
34717
  }].map((s) => [s.stepId, s.defaultModelId])));
34365
34718
  string().min(1);
34719
+ var CLUSTER_STEP_SETTING_FIELDS = [{
34720
+ stepId: "face-embedding",
34721
+ key: "minLandmarkFaceSize",
34722
+ label: "Min face size for recognition (detection px)",
34723
+ description: "Refuse to embed a face smaller than this IN THE DETECTION FRAME. Applies to every node — the enrolled gallery is one index, and two admission floors would fill it from two policies. 0 disables the gate.",
34724
+ type: "slider",
34725
+ min: 0,
34726
+ max: 64,
34727
+ step: 2,
34728
+ default: 24
34729
+ }];
34730
+ function clusterStepSettingKey(stepId, fieldKey) {
34731
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
34732
+ }
34733
+ var ClusterSettingNumberSchema = number().finite();
34734
+ function readClusterStepSettings(config) {
34735
+ const out = {};
34736
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
34737
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
34738
+ const value = parsed.success ? parsed.data : field.default;
34739
+ const existing = out[field.stepId] ?? {};
34740
+ out[field.stepId] = {
34741
+ ...existing,
34742
+ [field.key]: value
34743
+ };
34744
+ }
34745
+ return out;
34746
+ }
34747
+ readClusterStepSettings({});
34366
34748
  object({
34367
34749
  /**
34368
34750
  * Fraction of the box's own size added on EACH side before cutting.