@camstack/addon-provider-homematic 1.2.27 → 1.2.29

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
@@ -5810,6 +5810,13 @@ var BaseAddon = class {
5810
5810
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5811
5811
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5812
5812
  _registeredCapNames = [];
5813
+ /**
5814
+ * True only after `readAddonStore` actually answered. Constructor
5815
+ * defaults look like stored config when the store is down — a forked
5816
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5817
+ * mode, 2026-08-25) is not "the operator chose this".
5818
+ */
5819
+ settingsStoreReady = false;
5813
5820
  /** Default config values. Provided via constructor. */
5814
5821
  defaults;
5815
5822
  constructor(defaults) {
@@ -6210,7 +6217,9 @@ var BaseAddon = class {
6210
6217
  ];
6211
6218
  let lastErr;
6212
6219
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6213
- return await settings.readAddonStore() ?? {};
6220
+ const stored = await settings.readAddonStore() ?? {};
6221
+ this.settingsStoreReady = true;
6222
+ return stored;
6214
6223
  } catch (err) {
6215
6224
  lastErr = err;
6216
6225
  const msg = err instanceof Error ? err.message : String(err);
@@ -6218,6 +6227,7 @@ var BaseAddon = class {
6218
6227
  if (attempt === delaysMs.length) break;
6219
6228
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6220
6229
  }
6230
+ this.settingsStoreReady = false;
6221
6231
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6222
6232
  return {};
6223
6233
  }
@@ -8029,6 +8039,15 @@ var LabelDefinitionSchema = object({
8029
8039
  description: string().optional(),
8030
8040
  icon: string().optional()
8031
8041
  });
8042
+ var ClassMapDefinitionSchema = object({
8043
+ mapping: record(string(), _enum([
8044
+ "person",
8045
+ "vehicle",
8046
+ "animal",
8047
+ "package"
8048
+ ])),
8049
+ preserveOriginal: boolean()
8050
+ });
8032
8051
  var MODEL_FORMATS = [
8033
8052
  "onnx",
8034
8053
  "coreml",
@@ -8112,6 +8131,12 @@ var ModelVariantGroupSchema = object({
8112
8131
  */
8113
8132
  resolution: number().int().positive().optional()
8114
8133
  });
8134
+ var ModelProviderIdSchema = _enum([
8135
+ "camstack",
8136
+ "frigate",
8137
+ "scrypted",
8138
+ "custom"
8139
+ ]);
8115
8140
  var ModelCatalogEntrySchema = object({
8116
8141
  id: string(),
8117
8142
  name: string(),
@@ -8207,7 +8232,19 @@ var ModelCatalogEntrySchema = object({
8207
8232
  * `id` stays the source of truth for resolution/download/persistence; grouping
8208
8233
  * is a presentation overlay resolved back to an `id`.
8209
8234
  */
8210
- group: ModelVariantGroupSchema.optional()
8235
+ group: ModelVariantGroupSchema.optional(),
8236
+ /**
8237
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8238
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8239
+ * persisted before this field existed (`inferModelProvider` fills those).
8240
+ */
8241
+ provider: ModelProviderIdSchema.optional(),
8242
+ /**
8243
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8244
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8245
+ * labels already ARE the CamStack macros (Scrypted identity map).
8246
+ */
8247
+ classMap: ClassMapDefinitionSchema.optional()
8211
8248
  });
8212
8249
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8213
8250
  format: literal("openvino"),
@@ -8236,7 +8273,8 @@ var ModelConvertMetadataSchema = object({
8236
8273
  "ocr",
8237
8274
  "segmentation"
8238
8275
  ]),
8239
- faceAlignment: boolean().optional()
8276
+ faceAlignment: boolean().optional(),
8277
+ classMap: ClassMapDefinitionSchema.optional()
8240
8278
  });
8241
8279
  var ConvertResultSchema = object({
8242
8280
  entry: ModelCatalogEntrySchema,
@@ -12059,6 +12097,27 @@ var LinkedDeviceSchema = object({
12059
12097
  features: array(string()),
12060
12098
  producesTrackedEvents: boolean().optional()
12061
12099
  });
12100
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
12101
+ * The batch answer needs the tag; the single-device answer already has it
12102
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
12103
+ var LinkedDevicesForDeviceSchema = object({
12104
+ deviceId: number(),
12105
+ mode: LinkedDevicesModeSchema,
12106
+ devices: array(LinkedDeviceSchema)
12107
+ });
12108
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
12109
+ * `getAllBindings` all answer in. Declared once: three copies of the same
12110
+ * object literal is exactly how the three drift apart. */
12111
+ var DeviceBindingsForDeviceSchema = object({
12112
+ deviceId: number(),
12113
+ entries: array(object({
12114
+ capName: string(),
12115
+ kind: _enum(["native", "wrapped"]),
12116
+ providerAddonId: string(),
12117
+ providerNodeId: string(),
12118
+ nativeAddonId: string()
12119
+ }))
12120
+ });
12062
12121
  var SavedDeviceRowSchema = object({
12063
12122
  /** Numeric id reserved at allocateDeviceId time. */
12064
12123
  id: number(),
@@ -12284,11 +12343,25 @@ method(object({
12284
12343
  projection: _enum(["full", "slim"]).optional(),
12285
12344
  /** Return only camera devices. Filtering server-side instead of
12286
12345
  * shipping 293 rows to find 12. */
12287
- isCamera: boolean().optional()
12346
+ isCamera: boolean().optional(),
12347
+ /**
12348
+ * Return only these device ids. For the caller that already KNOWS the
12349
+ * handful it wants and needs a field the id-bearing answer does not
12350
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12351
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12352
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12353
+ * refetches on the reconcile interval, on a phone.
12354
+ *
12355
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12356
+ * keys rather than rejecting them (verified against the live hub
12357
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12358
+ * it answers today and the caller filters as it already does.
12359
+ */
12360
+ deviceIds: array(number()).optional()
12288
12361
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12289
12362
  mode: LinkedDevicesModeSchema,
12290
12363
  devices: array(LinkedDeviceSchema)
12291
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12364
+ })), 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({
12292
12365
  deviceId: number(),
12293
12366
  values: record(string(), unknown())
12294
12367
  }), object({ success: literal(true) }), {
@@ -12315,25 +12388,7 @@ method(object({
12315
12388
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
12316
12389
  kind: "mutation",
12317
12390
  auth: "admin"
12318
- }), method(object({ deviceId: number() }), object({
12319
- deviceId: number(),
12320
- entries: array(object({
12321
- capName: string(),
12322
- kind: _enum(["native", "wrapped"]),
12323
- providerAddonId: string(),
12324
- providerNodeId: string(),
12325
- nativeAddonId: string()
12326
- }))
12327
- })), method(object({}), array(object({
12328
- deviceId: number(),
12329
- entries: array(object({
12330
- capName: string(),
12331
- kind: _enum(["native", "wrapped"]),
12332
- providerAddonId: string(),
12333
- providerNodeId: string(),
12334
- nativeAddonId: string()
12335
- }))
12336
- }))), method(object({
12391
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12337
12392
  deviceId: number(),
12338
12393
  capName: string(),
12339
12394
  wrapperAddonId: string(),
@@ -14743,12 +14798,15 @@ var NcOccupancyConditionSchema = object({
14743
14798
  * there is no second switch that can disagree with the first and every rule
14744
14799
  * authored before the decision migrates for free (`audioModeOf`):
14745
14800
  *
14746
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14747
- * classifier labels with one of them. No window, no percentage:
14748
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14749
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14750
- * the analyzer's (`classificationMinScore`, per device) — a label only
14751
- * reaches this condition if the classifier was already confident enough.
14801
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14802
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14803
+ * `hitPercent` and `samplingSeconds` are still ignored — a percentage of
14804
+ * frames is the wrong question for a classifier that labels 1–3 frames
14805
+ * per episode. The count window is the brake that drops a single-frame
14806
+ * false positive; the rule's own `throttle` cooldown is the other. The
14807
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14808
+ * per device) — a label only reaches this condition if the classifier was
14809
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14752
14810
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14753
14811
  * the condition: at least `hitPercent`% of the samples over
14754
14812
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14775,14 +14833,22 @@ var NcOccupancyConditionSchema = object({
14775
14833
  * an operator who typed `dog` mean the same thing.
14776
14834
  */
14777
14835
  var NcAudioConditionSchema = object({
14778
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14836
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14779
14837
  labels: array(string().min(1)).min(1).optional(),
14780
14838
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14781
14839
  dbThreshold: number().min(-96).max(0).optional(),
14782
14840
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14783
14841
  hitPercent: number().int().min(1).max(100).default(60),
14784
14842
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14785
- samplingSeconds: number().int().min(1).max(300).default(10)
14843
+ samplingSeconds: number().int().min(1).max(300).default(10),
14844
+ /**
14845
+ * LABEL MODE: how many labelled frames must land inside
14846
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14847
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14848
+ */
14849
+ confirmHits: number().int().min(1).max(20).optional(),
14850
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14851
+ confirmWindowSec: number().int().min(1).max(60).optional()
14786
14852
  });
14787
14853
  /**
14788
14854
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -17156,6 +17222,46 @@ var RecentTracksPageSchema = object({
17156
17222
  /** Cursor for the next page, or null when this page is the last. */
17157
17223
  nextCursor: string().nullable()
17158
17224
  });
17225
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
17226
+ var LIST_GROUPS_MAX_LIMIT = 100;
17227
+ var AnalyticsGroupRecordSchema = object({
17228
+ id: string(),
17229
+ deviceId: number().int(),
17230
+ openedAt: number().int(),
17231
+ closedAt: number().int(),
17232
+ timestamp: number().int(),
17233
+ memberCount: number().int(),
17234
+ memberTrackIds: array(string()).readonly(),
17235
+ className: string(),
17236
+ classes: array(string()).readonly(),
17237
+ /** Relative event-media path, or null when the group has no picture yet. */
17238
+ mediaUrl: string().nullable(),
17239
+ singleton: boolean()
17240
+ });
17241
+ var AnalyticsGroupMemberSchema = object({
17242
+ trackId: string(),
17243
+ deviceId: number().int(),
17244
+ className: string(),
17245
+ firstSeen: number().int(),
17246
+ lastSeen: number().int(),
17247
+ mediaUrl: string().nullable()
17248
+ });
17249
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
17250
+ var ListGroupsQueryInput = object({
17251
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
17252
+ deviceIds: array(number()),
17253
+ /** Window lower bound on `closedAt` (inclusive). */
17254
+ since: number().optional(),
17255
+ /** Window upper bound on `openedAt` (inclusive). */
17256
+ until: number().optional(),
17257
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
17258
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
17259
+ cursor: string().optional()
17260
+ });
17261
+ var ListGroupsPageSchema = object({
17262
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
17263
+ nextCursor: string().nullable()
17264
+ });
17159
17265
  var KeyEventQueryInput = object({
17160
17266
  deviceId: number(),
17161
17267
  /** Window lower bound (track firstSeen ≥ since). */
@@ -17231,7 +17337,9 @@ var TrackCascadeCountsSchema = object({
17231
17337
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
17232
17338
  plates: number().int(),
17233
17339
  /** Per-track CLIP search vectors removed (best-effort). */
17234
- embeddings: number().int()
17340
+ embeddings: number().int(),
17341
+ /** Group membership + group rows removed with their last member (best-effort). */
17342
+ groups: number().int()
17235
17343
  });
17236
17344
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
17237
17345
  var DiskReconcileCountsSchema = object({
@@ -17377,7 +17485,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17377
17485
  * stationary registry). Default false: the timeline lists passages,
17378
17486
  * not parking records (operator decision, 2026-08-15). */
17379
17487
  includeStationary: boolean().optional()
17380
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17488
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17489
+ deviceId: number(),
17490
+ groupId: string().min(1)
17491
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17381
17492
  kind: "mutation",
17382
17493
  auth: "admin"
17383
17494
  }), 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({
@@ -17595,6 +17706,33 @@ var NativeCropRefSchema = object({
17595
17706
  h: number()
17596
17707
  })
17597
17708
  });
17709
+ object({
17710
+ crop: object({
17711
+ left: number(),
17712
+ top: number(),
17713
+ width: number().positive(),
17714
+ height: number().positive()
17715
+ }).optional(),
17716
+ content: object({
17717
+ width: number().int().positive(),
17718
+ height: number().int().positive()
17719
+ }),
17720
+ fit: _enum(["stretch", "contain"]),
17721
+ format: _enum([
17722
+ "rgb",
17723
+ "gray",
17724
+ "jpeg"
17725
+ ])
17726
+ });
17727
+ var FrameRefSchema = object({
17728
+ registryId: string().min(1),
17729
+ id: string().min(1),
17730
+ width: number().int().positive(),
17731
+ height: number().int().positive(),
17732
+ format: _enum(["rgb", "gray"]),
17733
+ timestamp: number(),
17734
+ capturedAt: number().optional()
17735
+ });
17598
17736
  var ModelFormatSchema$1 = _enum([
17599
17737
  "onnx",
17600
17738
  "coreml",
@@ -17660,7 +17798,8 @@ var PipelineModelOptionSchema = object({
17660
17798
  sizeMB: number()
17661
17799
  })),
17662
17800
  group: ModelVariantGroupSchema.optional(),
17663
- legacy: boolean().optional()
17801
+ legacy: boolean().optional(),
17802
+ provider: ModelProviderIdSchema.optional()
17664
17803
  });
17665
17804
  var ConfigFieldBridge = custom();
17666
17805
  var PipelineAddonSchemaSchema = object({
@@ -17839,6 +17978,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
17839
17978
  steps: array(PipelineStepInputSchema).min(1),
17840
17979
  frame: FrameInputSchema.optional(),
17841
17980
  /**
17981
+ * Process-local lazy frame. Valid only when caller and provider resolve
17982
+ * in the same execution-group process; split/cross-node callers use
17983
+ * `frame`/`image` inline compatibility instead.
17984
+ */
17985
+ frameRef: FrameRefSchema.optional(),
17986
+ /**
17842
17987
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
17843
17988
  * the decoded pixels live in. One more member of the one-of
17844
17989
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -18134,7 +18279,10 @@ var NativeCropResultSchema = object({
18134
18279
  * Which source served this crop, so a quality-sensitive consumer (the native
18135
18280
  * `keyFrame`) can reject a degraded fallback:
18136
18281
  * - `native` — cut from the decode worker's retained NATIVE surface (the
18137
- * quality path).
18282
+ * quality path). A subject-tile serve is also native-resolution and stays
18283
+ * `native` here: the public enum cannot name `tile` without a breaking cap
18284
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
18285
+ * internal crop result (`nativeHits` vs `tileHits`).
18138
18286
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
18139
18287
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
18140
18288
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -18625,12 +18773,41 @@ var RunnerLocalLoadSchema = object({
18625
18773
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
18626
18774
  * working unchanged when they switch to reading from the runner cap.
18627
18775
  */
18776
+ var FrameLazyCountersSchema = object({
18777
+ framesDecoded: number(),
18778
+ framesAdmitted: number(),
18779
+ framesDroppedPixelFree: number(),
18780
+ viewsMaterialized: number(),
18781
+ viewsSkipped: number(),
18782
+ workerToRunnerBytes: number(),
18783
+ runnerToPoolRawBytes: number(),
18784
+ runnerToPoolJpegBytes: number(),
18785
+ onDemandFullFrameRequests: number(),
18786
+ onDemandCropRequests: number(),
18787
+ nativeHits: number(),
18788
+ nativeMisses: number(),
18789
+ tileHits: number(),
18790
+ tileMisses: number(),
18791
+ fallbackHits: number(),
18792
+ fallbackMisses: number(),
18793
+ retainedWritesAvoided: number(),
18794
+ residentRefs: number(),
18795
+ residentBytes: number(),
18796
+ releases: number(),
18797
+ evictions: number(),
18798
+ staleMisses: number()
18799
+ });
18800
+ var FrameLazyMetricsSchema = object({
18801
+ node: FrameLazyCountersSchema,
18802
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
18803
+ });
18628
18804
  var RunnerLocalMetricsSchema = object({
18629
18805
  nodeId: string(),
18630
18806
  activeCameras: number(),
18631
18807
  throttledCameras: number(),
18632
18808
  avgInferenceTimeMs: number(),
18633
- queueDepth: number()
18809
+ queueDepth: number(),
18810
+ frameLazy: FrameLazyMetricsSchema.optional()
18634
18811
  });
18635
18812
  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({
18636
18813
  handle: FrameHandleSchema,
@@ -19930,6 +20107,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
19930
20107
  location: StorageLocationSchema,
19931
20108
  relativePath: string()
19932
20109
  }), _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" });
20110
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
20111
+ var ProfileSettingsSchemaBridge = unknown().nullable();
20112
+ var ProfileSettingsBagSchema = record(string(), unknown());
19933
20113
  /**
19934
20114
  * A live terminal session hosted by the provider addon. Output and input do
19935
20115
  * NOT flow through the capability — they use the addon data plane
@@ -19959,7 +20139,14 @@ var TerminalSessionInfoSchema = object({
19959
20139
  var TerminalProfileInfoSchema = object({
19960
20140
  profileId: string(),
19961
20141
  label: string(),
19962
- description: string().optional()
20142
+ description: string().optional(),
20143
+ /** Spawn defaults the instance form copies on create. */
20144
+ executable: string().optional(),
20145
+ args: array(string()).readonly().optional(),
20146
+ cwd: string().optional(),
20147
+ environment: array(string()).readonly().optional(),
20148
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
20149
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
19963
20150
  });
19964
20151
  /**
19965
20152
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -19972,7 +20159,12 @@ var TerminalInstanceInfoSchema = object({
19972
20159
  profileId: string(),
19973
20160
  profileLabel: string(),
19974
20161
  name: string(),
19975
- enabled: boolean()
20162
+ enabled: boolean(),
20163
+ executable: string(),
20164
+ args: array(string()).readonly(),
20165
+ cwd: string(),
20166
+ environment: array(string()).readonly(),
20167
+ profileSettings: ProfileSettingsBagSchema
19976
20168
  });
19977
20169
  var TerminalLegacyCameraSchema = object({
19978
20170
  stableId: string(),
@@ -20002,7 +20194,23 @@ var TerminalOutputBatchSchema = object({
20002
20194
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
20003
20195
  targetNodeId: string().min(1),
20004
20196
  profileId: string().min(1),
20005
- name: string().trim().min(1).max(160).optional()
20197
+ name: string().trim().min(1).max(160).optional(),
20198
+ executable: string().max(1024).optional(),
20199
+ args: array(string().max(2048)).max(64).optional(),
20200
+ cwd: string().max(1024).optional(),
20201
+ environment: array(string().max(4096)).max(64).optional(),
20202
+ profileSettings: ProfileSettingsBagSchema.optional()
20203
+ }), TerminalInstanceInfoSchema, {
20204
+ kind: "mutation",
20205
+ auth: "admin"
20206
+ }), method(object({
20207
+ instanceId: string().min(1),
20208
+ name: string().trim().min(1).max(160).optional(),
20209
+ executable: string().max(1024).optional(),
20210
+ args: array(string().max(2048)).max(64).optional(),
20211
+ cwd: string().max(1024).optional(),
20212
+ environment: array(string().max(4096)).max(64).optional(),
20213
+ profileSettings: ProfileSettingsBagSchema.optional()
20006
20214
  }), TerminalInstanceInfoSchema, {
20007
20215
  kind: "mutation",
20008
20216
  auth: "admin"
@@ -20024,7 +20232,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
20024
20232
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
20025
20233
  profileId: string(),
20026
20234
  cols: number().int().positive(),
20027
- rows: number().int().positive()
20235
+ rows: number().int().positive(),
20236
+ executable: string().max(1024).optional(),
20237
+ args: array(string().max(2048)).max(64).optional(),
20238
+ cwd: string().max(1024).optional(),
20239
+ environment: array(string().max(4096)).max(64).optional()
20028
20240
  }), TerminalSessionInfoSchema, {
20029
20241
  kind: "mutation",
20030
20242
  auth: "admin"
@@ -23899,10 +24111,10 @@ var lawnMowerControlCapability = {
23899
24111
  *
23900
24112
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
23901
24113
  * to receive an ordered list of candidate base URLs it should race
23902
- * on connect — LAN IPv4 first (lowest latency when on same network),
23903
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
23904
- * race them with short timeouts and stick with the winner for the
23905
- * session.
24114
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
24115
+ * when on the same network), then public hostname (if a tunnel is
24116
+ * up). The SDK can race them with short timeouts and stick with the
24117
+ * winner for the session.
23906
24118
  *
23907
24119
  * Why hub-only: agents are not directly addressable by the operator's
23908
24120
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -24057,6 +24269,17 @@ var NotificationEndpointSchema = object({
24057
24269
  /** What the ranking currently resolves to (null when nothing is reachable). */
24058
24270
  resolved: string().nullable()
24059
24271
  });
24272
+ /**
24273
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
24274
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
24275
+ * currently expands to, so the UI can show the effective set either way.
24276
+ */
24277
+ var ViewerEndpointsSchema = object({
24278
+ /** The operator's explicit race set, or empty for AUTO. */
24279
+ baseUrls: array(string()).readonly(),
24280
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
24281
+ resolved: array(string()).readonly()
24282
+ });
24060
24283
  var AllowedAddressesSchema = object({
24061
24284
  /**
24062
24285
  * Allowlist of interface addresses operators have explicitly opted
@@ -24065,6 +24288,20 @@ var AllowedAddressesSchema = object({
24065
24288
  * Network Addresses admin page and persisted by the addon.
24066
24289
  */
24067
24290
  addresses: array(string()).readonly() });
24291
+ var TlsStatusSchema = object({
24292
+ mode: _enum([
24293
+ "generated",
24294
+ "uploaded",
24295
+ "disabled"
24296
+ ]),
24297
+ leafFingerprintSha256: string().nullable(),
24298
+ caFingerprintSha256: string().nullable(),
24299
+ validTo: string().nullable(),
24300
+ sans: array(string()),
24301
+ caCertPem: string().nullable(),
24302
+ reissueError: string().nullable(),
24303
+ restartRequired: boolean()
24304
+ });
24068
24305
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
24069
24306
  /**
24070
24307
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -24074,17 +24311,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
24074
24311
  */
24075
24312
  port: number().int().min(1).max(65535).optional(),
24076
24313
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
24077
- * candidate. Default `true`. */
24314
+ * candidate. Default `false` — loopback is not a client route. */
24078
24315
  includeLoopback: boolean().optional(),
24079
- /** Skip IPv6 entries. Some legacy clients can't parse them.
24080
- * Default `false`. */
24316
+ /** Skip IPv6 entries. Default `false` — the palette includes stable
24317
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
24318
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
24081
24319
  ipv4Only: boolean().optional(),
24082
24320
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
24083
24321
  * Pass `'https'` when the caller is itself loaded over HTTPS
24084
24322
  * to avoid mixed-content blocks in the browser. The public
24085
24323
  * tunnel always emits `https://` regardless. */
24086
24324
  scheme: _enum(["http", "https"]).optional()
24087
- }), 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" });
24325
+ }), 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, {
24326
+ kind: "mutation",
24327
+ auth: "admin"
24328
+ }), method(object({
24329
+ certPem: string().min(1),
24330
+ keyPem: string().min(1),
24331
+ caPem: string().optional()
24332
+ }), TlsStatusSchema, {
24333
+ kind: "mutation",
24334
+ auth: "admin"
24335
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
24336
+ kind: "mutation",
24337
+ auth: "admin"
24338
+ });
24088
24339
  var LockControlStatusSchema = object({
24089
24340
  /** Lifecycle state of the lock. `jammed` means the motor reported
24090
24341
  * failure to reach the target — operator intervention required. */
@@ -25645,7 +25896,12 @@ var PlateInfoSchema = object({
25645
25896
  plateBbox: BoundingBoxSchema.optional(),
25646
25897
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
25647
25898
  keyFrameMediaKey: string().optional(),
25648
- base64: string().optional()
25899
+ base64: string().optional(),
25900
+ /**
25901
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
25902
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
25903
+ */
25904
+ cropUrl: string().optional()
25649
25905
  });
25650
25906
  var MediaFileLiteSchema = object({
25651
25907
  key: string(),
@@ -31169,6 +31425,12 @@ Object.freeze({
31169
31425
  addonId: null,
31170
31426
  access: "view"
31171
31427
  },
31428
+ "deviceManager.getBindingsBatch": {
31429
+ capName: "device-manager",
31430
+ capScope: "system",
31431
+ addonId: null,
31432
+ access: "view"
31433
+ },
31172
31434
  "deviceManager.getChildren": {
31173
31435
  capName: "device-manager",
31174
31436
  capScope: "system",
@@ -31229,6 +31491,12 @@ Object.freeze({
31229
31491
  addonId: null,
31230
31492
  access: "view"
31231
31493
  },
31494
+ "deviceManager.getLinkedDevicesBatch": {
31495
+ capName: "device-manager",
31496
+ capScope: "system",
31497
+ addonId: null,
31498
+ access: "view"
31499
+ },
31232
31500
  "deviceManager.getRoleDisplayDefaults": {
31233
31501
  capName: "device-manager",
31234
31502
  capScope: "system",
@@ -32111,6 +32379,12 @@ Object.freeze({
32111
32379
  addonId: null,
32112
32380
  access: "create"
32113
32381
  },
32382
+ "localNetwork.downloadCa": {
32383
+ capName: "local-network",
32384
+ capScope: "system",
32385
+ addonId: null,
32386
+ access: "view"
32387
+ },
32114
32388
  "localNetwork.getAllowedAddresses": {
32115
32389
  capName: "local-network",
32116
32390
  capScope: "system",
@@ -32135,18 +32409,42 @@ Object.freeze({
32135
32409
  addonId: null,
32136
32410
  access: "view"
32137
32411
  },
32412
+ "localNetwork.getTlsStatus": {
32413
+ capName: "local-network",
32414
+ capScope: "system",
32415
+ addonId: null,
32416
+ access: "view"
32417
+ },
32418
+ "localNetwork.getViewerEndpoints": {
32419
+ capName: "local-network",
32420
+ capScope: "system",
32421
+ addonId: null,
32422
+ access: "view"
32423
+ },
32138
32424
  "localNetwork.list": {
32139
32425
  capName: "local-network",
32140
32426
  capScope: "system",
32141
32427
  addonId: null,
32142
32428
  access: "view"
32143
32429
  },
32430
+ "localNetwork.regenerateCertificate": {
32431
+ capName: "local-network",
32432
+ capScope: "system",
32433
+ addonId: null,
32434
+ access: "create"
32435
+ },
32144
32436
  "localNetwork.resetAllowlistToBestMatch": {
32145
32437
  capName: "local-network",
32146
32438
  capScope: "system",
32147
32439
  addonId: null,
32148
32440
  access: "delete"
32149
32441
  },
32442
+ "localNetwork.revertToGeneratedCertificate": {
32443
+ capName: "local-network",
32444
+ capScope: "system",
32445
+ addonId: null,
32446
+ access: "create"
32447
+ },
32150
32448
  "localNetwork.setAllowedAddresses": {
32151
32449
  capName: "local-network",
32152
32450
  capScope: "system",
@@ -32159,6 +32457,18 @@ Object.freeze({
32159
32457
  addonId: null,
32160
32458
  access: "create"
32161
32459
  },
32460
+ "localNetwork.setViewerEndpoints": {
32461
+ capName: "local-network",
32462
+ capScope: "system",
32463
+ addonId: null,
32464
+ access: "create"
32465
+ },
32466
+ "localNetwork.uploadCertificate": {
32467
+ capName: "local-network",
32468
+ capScope: "system",
32469
+ addonId: null,
32470
+ access: "create"
32471
+ },
32162
32472
  "lockControl.lock": {
32163
32473
  capName: "lock-control",
32164
32474
  capScope: "device",
@@ -32957,6 +33267,12 @@ Object.freeze({
32957
33267
  addonId: null,
32958
33268
  access: "view"
32959
33269
  },
33270
+ "pipelineAnalytics.getGroup": {
33271
+ capName: "pipeline-analytics",
33272
+ capScope: "device",
33273
+ addonId: null,
33274
+ access: "view"
33275
+ },
32960
33276
  "pipelineAnalytics.getKeyEvents": {
32961
33277
  capName: "pipeline-analytics",
32962
33278
  capScope: "device",
@@ -33041,6 +33357,12 @@ Object.freeze({
33041
33357
  addonId: null,
33042
33358
  access: "view"
33043
33359
  },
33360
+ "pipelineAnalytics.listGroups": {
33361
+ capName: "pipeline-analytics",
33362
+ capScope: "device",
33363
+ addonId: null,
33364
+ access: "view"
33365
+ },
33044
33366
  "pipelineAnalytics.listOpsLog": {
33045
33367
  capName: "pipeline-analytics",
33046
33368
  capScope: "device",
@@ -35039,6 +35361,12 @@ Object.freeze({
35039
35361
  addonId: null,
35040
35362
  access: "create"
35041
35363
  },
35364
+ "terminalSession.updateInstance": {
35365
+ capName: "terminal-session",
35366
+ capScope: "system",
35367
+ addonId: null,
35368
+ access: "create"
35369
+ },
35042
35370
  "terminalSession.writeInput": {
35043
35371
  capName: "terminal-session",
35044
35372
  capScope: "system",
@@ -35816,6 +36144,11 @@ Object.freeze({
35816
36144
  form: "single",
35817
36145
  optional: false
35818
36146
  }],
36147
+ "deviceManager.getBindingsBatch": [{
36148
+ name: "deviceIds",
36149
+ form: "array",
36150
+ optional: false
36151
+ }],
35819
36152
  "deviceManager.getChildren": [{
35820
36153
  name: "parentDeviceId",
35821
36154
  form: "single",
@@ -35861,6 +36194,11 @@ Object.freeze({
35861
36194
  form: "single",
35862
36195
  optional: false
35863
36196
  }],
36197
+ "deviceManager.getLinkedDevicesBatch": [{
36198
+ name: "deviceIds",
36199
+ form: "array",
36200
+ optional: false
36201
+ }],
35864
36202
  "deviceManager.getSettingsSchema": [{
35865
36203
  name: "deviceId",
35866
36204
  form: "single",
@@ -35881,6 +36219,11 @@ Object.freeze({
35881
36219
  form: "single",
35882
36220
  optional: false
35883
36221
  }],
36222
+ "deviceManager.listAll": [{
36223
+ name: "deviceIds",
36224
+ form: "array",
36225
+ optional: true
36226
+ }],
35884
36227
  "deviceManager.loadConfig": [{
35885
36228
  name: "deviceId",
35886
36229
  form: "single",
@@ -36454,6 +36797,11 @@ Object.freeze({
36454
36797
  form: "single",
36455
36798
  optional: false
36456
36799
  }],
36800
+ "pipelineAnalytics.getGroup": [{
36801
+ name: "deviceId",
36802
+ form: "single",
36803
+ optional: false
36804
+ }],
36457
36805
  "pipelineAnalytics.getKeyEvents": [{
36458
36806
  name: "deviceId",
36459
36807
  form: "single",
@@ -36509,6 +36857,11 @@ Object.freeze({
36509
36857
  form: "array",
36510
36858
  optional: false
36511
36859
  }],
36860
+ "pipelineAnalytics.listGroups": [{
36861
+ name: "deviceIds",
36862
+ form: "array",
36863
+ optional: false
36864
+ }],
36512
36865
  "pipelineAnalytics.listOpsLog": [{
36513
36866
  name: "deviceId",
36514
36867
  form: "single",
@@ -37526,6 +37879,35 @@ Object.freeze(Object.fromEntries([{
37526
37879
  }]
37527
37880
  }].map((s) => [s.stepId, s.defaultModelId])));
37528
37881
  string().min(1);
37882
+ var CLUSTER_STEP_SETTING_FIELDS = [{
37883
+ stepId: "face-embedding",
37884
+ key: "minLandmarkFaceSize",
37885
+ label: "Min face size for recognition (detection px)",
37886
+ 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.",
37887
+ type: "slider",
37888
+ min: 0,
37889
+ max: 64,
37890
+ step: 2,
37891
+ default: 24
37892
+ }];
37893
+ function clusterStepSettingKey(stepId, fieldKey) {
37894
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
37895
+ }
37896
+ var ClusterSettingNumberSchema = number().finite();
37897
+ function readClusterStepSettings(config) {
37898
+ const out = {};
37899
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
37900
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
37901
+ const value = parsed.success ? parsed.data : field.default;
37902
+ const existing = out[field.stepId] ?? {};
37903
+ out[field.stepId] = {
37904
+ ...existing,
37905
+ [field.key]: value
37906
+ };
37907
+ }
37908
+ return out;
37909
+ }
37910
+ readClusterStepSettings({});
37529
37911
  object({
37530
37912
  /**
37531
37913
  * Fraction of the box's own size added on EACH side before cutting.