@camstack/addon-provider-hikvision 1.2.33 → 1.2.35

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.js CHANGED
@@ -5808,6 +5808,13 @@ var BaseAddon = class {
5808
5808
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5809
5809
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5810
5810
  _registeredCapNames = [];
5811
+ /**
5812
+ * True only after `readAddonStore` actually answered. Constructor
5813
+ * defaults look like stored config when the store is down — a forked
5814
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5815
+ * mode, 2026-08-25) is not "the operator chose this".
5816
+ */
5817
+ settingsStoreReady = false;
5811
5818
  /** Default config values. Provided via constructor. */
5812
5819
  defaults;
5813
5820
  constructor(defaults) {
@@ -6208,7 +6215,9 @@ var BaseAddon = class {
6208
6215
  ];
6209
6216
  let lastErr;
6210
6217
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6211
- return await settings.readAddonStore() ?? {};
6218
+ const stored = await settings.readAddonStore() ?? {};
6219
+ this.settingsStoreReady = true;
6220
+ return stored;
6212
6221
  } catch (err) {
6213
6222
  lastErr = err;
6214
6223
  const msg = err instanceof Error ? err.message : String(err);
@@ -6216,6 +6225,7 @@ var BaseAddon = class {
6216
6225
  if (attempt === delaysMs.length) break;
6217
6226
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6218
6227
  }
6228
+ this.settingsStoreReady = false;
6219
6229
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6220
6230
  return {};
6221
6231
  }
@@ -8190,6 +8200,15 @@ var LabelDefinitionSchema = object({
8190
8200
  description: string().optional(),
8191
8201
  icon: string().optional()
8192
8202
  });
8203
+ var ClassMapDefinitionSchema = object({
8204
+ mapping: record(string(), _enum([
8205
+ "person",
8206
+ "vehicle",
8207
+ "animal",
8208
+ "package"
8209
+ ])),
8210
+ preserveOriginal: boolean()
8211
+ });
8193
8212
  var MODEL_FORMATS = [
8194
8213
  "onnx",
8195
8214
  "coreml",
@@ -8273,6 +8292,12 @@ var ModelVariantGroupSchema = object({
8273
8292
  */
8274
8293
  resolution: number().int().positive().optional()
8275
8294
  });
8295
+ var ModelProviderIdSchema = _enum([
8296
+ "camstack",
8297
+ "frigate",
8298
+ "scrypted",
8299
+ "custom"
8300
+ ]);
8276
8301
  var ModelCatalogEntrySchema = object({
8277
8302
  id: string(),
8278
8303
  name: string(),
@@ -8368,7 +8393,19 @@ var ModelCatalogEntrySchema = object({
8368
8393
  * `id` stays the source of truth for resolution/download/persistence; grouping
8369
8394
  * is a presentation overlay resolved back to an `id`.
8370
8395
  */
8371
- group: ModelVariantGroupSchema.optional()
8396
+ group: ModelVariantGroupSchema.optional(),
8397
+ /**
8398
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8399
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8400
+ * persisted before this field existed (`inferModelProvider` fills those).
8401
+ */
8402
+ provider: ModelProviderIdSchema.optional(),
8403
+ /**
8404
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8405
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8406
+ * labels already ARE the CamStack macros (Scrypted identity map).
8407
+ */
8408
+ classMap: ClassMapDefinitionSchema.optional()
8372
8409
  });
8373
8410
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8374
8411
  format: literal("openvino"),
@@ -8397,7 +8434,8 @@ var ModelConvertMetadataSchema = object({
8397
8434
  "ocr",
8398
8435
  "segmentation"
8399
8436
  ]),
8400
- faceAlignment: boolean().optional()
8437
+ faceAlignment: boolean().optional(),
8438
+ classMap: ClassMapDefinitionSchema.optional()
8401
8439
  });
8402
8440
  var ConvertResultSchema = object({
8403
8441
  entry: ModelCatalogEntrySchema,
@@ -12166,6 +12204,27 @@ var LinkedDeviceSchema = object({
12166
12204
  features: array(string()),
12167
12205
  producesTrackedEvents: boolean().optional()
12168
12206
  });
12207
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
12208
+ * The batch answer needs the tag; the single-device answer already has it
12209
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
12210
+ var LinkedDevicesForDeviceSchema = object({
12211
+ deviceId: number(),
12212
+ mode: LinkedDevicesModeSchema,
12213
+ devices: array(LinkedDeviceSchema)
12214
+ });
12215
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
12216
+ * `getAllBindings` all answer in. Declared once: three copies of the same
12217
+ * object literal is exactly how the three drift apart. */
12218
+ var DeviceBindingsForDeviceSchema = object({
12219
+ deviceId: number(),
12220
+ entries: array(object({
12221
+ capName: string(),
12222
+ kind: _enum(["native", "wrapped"]),
12223
+ providerAddonId: string(),
12224
+ providerNodeId: string(),
12225
+ nativeAddonId: string()
12226
+ }))
12227
+ });
12169
12228
  var SavedDeviceRowSchema = object({
12170
12229
  /** Numeric id reserved at allocateDeviceId time. */
12171
12230
  id: number(),
@@ -12391,11 +12450,25 @@ method(object({
12391
12450
  projection: _enum(["full", "slim"]).optional(),
12392
12451
  /** Return only camera devices. Filtering server-side instead of
12393
12452
  * shipping 293 rows to find 12. */
12394
- isCamera: boolean().optional()
12453
+ isCamera: boolean().optional(),
12454
+ /**
12455
+ * Return only these device ids. For the caller that already KNOWS the
12456
+ * handful it wants and needs a field the id-bearing answer does not
12457
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12458
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12459
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12460
+ * refetches on the reconcile interval, on a phone.
12461
+ *
12462
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12463
+ * keys rather than rejecting them (verified against the live hub
12464
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12465
+ * it answers today and the caller filters as it already does.
12466
+ */
12467
+ deviceIds: array(number()).optional()
12395
12468
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12396
12469
  mode: LinkedDevicesModeSchema,
12397
12470
  devices: array(LinkedDeviceSchema)
12398
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12471
+ })), 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({
12399
12472
  deviceId: number(),
12400
12473
  values: record(string(), unknown())
12401
12474
  }), object({ success: literal(true) }), {
@@ -12422,25 +12495,7 @@ method(object({
12422
12495
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
12423
12496
  kind: "mutation",
12424
12497
  auth: "admin"
12425
- }), method(object({ deviceId: number() }), object({
12426
- deviceId: number(),
12427
- entries: array(object({
12428
- capName: string(),
12429
- kind: _enum(["native", "wrapped"]),
12430
- providerAddonId: string(),
12431
- providerNodeId: string(),
12432
- nativeAddonId: string()
12433
- }))
12434
- })), method(object({}), array(object({
12435
- deviceId: number(),
12436
- entries: array(object({
12437
- capName: string(),
12438
- kind: _enum(["native", "wrapped"]),
12439
- providerAddonId: string(),
12440
- providerNodeId: string(),
12441
- nativeAddonId: string()
12442
- }))
12443
- }))), method(object({
12498
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12444
12499
  deviceId: number(),
12445
12500
  capName: string(),
12446
12501
  wrapperAddonId: string(),
@@ -14850,12 +14905,15 @@ var NcOccupancyConditionSchema = object({
14850
14905
  * there is no second switch that can disagree with the first and every rule
14851
14906
  * authored before the decision migrates for free (`audioModeOf`):
14852
14907
  *
14853
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14854
- * classifier labels with one of them. No window, no percentage:
14855
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14856
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14857
- * the analyzer's (`classificationMinScore`, per device) a label only
14858
- * reaches this condition if the classifier was already confident enough.
14908
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14909
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14910
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14911
+ * frames is the wrong question for a classifier that labels 1–3 frames
14912
+ * per episode. The count window is the brake that drops a single-frame
14913
+ * false positive; the rule's own `throttle` cooldown is the other. The
14914
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14915
+ * per device) — a label only reaches this condition if the classifier was
14916
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14859
14917
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14860
14918
  * the condition: at least `hitPercent`% of the samples over
14861
14919
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14882,14 +14940,22 @@ var NcOccupancyConditionSchema = object({
14882
14940
  * an operator who typed `dog` mean the same thing.
14883
14941
  */
14884
14942
  var NcAudioConditionSchema = object({
14885
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14943
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14886
14944
  labels: array(string().min(1)).min(1).optional(),
14887
14945
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14888
14946
  dbThreshold: number().min(-96).max(0).optional(),
14889
14947
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14890
14948
  hitPercent: number().int().min(1).max(100).default(60),
14891
14949
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14892
- samplingSeconds: number().int().min(1).max(300).default(10)
14950
+ samplingSeconds: number().int().min(1).max(300).default(10),
14951
+ /**
14952
+ * LABEL MODE: how many labelled frames must land inside
14953
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14954
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14955
+ */
14956
+ confirmHits: number().int().min(1).max(20).optional(),
14957
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14958
+ confirmWindowSec: number().int().min(1).max(60).optional()
14893
14959
  });
14894
14960
  /**
14895
14961
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -17263,6 +17329,46 @@ var RecentTracksPageSchema = object({
17263
17329
  /** Cursor for the next page, or null when this page is the last. */
17264
17330
  nextCursor: string().nullable()
17265
17331
  });
17332
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
17333
+ var LIST_GROUPS_MAX_LIMIT = 100;
17334
+ var AnalyticsGroupRecordSchema = object({
17335
+ id: string(),
17336
+ deviceId: number().int(),
17337
+ openedAt: number().int(),
17338
+ closedAt: number().int(),
17339
+ timestamp: number().int(),
17340
+ memberCount: number().int(),
17341
+ memberTrackIds: array(string()).readonly(),
17342
+ className: string(),
17343
+ classes: array(string()).readonly(),
17344
+ /** Relative event-media path, or null when the group has no picture yet. */
17345
+ mediaUrl: string().nullable(),
17346
+ singleton: boolean()
17347
+ });
17348
+ var AnalyticsGroupMemberSchema = object({
17349
+ trackId: string(),
17350
+ deviceId: number().int(),
17351
+ className: string(),
17352
+ firstSeen: number().int(),
17353
+ lastSeen: number().int(),
17354
+ mediaUrl: string().nullable()
17355
+ });
17356
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
17357
+ var ListGroupsQueryInput = object({
17358
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
17359
+ deviceIds: array(number()),
17360
+ /** Window lower bound on `closedAt` (inclusive). */
17361
+ since: number().optional(),
17362
+ /** Window upper bound on `openedAt` (inclusive). */
17363
+ until: number().optional(),
17364
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
17365
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
17366
+ cursor: string().optional()
17367
+ });
17368
+ var ListGroupsPageSchema = object({
17369
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
17370
+ nextCursor: string().nullable()
17371
+ });
17266
17372
  var KeyEventQueryInput = object({
17267
17373
  deviceId: number(),
17268
17374
  /** Window lower bound (track firstSeen ≥ since). */
@@ -17338,7 +17444,9 @@ var TrackCascadeCountsSchema = object({
17338
17444
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
17339
17445
  plates: number().int(),
17340
17446
  /** Per-track CLIP search vectors removed (best-effort). */
17341
- embeddings: number().int()
17447
+ embeddings: number().int(),
17448
+ /** Group membership + group rows removed with their last member (best-effort). */
17449
+ groups: number().int()
17342
17450
  });
17343
17451
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
17344
17452
  var DiskReconcileCountsSchema = object({
@@ -17484,7 +17592,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17484
17592
  * stationary registry). Default false: the timeline lists passages,
17485
17593
  * not parking records (operator decision, 2026-08-15). */
17486
17594
  includeStationary: boolean().optional()
17487
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17595
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17596
+ deviceId: number(),
17597
+ groupId: string().min(1)
17598
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17488
17599
  kind: "mutation",
17489
17600
  auth: "admin"
17490
17601
  }), 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({
@@ -17702,6 +17813,33 @@ var NativeCropRefSchema = object({
17702
17813
  h: number()
17703
17814
  })
17704
17815
  });
17816
+ object({
17817
+ crop: object({
17818
+ left: number(),
17819
+ top: number(),
17820
+ width: number().positive(),
17821
+ height: number().positive()
17822
+ }).optional(),
17823
+ content: object({
17824
+ width: number().int().positive(),
17825
+ height: number().int().positive()
17826
+ }),
17827
+ fit: _enum(["stretch", "contain"]),
17828
+ format: _enum([
17829
+ "rgb",
17830
+ "gray",
17831
+ "jpeg"
17832
+ ])
17833
+ });
17834
+ var FrameRefSchema = object({
17835
+ registryId: string().min(1),
17836
+ id: string().min(1),
17837
+ width: number().int().positive(),
17838
+ height: number().int().positive(),
17839
+ format: _enum(["rgb", "gray"]),
17840
+ timestamp: number(),
17841
+ capturedAt: number().optional()
17842
+ });
17705
17843
  var ModelFormatSchema$1 = _enum([
17706
17844
  "onnx",
17707
17845
  "coreml",
@@ -17767,7 +17905,8 @@ var PipelineModelOptionSchema = object({
17767
17905
  sizeMB: number()
17768
17906
  })),
17769
17907
  group: ModelVariantGroupSchema.optional(),
17770
- legacy: boolean().optional()
17908
+ legacy: boolean().optional(),
17909
+ provider: ModelProviderIdSchema.optional()
17771
17910
  });
17772
17911
  var ConfigFieldBridge = custom();
17773
17912
  var PipelineAddonSchemaSchema = object({
@@ -17946,6 +18085,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
17946
18085
  steps: array(PipelineStepInputSchema).min(1),
17947
18086
  frame: FrameInputSchema.optional(),
17948
18087
  /**
18088
+ * Process-local lazy frame. Valid only when caller and provider resolve
18089
+ * in the same execution-group process; split/cross-node callers use
18090
+ * `frame`/`image` inline compatibility instead.
18091
+ */
18092
+ frameRef: FrameRefSchema.optional(),
18093
+ /**
17949
18094
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
17950
18095
  * the decoded pixels live in. One more member of the one-of
17951
18096
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -18241,7 +18386,10 @@ var NativeCropResultSchema = object({
18241
18386
  * Which source served this crop, so a quality-sensitive consumer (the native
18242
18387
  * `keyFrame`) can reject a degraded fallback:
18243
18388
  * - `native` — cut from the decode worker's retained NATIVE surface (the
18244
- * quality path).
18389
+ * quality path). A subject-tile serve is also native-resolution and stays
18390
+ * `native` here: the public enum cannot name `tile` without a breaking cap
18391
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
18392
+ * internal crop result (`nativeHits` vs `tileHits`).
18245
18393
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
18246
18394
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
18247
18395
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -18732,12 +18880,41 @@ var RunnerLocalLoadSchema = object({
18732
18880
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
18733
18881
  * working unchanged when they switch to reading from the runner cap.
18734
18882
  */
18883
+ var FrameLazyCountersSchema = object({
18884
+ framesDecoded: number(),
18885
+ framesAdmitted: number(),
18886
+ framesDroppedPixelFree: number(),
18887
+ viewsMaterialized: number(),
18888
+ viewsSkipped: number(),
18889
+ workerToRunnerBytes: number(),
18890
+ runnerToPoolRawBytes: number(),
18891
+ runnerToPoolJpegBytes: number(),
18892
+ onDemandFullFrameRequests: number(),
18893
+ onDemandCropRequests: number(),
18894
+ nativeHits: number(),
18895
+ nativeMisses: number(),
18896
+ tileHits: number(),
18897
+ tileMisses: number(),
18898
+ fallbackHits: number(),
18899
+ fallbackMisses: number(),
18900
+ retainedWritesAvoided: number(),
18901
+ residentRefs: number(),
18902
+ residentBytes: number(),
18903
+ releases: number(),
18904
+ evictions: number(),
18905
+ staleMisses: number()
18906
+ });
18907
+ var FrameLazyMetricsSchema = object({
18908
+ node: FrameLazyCountersSchema,
18909
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
18910
+ });
18735
18911
  var RunnerLocalMetricsSchema = object({
18736
18912
  nodeId: string(),
18737
18913
  activeCameras: number(),
18738
18914
  throttledCameras: number(),
18739
18915
  avgInferenceTimeMs: number(),
18740
- queueDepth: number()
18916
+ queueDepth: number(),
18917
+ frameLazy: FrameLazyMetricsSchema.optional()
18741
18918
  });
18742
18919
  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({
18743
18920
  handle: FrameHandleSchema,
@@ -20141,6 +20318,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
20141
20318
  location: StorageLocationSchema,
20142
20319
  relativePath: string()
20143
20320
  }), _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" });
20321
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
20322
+ var ProfileSettingsSchemaBridge = unknown().nullable();
20323
+ var ProfileSettingsBagSchema = record(string(), unknown());
20144
20324
  /**
20145
20325
  * A live terminal session hosted by the provider addon. Output and input do
20146
20326
  * NOT flow through the capability — they use the addon data plane
@@ -20170,7 +20350,14 @@ var TerminalSessionInfoSchema = object({
20170
20350
  var TerminalProfileInfoSchema = object({
20171
20351
  profileId: string(),
20172
20352
  label: string(),
20173
- description: string().optional()
20353
+ description: string().optional(),
20354
+ /** Spawn defaults the instance form copies on create. */
20355
+ executable: string().optional(),
20356
+ args: array(string()).readonly().optional(),
20357
+ cwd: string().optional(),
20358
+ environment: array(string()).readonly().optional(),
20359
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
20360
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
20174
20361
  });
20175
20362
  /**
20176
20363
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -20183,7 +20370,12 @@ var TerminalInstanceInfoSchema = object({
20183
20370
  profileId: string(),
20184
20371
  profileLabel: string(),
20185
20372
  name: string(),
20186
- enabled: boolean()
20373
+ enabled: boolean(),
20374
+ executable: string(),
20375
+ args: array(string()).readonly(),
20376
+ cwd: string(),
20377
+ environment: array(string()).readonly(),
20378
+ profileSettings: ProfileSettingsBagSchema
20187
20379
  });
20188
20380
  var TerminalLegacyCameraSchema = object({
20189
20381
  stableId: string(),
@@ -20213,7 +20405,23 @@ var TerminalOutputBatchSchema = object({
20213
20405
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
20214
20406
  targetNodeId: string().min(1),
20215
20407
  profileId: string().min(1),
20216
- name: string().trim().min(1).max(160).optional()
20408
+ name: string().trim().min(1).max(160).optional(),
20409
+ executable: string().max(1024).optional(),
20410
+ args: array(string().max(2048)).max(64).optional(),
20411
+ cwd: string().max(1024).optional(),
20412
+ environment: array(string().max(4096)).max(64).optional(),
20413
+ profileSettings: ProfileSettingsBagSchema.optional()
20414
+ }), TerminalInstanceInfoSchema, {
20415
+ kind: "mutation",
20416
+ auth: "admin"
20417
+ }), method(object({
20418
+ instanceId: string().min(1),
20419
+ name: string().trim().min(1).max(160).optional(),
20420
+ executable: string().max(1024).optional(),
20421
+ args: array(string().max(2048)).max(64).optional(),
20422
+ cwd: string().max(1024).optional(),
20423
+ environment: array(string().max(4096)).max(64).optional(),
20424
+ profileSettings: ProfileSettingsBagSchema.optional()
20217
20425
  }), TerminalInstanceInfoSchema, {
20218
20426
  kind: "mutation",
20219
20427
  auth: "admin"
@@ -20235,7 +20443,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
20235
20443
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
20236
20444
  profileId: string(),
20237
20445
  cols: number().int().positive(),
20238
- rows: number().int().positive()
20446
+ rows: number().int().positive(),
20447
+ executable: string().max(1024).optional(),
20448
+ args: array(string().max(2048)).max(64).optional(),
20449
+ cwd: string().max(1024).optional(),
20450
+ environment: array(string().max(4096)).max(64).optional()
20239
20451
  }), TerminalSessionInfoSchema, {
20240
20452
  kind: "mutation",
20241
20453
  auth: "admin"
@@ -24110,10 +24322,10 @@ var lawnMowerControlCapability = {
24110
24322
  *
24111
24323
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
24112
24324
  * to receive an ordered list of candidate base URLs it should race
24113
- * on connect — LAN IPv4 first (lowest latency when on same network),
24114
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
24115
- * race them with short timeouts and stick with the winner for the
24116
- * session.
24325
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
24326
+ * when on the same network), then public hostname (if a tunnel is
24327
+ * up). The SDK can race them with short timeouts and stick with the
24328
+ * winner for the session.
24117
24329
  *
24118
24330
  * Why hub-only: agents are not directly addressable by the operator's
24119
24331
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -24268,6 +24480,17 @@ var NotificationEndpointSchema = object({
24268
24480
  /** What the ranking currently resolves to (null when nothing is reachable). */
24269
24481
  resolved: string().nullable()
24270
24482
  });
24483
+ /**
24484
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
24485
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
24486
+ * currently expands to, so the UI can show the effective set either way.
24487
+ */
24488
+ var ViewerEndpointsSchema = object({
24489
+ /** The operator's explicit race set, or empty for AUTO. */
24490
+ baseUrls: array(string()).readonly(),
24491
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
24492
+ resolved: array(string()).readonly()
24493
+ });
24271
24494
  var AllowedAddressesSchema = object({
24272
24495
  /**
24273
24496
  * Allowlist of interface addresses operators have explicitly opted
@@ -24276,6 +24499,20 @@ var AllowedAddressesSchema = object({
24276
24499
  * Network Addresses admin page and persisted by the addon.
24277
24500
  */
24278
24501
  addresses: array(string()).readonly() });
24502
+ var TlsStatusSchema = object({
24503
+ mode: _enum([
24504
+ "generated",
24505
+ "uploaded",
24506
+ "disabled"
24507
+ ]),
24508
+ leafFingerprintSha256: string().nullable(),
24509
+ caFingerprintSha256: string().nullable(),
24510
+ validTo: string().nullable(),
24511
+ sans: array(string()),
24512
+ caCertPem: string().nullable(),
24513
+ reissueError: string().nullable(),
24514
+ restartRequired: boolean()
24515
+ });
24279
24516
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
24280
24517
  /**
24281
24518
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -24285,17 +24522,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
24285
24522
  */
24286
24523
  port: number().int().min(1).max(65535).optional(),
24287
24524
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
24288
- * candidate. Default `true`. */
24525
+ * candidate. Default `false` — loopback is not a client route. */
24289
24526
  includeLoopback: boolean().optional(),
24290
- /** Skip IPv6 entries. Some legacy clients can't parse them.
24291
- * Default `false`. */
24527
+ /** Skip IPv6 entries. Default `false` the palette includes stable
24528
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
24529
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
24292
24530
  ipv4Only: boolean().optional(),
24293
24531
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
24294
24532
  * Pass `'https'` when the caller is itself loaded over HTTPS
24295
24533
  * to avoid mixed-content blocks in the browser. The public
24296
24534
  * tunnel always emits `https://` regardless. */
24297
24535
  scheme: _enum(["http", "https"]).optional()
24298
- }), 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" });
24536
+ }), 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, {
24537
+ kind: "mutation",
24538
+ auth: "admin"
24539
+ }), method(object({
24540
+ certPem: string().min(1),
24541
+ keyPem: string().min(1),
24542
+ caPem: string().optional()
24543
+ }), TlsStatusSchema, {
24544
+ kind: "mutation",
24545
+ auth: "admin"
24546
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
24547
+ kind: "mutation",
24548
+ auth: "admin"
24549
+ });
24299
24550
  var LockControlStatusSchema = object({
24300
24551
  /** Lifecycle state of the lock. `jammed` means the motor reported
24301
24552
  * failure to reach the target — operator intervention required. */
@@ -25902,7 +26153,12 @@ var PlateInfoSchema = object({
25902
26153
  plateBbox: BoundingBoxSchema.optional(),
25903
26154
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
25904
26155
  keyFrameMediaKey: string().optional(),
25905
- base64: string().optional()
26156
+ base64: string().optional(),
26157
+ /**
26158
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
26159
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
26160
+ */
26161
+ cropUrl: string().optional()
25906
26162
  });
25907
26163
  var MediaFileLiteSchema = object({
25908
26164
  key: string(),
@@ -31944,6 +32200,12 @@ Object.freeze({
31944
32200
  addonId: null,
31945
32201
  access: "view"
31946
32202
  },
32203
+ "deviceManager.getBindingsBatch": {
32204
+ capName: "device-manager",
32205
+ capScope: "system",
32206
+ addonId: null,
32207
+ access: "view"
32208
+ },
31947
32209
  "deviceManager.getChildren": {
31948
32210
  capName: "device-manager",
31949
32211
  capScope: "system",
@@ -32004,6 +32266,12 @@ Object.freeze({
32004
32266
  addonId: null,
32005
32267
  access: "view"
32006
32268
  },
32269
+ "deviceManager.getLinkedDevicesBatch": {
32270
+ capName: "device-manager",
32271
+ capScope: "system",
32272
+ addonId: null,
32273
+ access: "view"
32274
+ },
32007
32275
  "deviceManager.getRoleDisplayDefaults": {
32008
32276
  capName: "device-manager",
32009
32277
  capScope: "system",
@@ -32886,6 +33154,12 @@ Object.freeze({
32886
33154
  addonId: null,
32887
33155
  access: "create"
32888
33156
  },
33157
+ "localNetwork.downloadCa": {
33158
+ capName: "local-network",
33159
+ capScope: "system",
33160
+ addonId: null,
33161
+ access: "view"
33162
+ },
32889
33163
  "localNetwork.getAllowedAddresses": {
32890
33164
  capName: "local-network",
32891
33165
  capScope: "system",
@@ -32910,18 +33184,42 @@ Object.freeze({
32910
33184
  addonId: null,
32911
33185
  access: "view"
32912
33186
  },
33187
+ "localNetwork.getTlsStatus": {
33188
+ capName: "local-network",
33189
+ capScope: "system",
33190
+ addonId: null,
33191
+ access: "view"
33192
+ },
33193
+ "localNetwork.getViewerEndpoints": {
33194
+ capName: "local-network",
33195
+ capScope: "system",
33196
+ addonId: null,
33197
+ access: "view"
33198
+ },
32913
33199
  "localNetwork.list": {
32914
33200
  capName: "local-network",
32915
33201
  capScope: "system",
32916
33202
  addonId: null,
32917
33203
  access: "view"
32918
33204
  },
33205
+ "localNetwork.regenerateCertificate": {
33206
+ capName: "local-network",
33207
+ capScope: "system",
33208
+ addonId: null,
33209
+ access: "create"
33210
+ },
32919
33211
  "localNetwork.resetAllowlistToBestMatch": {
32920
33212
  capName: "local-network",
32921
33213
  capScope: "system",
32922
33214
  addonId: null,
32923
33215
  access: "delete"
32924
33216
  },
33217
+ "localNetwork.revertToGeneratedCertificate": {
33218
+ capName: "local-network",
33219
+ capScope: "system",
33220
+ addonId: null,
33221
+ access: "create"
33222
+ },
32925
33223
  "localNetwork.setAllowedAddresses": {
32926
33224
  capName: "local-network",
32927
33225
  capScope: "system",
@@ -32934,6 +33232,18 @@ Object.freeze({
32934
33232
  addonId: null,
32935
33233
  access: "create"
32936
33234
  },
33235
+ "localNetwork.setViewerEndpoints": {
33236
+ capName: "local-network",
33237
+ capScope: "system",
33238
+ addonId: null,
33239
+ access: "create"
33240
+ },
33241
+ "localNetwork.uploadCertificate": {
33242
+ capName: "local-network",
33243
+ capScope: "system",
33244
+ addonId: null,
33245
+ access: "create"
33246
+ },
32937
33247
  "lockControl.lock": {
32938
33248
  capName: "lock-control",
32939
33249
  capScope: "device",
@@ -33732,6 +34042,12 @@ Object.freeze({
33732
34042
  addonId: null,
33733
34043
  access: "view"
33734
34044
  },
34045
+ "pipelineAnalytics.getGroup": {
34046
+ capName: "pipeline-analytics",
34047
+ capScope: "device",
34048
+ addonId: null,
34049
+ access: "view"
34050
+ },
33735
34051
  "pipelineAnalytics.getKeyEvents": {
33736
34052
  capName: "pipeline-analytics",
33737
34053
  capScope: "device",
@@ -33816,6 +34132,12 @@ Object.freeze({
33816
34132
  addonId: null,
33817
34133
  access: "view"
33818
34134
  },
34135
+ "pipelineAnalytics.listGroups": {
34136
+ capName: "pipeline-analytics",
34137
+ capScope: "device",
34138
+ addonId: null,
34139
+ access: "view"
34140
+ },
33819
34141
  "pipelineAnalytics.listOpsLog": {
33820
34142
  capName: "pipeline-analytics",
33821
34143
  capScope: "device",
@@ -35814,6 +36136,12 @@ Object.freeze({
35814
36136
  addonId: null,
35815
36137
  access: "create"
35816
36138
  },
36139
+ "terminalSession.updateInstance": {
36140
+ capName: "terminal-session",
36141
+ capScope: "system",
36142
+ addonId: null,
36143
+ access: "create"
36144
+ },
35817
36145
  "terminalSession.writeInput": {
35818
36146
  capName: "terminal-session",
35819
36147
  capScope: "system",
@@ -36591,6 +36919,11 @@ Object.freeze({
36591
36919
  form: "single",
36592
36920
  optional: false
36593
36921
  }],
36922
+ "deviceManager.getBindingsBatch": [{
36923
+ name: "deviceIds",
36924
+ form: "array",
36925
+ optional: false
36926
+ }],
36594
36927
  "deviceManager.getChildren": [{
36595
36928
  name: "parentDeviceId",
36596
36929
  form: "single",
@@ -36636,6 +36969,11 @@ Object.freeze({
36636
36969
  form: "single",
36637
36970
  optional: false
36638
36971
  }],
36972
+ "deviceManager.getLinkedDevicesBatch": [{
36973
+ name: "deviceIds",
36974
+ form: "array",
36975
+ optional: false
36976
+ }],
36639
36977
  "deviceManager.getSettingsSchema": [{
36640
36978
  name: "deviceId",
36641
36979
  form: "single",
@@ -36656,6 +36994,11 @@ Object.freeze({
36656
36994
  form: "single",
36657
36995
  optional: false
36658
36996
  }],
36997
+ "deviceManager.listAll": [{
36998
+ name: "deviceIds",
36999
+ form: "array",
37000
+ optional: true
37001
+ }],
36659
37002
  "deviceManager.loadConfig": [{
36660
37003
  name: "deviceId",
36661
37004
  form: "single",
@@ -37229,6 +37572,11 @@ Object.freeze({
37229
37572
  form: "single",
37230
37573
  optional: false
37231
37574
  }],
37575
+ "pipelineAnalytics.getGroup": [{
37576
+ name: "deviceId",
37577
+ form: "single",
37578
+ optional: false
37579
+ }],
37232
37580
  "pipelineAnalytics.getKeyEvents": [{
37233
37581
  name: "deviceId",
37234
37582
  form: "single",
@@ -37284,6 +37632,11 @@ Object.freeze({
37284
37632
  form: "array",
37285
37633
  optional: false
37286
37634
  }],
37635
+ "pipelineAnalytics.listGroups": [{
37636
+ name: "deviceIds",
37637
+ form: "array",
37638
+ optional: false
37639
+ }],
37287
37640
  "pipelineAnalytics.listOpsLog": [{
37288
37641
  name: "deviceId",
37289
37642
  form: "single",
@@ -38301,6 +38654,35 @@ Object.freeze(Object.fromEntries([{
38301
38654
  }]
38302
38655
  }].map((s) => [s.stepId, s.defaultModelId])));
38303
38656
  string().min(1);
38657
+ var CLUSTER_STEP_SETTING_FIELDS = [{
38658
+ stepId: "face-embedding",
38659
+ key: "minLandmarkFaceSize",
38660
+ label: "Min face size for recognition (detection px)",
38661
+ 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.",
38662
+ type: "slider",
38663
+ min: 0,
38664
+ max: 64,
38665
+ step: 2,
38666
+ default: 24
38667
+ }];
38668
+ function clusterStepSettingKey(stepId, fieldKey) {
38669
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
38670
+ }
38671
+ var ClusterSettingNumberSchema = number().finite();
38672
+ function readClusterStepSettings(config) {
38673
+ const out = {};
38674
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
38675
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
38676
+ const value = parsed.success ? parsed.data : field.default;
38677
+ const existing = out[field.stepId] ?? {};
38678
+ out[field.stepId] = {
38679
+ ...existing,
38680
+ [field.key]: value
38681
+ };
38682
+ }
38683
+ return out;
38684
+ }
38685
+ readClusterStepSettings({});
38304
38686
  object({
38305
38687
  /**
38306
38688
  * Fraction of the box's own size added on EACH side before cutting.