@camstack/addon-provider-rtsp 1.2.26 → 1.2.28

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
@@ -5825,6 +5825,13 @@ var BaseAddon = class {
5825
5825
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5826
5826
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5827
5827
  _registeredCapNames = [];
5828
+ /**
5829
+ * True only after `readAddonStore` actually answered. Constructor
5830
+ * defaults look like stored config when the store is down — a forked
5831
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5832
+ * mode, 2026-08-25) is not "the operator chose this".
5833
+ */
5834
+ settingsStoreReady = false;
5828
5835
  /** Default config values. Provided via constructor. */
5829
5836
  defaults;
5830
5837
  constructor(defaults) {
@@ -6225,7 +6232,9 @@ var BaseAddon = class {
6225
6232
  ];
6226
6233
  let lastErr;
6227
6234
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6228
- return await settings.readAddonStore() ?? {};
6235
+ const stored = await settings.readAddonStore() ?? {};
6236
+ this.settingsStoreReady = true;
6237
+ return stored;
6229
6238
  } catch (err) {
6230
6239
  lastErr = err;
6231
6240
  const msg = err instanceof Error ? err.message : String(err);
@@ -6233,6 +6242,7 @@ var BaseAddon = class {
6233
6242
  if (attempt === delaysMs.length) break;
6234
6243
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6235
6244
  }
6245
+ this.settingsStoreReady = false;
6236
6246
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6237
6247
  return {};
6238
6248
  }
@@ -8076,6 +8086,15 @@ var LabelDefinitionSchema = object({
8076
8086
  description: string().optional(),
8077
8087
  icon: string().optional()
8078
8088
  });
8089
+ var ClassMapDefinitionSchema = object({
8090
+ mapping: record(string(), _enum([
8091
+ "person",
8092
+ "vehicle",
8093
+ "animal",
8094
+ "package"
8095
+ ])),
8096
+ preserveOriginal: boolean()
8097
+ });
8079
8098
  var MODEL_FORMATS = [
8080
8099
  "onnx",
8081
8100
  "coreml",
@@ -8159,6 +8178,12 @@ var ModelVariantGroupSchema = object({
8159
8178
  */
8160
8179
  resolution: number().int().positive().optional()
8161
8180
  });
8181
+ var ModelProviderIdSchema = _enum([
8182
+ "camstack",
8183
+ "frigate",
8184
+ "scrypted",
8185
+ "custom"
8186
+ ]);
8162
8187
  var ModelCatalogEntrySchema = object({
8163
8188
  id: string(),
8164
8189
  name: string(),
@@ -8254,7 +8279,19 @@ var ModelCatalogEntrySchema = object({
8254
8279
  * `id` stays the source of truth for resolution/download/persistence; grouping
8255
8280
  * is a presentation overlay resolved back to an `id`.
8256
8281
  */
8257
- group: ModelVariantGroupSchema.optional()
8282
+ group: ModelVariantGroupSchema.optional(),
8283
+ /**
8284
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8285
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8286
+ * persisted before this field existed (`inferModelProvider` fills those).
8287
+ */
8288
+ provider: ModelProviderIdSchema.optional(),
8289
+ /**
8290
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8291
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8292
+ * labels already ARE the CamStack macros (Scrypted identity map).
8293
+ */
8294
+ classMap: ClassMapDefinitionSchema.optional()
8258
8295
  });
8259
8296
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8260
8297
  format: literal("openvino"),
@@ -8283,7 +8320,8 @@ var ModelConvertMetadataSchema = object({
8283
8320
  "ocr",
8284
8321
  "segmentation"
8285
8322
  ]),
8286
- faceAlignment: boolean().optional()
8323
+ faceAlignment: boolean().optional(),
8324
+ classMap: ClassMapDefinitionSchema.optional()
8287
8325
  });
8288
8326
  var ConvertResultSchema = object({
8289
8327
  entry: ModelCatalogEntrySchema,
@@ -12040,6 +12078,27 @@ var LinkedDeviceSchema = object({
12040
12078
  features: array(string()),
12041
12079
  producesTrackedEvents: boolean().optional()
12042
12080
  });
12081
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
12082
+ * The batch answer needs the tag; the single-device answer already has it
12083
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
12084
+ var LinkedDevicesForDeviceSchema = object({
12085
+ deviceId: number(),
12086
+ mode: LinkedDevicesModeSchema,
12087
+ devices: array(LinkedDeviceSchema)
12088
+ });
12089
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
12090
+ * `getAllBindings` all answer in. Declared once: three copies of the same
12091
+ * object literal is exactly how the three drift apart. */
12092
+ var DeviceBindingsForDeviceSchema = object({
12093
+ deviceId: number(),
12094
+ entries: array(object({
12095
+ capName: string(),
12096
+ kind: _enum(["native", "wrapped"]),
12097
+ providerAddonId: string(),
12098
+ providerNodeId: string(),
12099
+ nativeAddonId: string()
12100
+ }))
12101
+ });
12043
12102
  var SavedDeviceRowSchema = object({
12044
12103
  /** Numeric id reserved at allocateDeviceId time. */
12045
12104
  id: number(),
@@ -12265,11 +12324,25 @@ method(object({
12265
12324
  projection: _enum(["full", "slim"]).optional(),
12266
12325
  /** Return only camera devices. Filtering server-side instead of
12267
12326
  * shipping 293 rows to find 12. */
12268
- isCamera: boolean().optional()
12327
+ isCamera: boolean().optional(),
12328
+ /**
12329
+ * Return only these device ids. For the caller that already KNOWS the
12330
+ * handful it wants and needs a field the id-bearing answer does not
12331
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12332
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12333
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12334
+ * refetches on the reconcile interval, on a phone.
12335
+ *
12336
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12337
+ * keys rather than rejecting them (verified against the live hub
12338
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12339
+ * it answers today and the caller filters as it already does.
12340
+ */
12341
+ deviceIds: array(number()).optional()
12269
12342
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12270
12343
  mode: LinkedDevicesModeSchema,
12271
12344
  devices: array(LinkedDeviceSchema)
12272
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12345
+ })), 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({
12273
12346
  deviceId: number(),
12274
12347
  values: record(string(), unknown())
12275
12348
  }), object({ success: literal(true) }), {
@@ -12296,25 +12369,7 @@ method(object({
12296
12369
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
12297
12370
  kind: "mutation",
12298
12371
  auth: "admin"
12299
- }), method(object({ deviceId: number() }), object({
12300
- deviceId: number(),
12301
- entries: array(object({
12302
- capName: string(),
12303
- kind: _enum(["native", "wrapped"]),
12304
- providerAddonId: string(),
12305
- providerNodeId: string(),
12306
- nativeAddonId: string()
12307
- }))
12308
- })), method(object({}), array(object({
12309
- deviceId: number(),
12310
- entries: array(object({
12311
- capName: string(),
12312
- kind: _enum(["native", "wrapped"]),
12313
- providerAddonId: string(),
12314
- providerNodeId: string(),
12315
- nativeAddonId: string()
12316
- }))
12317
- }))), method(object({
12372
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12318
12373
  deviceId: number(),
12319
12374
  capName: string(),
12320
12375
  wrapperAddonId: string(),
@@ -14724,12 +14779,15 @@ var NcOccupancyConditionSchema = object({
14724
14779
  * there is no second switch that can disagree with the first and every rule
14725
14780
  * authored before the decision migrates for free (`audioModeOf`):
14726
14781
  *
14727
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14728
- * classifier labels with one of them. No window, no percentage:
14729
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14730
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14731
- * the analyzer's (`classificationMinScore`, per device) a label only
14732
- * reaches this condition if the classifier was already confident enough.
14782
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14783
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14784
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14785
+ * frames is the wrong question for a classifier that labels 1–3 frames
14786
+ * per episode. The count window is the brake that drops a single-frame
14787
+ * false positive; the rule's own `throttle` cooldown is the other. The
14788
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14789
+ * per device) — a label only reaches this condition if the classifier was
14790
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14733
14791
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14734
14792
  * the condition: at least `hitPercent`% of the samples over
14735
14793
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14756,14 +14814,22 @@ var NcOccupancyConditionSchema = object({
14756
14814
  * an operator who typed `dog` mean the same thing.
14757
14815
  */
14758
14816
  var NcAudioConditionSchema = object({
14759
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14817
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14760
14818
  labels: array(string().min(1)).min(1).optional(),
14761
14819
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14762
14820
  dbThreshold: number().min(-96).max(0).optional(),
14763
14821
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14764
14822
  hitPercent: number().int().min(1).max(100).default(60),
14765
14823
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14766
- samplingSeconds: number().int().min(1).max(300).default(10)
14824
+ samplingSeconds: number().int().min(1).max(300).default(10),
14825
+ /**
14826
+ * LABEL MODE: how many labelled frames must land inside
14827
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14828
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14829
+ */
14830
+ confirmHits: number().int().min(1).max(20).optional(),
14831
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14832
+ confirmWindowSec: number().int().min(1).max(60).optional()
14767
14833
  });
14768
14834
  /**
14769
14835
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -17137,6 +17203,46 @@ var RecentTracksPageSchema = object({
17137
17203
  /** Cursor for the next page, or null when this page is the last. */
17138
17204
  nextCursor: string().nullable()
17139
17205
  });
17206
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
17207
+ var LIST_GROUPS_MAX_LIMIT = 100;
17208
+ var AnalyticsGroupRecordSchema = object({
17209
+ id: string(),
17210
+ deviceId: number().int(),
17211
+ openedAt: number().int(),
17212
+ closedAt: number().int(),
17213
+ timestamp: number().int(),
17214
+ memberCount: number().int(),
17215
+ memberTrackIds: array(string()).readonly(),
17216
+ className: string(),
17217
+ classes: array(string()).readonly(),
17218
+ /** Relative event-media path, or null when the group has no picture yet. */
17219
+ mediaUrl: string().nullable(),
17220
+ singleton: boolean()
17221
+ });
17222
+ var AnalyticsGroupMemberSchema = object({
17223
+ trackId: string(),
17224
+ deviceId: number().int(),
17225
+ className: string(),
17226
+ firstSeen: number().int(),
17227
+ lastSeen: number().int(),
17228
+ mediaUrl: string().nullable()
17229
+ });
17230
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
17231
+ var ListGroupsQueryInput = object({
17232
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
17233
+ deviceIds: array(number()),
17234
+ /** Window lower bound on `closedAt` (inclusive). */
17235
+ since: number().optional(),
17236
+ /** Window upper bound on `openedAt` (inclusive). */
17237
+ until: number().optional(),
17238
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
17239
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
17240
+ cursor: string().optional()
17241
+ });
17242
+ var ListGroupsPageSchema = object({
17243
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
17244
+ nextCursor: string().nullable()
17245
+ });
17140
17246
  var KeyEventQueryInput = object({
17141
17247
  deviceId: number(),
17142
17248
  /** Window lower bound (track firstSeen ≥ since). */
@@ -17212,7 +17318,9 @@ var TrackCascadeCountsSchema = object({
17212
17318
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
17213
17319
  plates: number().int(),
17214
17320
  /** Per-track CLIP search vectors removed (best-effort). */
17215
- embeddings: number().int()
17321
+ embeddings: number().int(),
17322
+ /** Group membership + group rows removed with their last member (best-effort). */
17323
+ groups: number().int()
17216
17324
  });
17217
17325
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
17218
17326
  var DiskReconcileCountsSchema = object({
@@ -17358,7 +17466,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17358
17466
  * stationary registry). Default false: the timeline lists passages,
17359
17467
  * not parking records (operator decision, 2026-08-15). */
17360
17468
  includeStationary: boolean().optional()
17361
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17469
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17470
+ deviceId: number(),
17471
+ groupId: string().min(1)
17472
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17362
17473
  kind: "mutation",
17363
17474
  auth: "admin"
17364
17475
  }), 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({
@@ -17576,6 +17687,33 @@ var NativeCropRefSchema = object({
17576
17687
  h: number()
17577
17688
  })
17578
17689
  });
17690
+ object({
17691
+ crop: object({
17692
+ left: number(),
17693
+ top: number(),
17694
+ width: number().positive(),
17695
+ height: number().positive()
17696
+ }).optional(),
17697
+ content: object({
17698
+ width: number().int().positive(),
17699
+ height: number().int().positive()
17700
+ }),
17701
+ fit: _enum(["stretch", "contain"]),
17702
+ format: _enum([
17703
+ "rgb",
17704
+ "gray",
17705
+ "jpeg"
17706
+ ])
17707
+ });
17708
+ var FrameRefSchema = object({
17709
+ registryId: string().min(1),
17710
+ id: string().min(1),
17711
+ width: number().int().positive(),
17712
+ height: number().int().positive(),
17713
+ format: _enum(["rgb", "gray"]),
17714
+ timestamp: number(),
17715
+ capturedAt: number().optional()
17716
+ });
17579
17717
  var ModelFormatSchema$1 = _enum([
17580
17718
  "onnx",
17581
17719
  "coreml",
@@ -17641,7 +17779,8 @@ var PipelineModelOptionSchema = object({
17641
17779
  sizeMB: number()
17642
17780
  })),
17643
17781
  group: ModelVariantGroupSchema.optional(),
17644
- legacy: boolean().optional()
17782
+ legacy: boolean().optional(),
17783
+ provider: ModelProviderIdSchema.optional()
17645
17784
  });
17646
17785
  var ConfigFieldBridge = custom();
17647
17786
  var PipelineAddonSchemaSchema = object({
@@ -17820,6 +17959,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
17820
17959
  steps: array(PipelineStepInputSchema).min(1),
17821
17960
  frame: FrameInputSchema.optional(),
17822
17961
  /**
17962
+ * Process-local lazy frame. Valid only when caller and provider resolve
17963
+ * in the same execution-group process; split/cross-node callers use
17964
+ * `frame`/`image` inline compatibility instead.
17965
+ */
17966
+ frameRef: FrameRefSchema.optional(),
17967
+ /**
17823
17968
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
17824
17969
  * the decoded pixels live in. One more member of the one-of
17825
17970
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -18115,7 +18260,10 @@ var NativeCropResultSchema = object({
18115
18260
  * Which source served this crop, so a quality-sensitive consumer (the native
18116
18261
  * `keyFrame`) can reject a degraded fallback:
18117
18262
  * - `native` — cut from the decode worker's retained NATIVE surface (the
18118
- * quality path).
18263
+ * quality path). A subject-tile serve is also native-resolution and stays
18264
+ * `native` here: the public enum cannot name `tile` without a breaking cap
18265
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
18266
+ * internal crop result (`nativeHits` vs `tileHits`).
18119
18267
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
18120
18268
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
18121
18269
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -18606,12 +18754,41 @@ var RunnerLocalLoadSchema = object({
18606
18754
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
18607
18755
  * working unchanged when they switch to reading from the runner cap.
18608
18756
  */
18757
+ var FrameLazyCountersSchema = object({
18758
+ framesDecoded: number(),
18759
+ framesAdmitted: number(),
18760
+ framesDroppedPixelFree: number(),
18761
+ viewsMaterialized: number(),
18762
+ viewsSkipped: number(),
18763
+ workerToRunnerBytes: number(),
18764
+ runnerToPoolRawBytes: number(),
18765
+ runnerToPoolJpegBytes: number(),
18766
+ onDemandFullFrameRequests: number(),
18767
+ onDemandCropRequests: number(),
18768
+ nativeHits: number(),
18769
+ nativeMisses: number(),
18770
+ tileHits: number(),
18771
+ tileMisses: number(),
18772
+ fallbackHits: number(),
18773
+ fallbackMisses: number(),
18774
+ retainedWritesAvoided: number(),
18775
+ residentRefs: number(),
18776
+ residentBytes: number(),
18777
+ releases: number(),
18778
+ evictions: number(),
18779
+ staleMisses: number()
18780
+ });
18781
+ var FrameLazyMetricsSchema = object({
18782
+ node: FrameLazyCountersSchema,
18783
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
18784
+ });
18609
18785
  var RunnerLocalMetricsSchema = object({
18610
18786
  nodeId: string(),
18611
18787
  activeCameras: number(),
18612
18788
  throttledCameras: number(),
18613
18789
  avgInferenceTimeMs: number(),
18614
- queueDepth: number()
18790
+ queueDepth: number(),
18791
+ frameLazy: FrameLazyMetricsSchema.optional()
18615
18792
  });
18616
18793
  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({
18617
18794
  handle: FrameHandleSchema,
@@ -20015,6 +20192,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
20015
20192
  location: StorageLocationSchema,
20016
20193
  relativePath: string()
20017
20194
  }), _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" });
20195
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
20196
+ var ProfileSettingsSchemaBridge = unknown().nullable();
20197
+ var ProfileSettingsBagSchema = record(string(), unknown());
20018
20198
  /**
20019
20199
  * A live terminal session hosted by the provider addon. Output and input do
20020
20200
  * NOT flow through the capability — they use the addon data plane
@@ -20044,7 +20224,14 @@ var TerminalSessionInfoSchema = object({
20044
20224
  var TerminalProfileInfoSchema = object({
20045
20225
  profileId: string(),
20046
20226
  label: string(),
20047
- description: string().optional()
20227
+ description: string().optional(),
20228
+ /** Spawn defaults the instance form copies on create. */
20229
+ executable: string().optional(),
20230
+ args: array(string()).readonly().optional(),
20231
+ cwd: string().optional(),
20232
+ environment: array(string()).readonly().optional(),
20233
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
20234
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
20048
20235
  });
20049
20236
  /**
20050
20237
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -20057,7 +20244,12 @@ var TerminalInstanceInfoSchema = object({
20057
20244
  profileId: string(),
20058
20245
  profileLabel: string(),
20059
20246
  name: string(),
20060
- enabled: boolean()
20247
+ enabled: boolean(),
20248
+ executable: string(),
20249
+ args: array(string()).readonly(),
20250
+ cwd: string(),
20251
+ environment: array(string()).readonly(),
20252
+ profileSettings: ProfileSettingsBagSchema
20061
20253
  });
20062
20254
  var TerminalLegacyCameraSchema = object({
20063
20255
  stableId: string(),
@@ -20087,7 +20279,23 @@ var TerminalOutputBatchSchema = object({
20087
20279
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
20088
20280
  targetNodeId: string().min(1),
20089
20281
  profileId: string().min(1),
20090
- name: string().trim().min(1).max(160).optional()
20282
+ name: string().trim().min(1).max(160).optional(),
20283
+ executable: string().max(1024).optional(),
20284
+ args: array(string().max(2048)).max(64).optional(),
20285
+ cwd: string().max(1024).optional(),
20286
+ environment: array(string().max(4096)).max(64).optional(),
20287
+ profileSettings: ProfileSettingsBagSchema.optional()
20288
+ }), TerminalInstanceInfoSchema, {
20289
+ kind: "mutation",
20290
+ auth: "admin"
20291
+ }), method(object({
20292
+ instanceId: string().min(1),
20293
+ name: string().trim().min(1).max(160).optional(),
20294
+ executable: string().max(1024).optional(),
20295
+ args: array(string().max(2048)).max(64).optional(),
20296
+ cwd: string().max(1024).optional(),
20297
+ environment: array(string().max(4096)).max(64).optional(),
20298
+ profileSettings: ProfileSettingsBagSchema.optional()
20091
20299
  }), TerminalInstanceInfoSchema, {
20092
20300
  kind: "mutation",
20093
20301
  auth: "admin"
@@ -20109,7 +20317,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
20109
20317
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
20110
20318
  profileId: string(),
20111
20319
  cols: number().int().positive(),
20112
- rows: number().int().positive()
20320
+ rows: number().int().positive(),
20321
+ executable: string().max(1024).optional(),
20322
+ args: array(string().max(2048)).max(64).optional(),
20323
+ cwd: string().max(1024).optional(),
20324
+ environment: array(string().max(4096)).max(64).optional()
20113
20325
  }), TerminalSessionInfoSchema, {
20114
20326
  kind: "mutation",
20115
20327
  auth: "admin"
@@ -23992,10 +24204,10 @@ var lawnMowerControlCapability = {
23992
24204
  *
23993
24205
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
23994
24206
  * to receive an ordered list of candidate base URLs it should race
23995
- * on connect — LAN IPv4 first (lowest latency when on same network),
23996
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
23997
- * race them with short timeouts and stick with the winner for the
23998
- * session.
24207
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
24208
+ * when on the same network), then public hostname (if a tunnel is
24209
+ * up). The SDK can race them with short timeouts and stick with the
24210
+ * winner for the session.
23999
24211
  *
24000
24212
  * Why hub-only: agents are not directly addressable by the operator's
24001
24213
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -24150,6 +24362,17 @@ var NotificationEndpointSchema = object({
24150
24362
  /** What the ranking currently resolves to (null when nothing is reachable). */
24151
24363
  resolved: string().nullable()
24152
24364
  });
24365
+ /**
24366
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
24367
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
24368
+ * currently expands to, so the UI can show the effective set either way.
24369
+ */
24370
+ var ViewerEndpointsSchema = object({
24371
+ /** The operator's explicit race set, or empty for AUTO. */
24372
+ baseUrls: array(string()).readonly(),
24373
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
24374
+ resolved: array(string()).readonly()
24375
+ });
24153
24376
  var AllowedAddressesSchema = object({
24154
24377
  /**
24155
24378
  * Allowlist of interface addresses operators have explicitly opted
@@ -24158,6 +24381,20 @@ var AllowedAddressesSchema = object({
24158
24381
  * Network Addresses admin page and persisted by the addon.
24159
24382
  */
24160
24383
  addresses: array(string()).readonly() });
24384
+ var TlsStatusSchema = object({
24385
+ mode: _enum([
24386
+ "generated",
24387
+ "uploaded",
24388
+ "disabled"
24389
+ ]),
24390
+ leafFingerprintSha256: string().nullable(),
24391
+ caFingerprintSha256: string().nullable(),
24392
+ validTo: string().nullable(),
24393
+ sans: array(string()),
24394
+ caCertPem: string().nullable(),
24395
+ reissueError: string().nullable(),
24396
+ restartRequired: boolean()
24397
+ });
24161
24398
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
24162
24399
  /**
24163
24400
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -24167,17 +24404,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
24167
24404
  */
24168
24405
  port: number().int().min(1).max(65535).optional(),
24169
24406
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
24170
- * candidate. Default `true`. */
24407
+ * candidate. Default `false` — loopback is not a client route. */
24171
24408
  includeLoopback: boolean().optional(),
24172
- /** Skip IPv6 entries. Some legacy clients can't parse them.
24173
- * Default `false`. */
24409
+ /** Skip IPv6 entries. Default `false` the palette includes stable
24410
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
24411
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
24174
24412
  ipv4Only: boolean().optional(),
24175
24413
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
24176
24414
  * Pass `'https'` when the caller is itself loaded over HTTPS
24177
24415
  * to avoid mixed-content blocks in the browser. The public
24178
24416
  * tunnel always emits `https://` regardless. */
24179
24417
  scheme: _enum(["http", "https"]).optional()
24180
- }), 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" });
24418
+ }), 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, {
24419
+ kind: "mutation",
24420
+ auth: "admin"
24421
+ }), method(object({
24422
+ certPem: string().min(1),
24423
+ keyPem: string().min(1),
24424
+ caPem: string().optional()
24425
+ }), TlsStatusSchema, {
24426
+ kind: "mutation",
24427
+ auth: "admin"
24428
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
24429
+ kind: "mutation",
24430
+ auth: "admin"
24431
+ });
24181
24432
  var LockControlStatusSchema = object({
24182
24433
  /** Lifecycle state of the lock. `jammed` means the motor reported
24183
24434
  * failure to reach the target — operator intervention required. */
@@ -25738,7 +25989,12 @@ var PlateInfoSchema = object({
25738
25989
  plateBbox: BoundingBoxSchema.optional(),
25739
25990
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
25740
25991
  keyFrameMediaKey: string().optional(),
25741
- base64: string().optional()
25992
+ base64: string().optional(),
25993
+ /**
25994
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
25995
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
25996
+ */
25997
+ cropUrl: string().optional()
25742
25998
  });
25743
25999
  var MediaFileLiteSchema = object({
25744
26000
  key: string(),
@@ -31328,6 +31584,12 @@ Object.freeze({
31328
31584
  addonId: null,
31329
31585
  access: "view"
31330
31586
  },
31587
+ "deviceManager.getBindingsBatch": {
31588
+ capName: "device-manager",
31589
+ capScope: "system",
31590
+ addonId: null,
31591
+ access: "view"
31592
+ },
31331
31593
  "deviceManager.getChildren": {
31332
31594
  capName: "device-manager",
31333
31595
  capScope: "system",
@@ -31388,6 +31650,12 @@ Object.freeze({
31388
31650
  addonId: null,
31389
31651
  access: "view"
31390
31652
  },
31653
+ "deviceManager.getLinkedDevicesBatch": {
31654
+ capName: "device-manager",
31655
+ capScope: "system",
31656
+ addonId: null,
31657
+ access: "view"
31658
+ },
31391
31659
  "deviceManager.getRoleDisplayDefaults": {
31392
31660
  capName: "device-manager",
31393
31661
  capScope: "system",
@@ -32270,6 +32538,12 @@ Object.freeze({
32270
32538
  addonId: null,
32271
32539
  access: "create"
32272
32540
  },
32541
+ "localNetwork.downloadCa": {
32542
+ capName: "local-network",
32543
+ capScope: "system",
32544
+ addonId: null,
32545
+ access: "view"
32546
+ },
32273
32547
  "localNetwork.getAllowedAddresses": {
32274
32548
  capName: "local-network",
32275
32549
  capScope: "system",
@@ -32294,18 +32568,42 @@ Object.freeze({
32294
32568
  addonId: null,
32295
32569
  access: "view"
32296
32570
  },
32571
+ "localNetwork.getTlsStatus": {
32572
+ capName: "local-network",
32573
+ capScope: "system",
32574
+ addonId: null,
32575
+ access: "view"
32576
+ },
32577
+ "localNetwork.getViewerEndpoints": {
32578
+ capName: "local-network",
32579
+ capScope: "system",
32580
+ addonId: null,
32581
+ access: "view"
32582
+ },
32297
32583
  "localNetwork.list": {
32298
32584
  capName: "local-network",
32299
32585
  capScope: "system",
32300
32586
  addonId: null,
32301
32587
  access: "view"
32302
32588
  },
32589
+ "localNetwork.regenerateCertificate": {
32590
+ capName: "local-network",
32591
+ capScope: "system",
32592
+ addonId: null,
32593
+ access: "create"
32594
+ },
32303
32595
  "localNetwork.resetAllowlistToBestMatch": {
32304
32596
  capName: "local-network",
32305
32597
  capScope: "system",
32306
32598
  addonId: null,
32307
32599
  access: "delete"
32308
32600
  },
32601
+ "localNetwork.revertToGeneratedCertificate": {
32602
+ capName: "local-network",
32603
+ capScope: "system",
32604
+ addonId: null,
32605
+ access: "create"
32606
+ },
32309
32607
  "localNetwork.setAllowedAddresses": {
32310
32608
  capName: "local-network",
32311
32609
  capScope: "system",
@@ -32318,6 +32616,18 @@ Object.freeze({
32318
32616
  addonId: null,
32319
32617
  access: "create"
32320
32618
  },
32619
+ "localNetwork.setViewerEndpoints": {
32620
+ capName: "local-network",
32621
+ capScope: "system",
32622
+ addonId: null,
32623
+ access: "create"
32624
+ },
32625
+ "localNetwork.uploadCertificate": {
32626
+ capName: "local-network",
32627
+ capScope: "system",
32628
+ addonId: null,
32629
+ access: "create"
32630
+ },
32321
32631
  "lockControl.lock": {
32322
32632
  capName: "lock-control",
32323
32633
  capScope: "device",
@@ -33116,6 +33426,12 @@ Object.freeze({
33116
33426
  addonId: null,
33117
33427
  access: "view"
33118
33428
  },
33429
+ "pipelineAnalytics.getGroup": {
33430
+ capName: "pipeline-analytics",
33431
+ capScope: "device",
33432
+ addonId: null,
33433
+ access: "view"
33434
+ },
33119
33435
  "pipelineAnalytics.getKeyEvents": {
33120
33436
  capName: "pipeline-analytics",
33121
33437
  capScope: "device",
@@ -33200,6 +33516,12 @@ Object.freeze({
33200
33516
  addonId: null,
33201
33517
  access: "view"
33202
33518
  },
33519
+ "pipelineAnalytics.listGroups": {
33520
+ capName: "pipeline-analytics",
33521
+ capScope: "device",
33522
+ addonId: null,
33523
+ access: "view"
33524
+ },
33203
33525
  "pipelineAnalytics.listOpsLog": {
33204
33526
  capName: "pipeline-analytics",
33205
33527
  capScope: "device",
@@ -35198,6 +35520,12 @@ Object.freeze({
35198
35520
  addonId: null,
35199
35521
  access: "create"
35200
35522
  },
35523
+ "terminalSession.updateInstance": {
35524
+ capName: "terminal-session",
35525
+ capScope: "system",
35526
+ addonId: null,
35527
+ access: "create"
35528
+ },
35201
35529
  "terminalSession.writeInput": {
35202
35530
  capName: "terminal-session",
35203
35531
  capScope: "system",
@@ -35975,6 +36303,11 @@ Object.freeze({
35975
36303
  form: "single",
35976
36304
  optional: false
35977
36305
  }],
36306
+ "deviceManager.getBindingsBatch": [{
36307
+ name: "deviceIds",
36308
+ form: "array",
36309
+ optional: false
36310
+ }],
35978
36311
  "deviceManager.getChildren": [{
35979
36312
  name: "parentDeviceId",
35980
36313
  form: "single",
@@ -36020,6 +36353,11 @@ Object.freeze({
36020
36353
  form: "single",
36021
36354
  optional: false
36022
36355
  }],
36356
+ "deviceManager.getLinkedDevicesBatch": [{
36357
+ name: "deviceIds",
36358
+ form: "array",
36359
+ optional: false
36360
+ }],
36023
36361
  "deviceManager.getSettingsSchema": [{
36024
36362
  name: "deviceId",
36025
36363
  form: "single",
@@ -36040,6 +36378,11 @@ Object.freeze({
36040
36378
  form: "single",
36041
36379
  optional: false
36042
36380
  }],
36381
+ "deviceManager.listAll": [{
36382
+ name: "deviceIds",
36383
+ form: "array",
36384
+ optional: true
36385
+ }],
36043
36386
  "deviceManager.loadConfig": [{
36044
36387
  name: "deviceId",
36045
36388
  form: "single",
@@ -36613,6 +36956,11 @@ Object.freeze({
36613
36956
  form: "single",
36614
36957
  optional: false
36615
36958
  }],
36959
+ "pipelineAnalytics.getGroup": [{
36960
+ name: "deviceId",
36961
+ form: "single",
36962
+ optional: false
36963
+ }],
36616
36964
  "pipelineAnalytics.getKeyEvents": [{
36617
36965
  name: "deviceId",
36618
36966
  form: "single",
@@ -36668,6 +37016,11 @@ Object.freeze({
36668
37016
  form: "array",
36669
37017
  optional: false
36670
37018
  }],
37019
+ "pipelineAnalytics.listGroups": [{
37020
+ name: "deviceIds",
37021
+ form: "array",
37022
+ optional: false
37023
+ }],
36671
37024
  "pipelineAnalytics.listOpsLog": [{
36672
37025
  name: "deviceId",
36673
37026
  form: "single",
@@ -37685,6 +38038,35 @@ Object.freeze(Object.fromEntries([{
37685
38038
  }]
37686
38039
  }].map((s) => [s.stepId, s.defaultModelId])));
37687
38040
  string().min(1);
38041
+ var CLUSTER_STEP_SETTING_FIELDS = [{
38042
+ stepId: "face-embedding",
38043
+ key: "minLandmarkFaceSize",
38044
+ label: "Min face size for recognition (detection px)",
38045
+ 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.",
38046
+ type: "slider",
38047
+ min: 0,
38048
+ max: 64,
38049
+ step: 2,
38050
+ default: 24
38051
+ }];
38052
+ function clusterStepSettingKey(stepId, fieldKey) {
38053
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
38054
+ }
38055
+ var ClusterSettingNumberSchema = number().finite();
38056
+ function readClusterStepSettings(config) {
38057
+ const out = {};
38058
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
38059
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
38060
+ const value = parsed.success ? parsed.data : field.default;
38061
+ const existing = out[field.stepId] ?? {};
38062
+ out[field.stepId] = {
38063
+ ...existing,
38064
+ [field.key]: value
38065
+ };
38066
+ }
38067
+ return out;
38068
+ }
38069
+ readClusterStepSettings({});
37688
38070
  object({
37689
38071
  /**
37690
38072
  * Fraction of the box's own size added on EACH side before cutting.