@camstack/addon-mqtt-broker 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.
@@ -5841,6 +5841,13 @@ var BaseAddon = class {
5841
5841
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5842
5842
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5843
5843
  _registeredCapNames = [];
5844
+ /**
5845
+ * True only after `readAddonStore` actually answered. Constructor
5846
+ * defaults look like stored config when the store is down — a forked
5847
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5848
+ * mode, 2026-08-25) is not "the operator chose this".
5849
+ */
5850
+ settingsStoreReady = false;
5844
5851
  /** Default config values. Provided via constructor. */
5845
5852
  defaults;
5846
5853
  constructor(defaults) {
@@ -6241,7 +6248,9 @@ var BaseAddon = class {
6241
6248
  ];
6242
6249
  let lastErr;
6243
6250
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6244
- return await settings.readAddonStore() ?? {};
6251
+ const stored = await settings.readAddonStore() ?? {};
6252
+ this.settingsStoreReady = true;
6253
+ return stored;
6245
6254
  } catch (err) {
6246
6255
  lastErr = err;
6247
6256
  const msg = err instanceof Error ? err.message : String(err);
@@ -6249,6 +6258,7 @@ var BaseAddon = class {
6249
6258
  if (attempt === delaysMs.length) break;
6250
6259
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6251
6260
  }
6261
+ this.settingsStoreReady = false;
6252
6262
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6253
6263
  return {};
6254
6264
  }
@@ -8044,6 +8054,15 @@ var LabelDefinitionSchema = object({
8044
8054
  description: string().optional(),
8045
8055
  icon: string().optional()
8046
8056
  });
8057
+ var ClassMapDefinitionSchema = object({
8058
+ mapping: record(string(), _enum([
8059
+ "person",
8060
+ "vehicle",
8061
+ "animal",
8062
+ "package"
8063
+ ])),
8064
+ preserveOriginal: boolean()
8065
+ });
8047
8066
  var MODEL_FORMATS = [
8048
8067
  "onnx",
8049
8068
  "coreml",
@@ -8127,6 +8146,12 @@ var ModelVariantGroupSchema = object({
8127
8146
  */
8128
8147
  resolution: number().int().positive().optional()
8129
8148
  });
8149
+ var ModelProviderIdSchema = _enum([
8150
+ "camstack",
8151
+ "frigate",
8152
+ "scrypted",
8153
+ "custom"
8154
+ ]);
8130
8155
  var ModelCatalogEntrySchema = object({
8131
8156
  id: string(),
8132
8157
  name: string(),
@@ -8222,7 +8247,19 @@ var ModelCatalogEntrySchema = object({
8222
8247
  * `id` stays the source of truth for resolution/download/persistence; grouping
8223
8248
  * is a presentation overlay resolved back to an `id`.
8224
8249
  */
8225
- group: ModelVariantGroupSchema.optional()
8250
+ group: ModelVariantGroupSchema.optional(),
8251
+ /**
8252
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8253
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8254
+ * persisted before this field existed (`inferModelProvider` fills those).
8255
+ */
8256
+ provider: ModelProviderIdSchema.optional(),
8257
+ /**
8258
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8259
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8260
+ * labels already ARE the CamStack macros (Scrypted identity map).
8261
+ */
8262
+ classMap: ClassMapDefinitionSchema.optional()
8226
8263
  });
8227
8264
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8228
8265
  format: literal("openvino"),
@@ -8251,7 +8288,8 @@ var ModelConvertMetadataSchema = object({
8251
8288
  "ocr",
8252
8289
  "segmentation"
8253
8290
  ]),
8254
- faceAlignment: boolean().optional()
8291
+ faceAlignment: boolean().optional(),
8292
+ classMap: ClassMapDefinitionSchema.optional()
8255
8293
  });
8256
8294
  var ConvertResultSchema = object({
8257
8295
  entry: ModelCatalogEntrySchema,
@@ -11793,6 +11831,27 @@ var LinkedDeviceSchema = object({
11793
11831
  features: array(string()),
11794
11832
  producesTrackedEvents: boolean().optional()
11795
11833
  });
11834
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
11835
+ * The batch answer needs the tag; the single-device answer already has it
11836
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
11837
+ var LinkedDevicesForDeviceSchema = object({
11838
+ deviceId: number(),
11839
+ mode: LinkedDevicesModeSchema,
11840
+ devices: array(LinkedDeviceSchema)
11841
+ });
11842
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
11843
+ * `getAllBindings` all answer in. Declared once: three copies of the same
11844
+ * object literal is exactly how the three drift apart. */
11845
+ var DeviceBindingsForDeviceSchema = object({
11846
+ deviceId: number(),
11847
+ entries: array(object({
11848
+ capName: string(),
11849
+ kind: _enum(["native", "wrapped"]),
11850
+ providerAddonId: string(),
11851
+ providerNodeId: string(),
11852
+ nativeAddonId: string()
11853
+ }))
11854
+ });
11796
11855
  var SavedDeviceRowSchema = object({
11797
11856
  /** Numeric id reserved at allocateDeviceId time. */
11798
11857
  id: number(),
@@ -12018,11 +12077,25 @@ method(object({
12018
12077
  projection: _enum(["full", "slim"]).optional(),
12019
12078
  /** Return only camera devices. Filtering server-side instead of
12020
12079
  * shipping 293 rows to find 12. */
12021
- isCamera: boolean().optional()
12080
+ isCamera: boolean().optional(),
12081
+ /**
12082
+ * Return only these device ids. For the caller that already KNOWS the
12083
+ * handful it wants and needs a field the id-bearing answer does not
12084
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12085
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12086
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12087
+ * refetches on the reconcile interval, on a phone.
12088
+ *
12089
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12090
+ * keys rather than rejecting them (verified against the live hub
12091
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12092
+ * it answers today and the caller filters as it already does.
12093
+ */
12094
+ deviceIds: array(number()).optional()
12022
12095
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12023
12096
  mode: LinkedDevicesModeSchema,
12024
12097
  devices: array(LinkedDeviceSchema)
12025
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12098
+ })), 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({
12026
12099
  deviceId: number(),
12027
12100
  values: record(string(), unknown())
12028
12101
  }), object({ success: literal(true) }), {
@@ -12049,25 +12122,7 @@ method(object({
12049
12122
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
12050
12123
  kind: "mutation",
12051
12124
  auth: "admin"
12052
- }), method(object({ deviceId: number() }), object({
12053
- deviceId: number(),
12054
- entries: array(object({
12055
- capName: string(),
12056
- kind: _enum(["native", "wrapped"]),
12057
- providerAddonId: string(),
12058
- providerNodeId: string(),
12059
- nativeAddonId: string()
12060
- }))
12061
- })), method(object({}), array(object({
12062
- deviceId: number(),
12063
- entries: array(object({
12064
- capName: string(),
12065
- kind: _enum(["native", "wrapped"]),
12066
- providerAddonId: string(),
12067
- providerNodeId: string(),
12068
- nativeAddonId: string()
12069
- }))
12070
- }))), method(object({
12125
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12071
12126
  deviceId: number(),
12072
12127
  capName: string(),
12073
12128
  wrapperAddonId: string(),
@@ -14458,12 +14513,15 @@ var NcOccupancyConditionSchema = object({
14458
14513
  * there is no second switch that can disagree with the first and every rule
14459
14514
  * authored before the decision migrates for free (`audioModeOf`):
14460
14515
  *
14461
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14462
- * classifier labels with one of them. No window, no percentage:
14463
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14464
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14465
- * the analyzer's (`classificationMinScore`, per device) a label only
14466
- * reaches this condition if the classifier was already confident enough.
14516
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14517
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14518
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14519
+ * frames is the wrong question for a classifier that labels 1–3 frames
14520
+ * per episode. The count window is the brake that drops a single-frame
14521
+ * false positive; the rule's own `throttle` cooldown is the other. The
14522
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14523
+ * per device) — a label only reaches this condition if the classifier was
14524
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14467
14525
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14468
14526
  * the condition: at least `hitPercent`% of the samples over
14469
14527
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14490,14 +14548,22 @@ var NcOccupancyConditionSchema = object({
14490
14548
  * an operator who typed `dog` mean the same thing.
14491
14549
  */
14492
14550
  var NcAudioConditionSchema = object({
14493
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14551
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14494
14552
  labels: array(string().min(1)).min(1).optional(),
14495
14553
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14496
14554
  dbThreshold: number().min(-96).max(0).optional(),
14497
14555
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14498
14556
  hitPercent: number().int().min(1).max(100).default(60),
14499
14557
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14500
- samplingSeconds: number().int().min(1).max(300).default(10)
14558
+ samplingSeconds: number().int().min(1).max(300).default(10),
14559
+ /**
14560
+ * LABEL MODE: how many labelled frames must land inside
14561
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14562
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14563
+ */
14564
+ confirmHits: number().int().min(1).max(20).optional(),
14565
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14566
+ confirmWindowSec: number().int().min(1).max(60).optional()
14501
14567
  });
14502
14568
  /**
14503
14569
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -16871,6 +16937,46 @@ var RecentTracksPageSchema = object({
16871
16937
  /** Cursor for the next page, or null when this page is the last. */
16872
16938
  nextCursor: string().nullable()
16873
16939
  });
16940
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
16941
+ var LIST_GROUPS_MAX_LIMIT = 100;
16942
+ var AnalyticsGroupRecordSchema = object({
16943
+ id: string(),
16944
+ deviceId: number().int(),
16945
+ openedAt: number().int(),
16946
+ closedAt: number().int(),
16947
+ timestamp: number().int(),
16948
+ memberCount: number().int(),
16949
+ memberTrackIds: array(string()).readonly(),
16950
+ className: string(),
16951
+ classes: array(string()).readonly(),
16952
+ /** Relative event-media path, or null when the group has no picture yet. */
16953
+ mediaUrl: string().nullable(),
16954
+ singleton: boolean()
16955
+ });
16956
+ var AnalyticsGroupMemberSchema = object({
16957
+ trackId: string(),
16958
+ deviceId: number().int(),
16959
+ className: string(),
16960
+ firstSeen: number().int(),
16961
+ lastSeen: number().int(),
16962
+ mediaUrl: string().nullable()
16963
+ });
16964
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
16965
+ var ListGroupsQueryInput = object({
16966
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
16967
+ deviceIds: array(number()),
16968
+ /** Window lower bound on `closedAt` (inclusive). */
16969
+ since: number().optional(),
16970
+ /** Window upper bound on `openedAt` (inclusive). */
16971
+ until: number().optional(),
16972
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
16973
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
16974
+ cursor: string().optional()
16975
+ });
16976
+ var ListGroupsPageSchema = object({
16977
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
16978
+ nextCursor: string().nullable()
16979
+ });
16874
16980
  var KeyEventQueryInput = object({
16875
16981
  deviceId: number(),
16876
16982
  /** Window lower bound (track firstSeen ≥ since). */
@@ -16946,7 +17052,9 @@ var TrackCascadeCountsSchema = object({
16946
17052
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
16947
17053
  plates: number().int(),
16948
17054
  /** Per-track CLIP search vectors removed (best-effort). */
16949
- embeddings: number().int()
17055
+ embeddings: number().int(),
17056
+ /** Group membership + group rows removed with their last member (best-effort). */
17057
+ groups: number().int()
16950
17058
  });
16951
17059
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
16952
17060
  var DiskReconcileCountsSchema = object({
@@ -17092,7 +17200,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17092
17200
  * stationary registry). Default false: the timeline lists passages,
17093
17201
  * not parking records (operator decision, 2026-08-15). */
17094
17202
  includeStationary: boolean().optional()
17095
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17203
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17204
+ deviceId: number(),
17205
+ groupId: string().min(1)
17206
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17096
17207
  kind: "mutation",
17097
17208
  auth: "admin"
17098
17209
  }), 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({
@@ -17310,6 +17421,33 @@ var NativeCropRefSchema = object({
17310
17421
  h: number()
17311
17422
  })
17312
17423
  });
17424
+ object({
17425
+ crop: object({
17426
+ left: number(),
17427
+ top: number(),
17428
+ width: number().positive(),
17429
+ height: number().positive()
17430
+ }).optional(),
17431
+ content: object({
17432
+ width: number().int().positive(),
17433
+ height: number().int().positive()
17434
+ }),
17435
+ fit: _enum(["stretch", "contain"]),
17436
+ format: _enum([
17437
+ "rgb",
17438
+ "gray",
17439
+ "jpeg"
17440
+ ])
17441
+ });
17442
+ var FrameRefSchema = object({
17443
+ registryId: string().min(1),
17444
+ id: string().min(1),
17445
+ width: number().int().positive(),
17446
+ height: number().int().positive(),
17447
+ format: _enum(["rgb", "gray"]),
17448
+ timestamp: number(),
17449
+ capturedAt: number().optional()
17450
+ });
17313
17451
  var ModelFormatSchema$1 = _enum([
17314
17452
  "onnx",
17315
17453
  "coreml",
@@ -17375,7 +17513,8 @@ var PipelineModelOptionSchema = object({
17375
17513
  sizeMB: number()
17376
17514
  })),
17377
17515
  group: ModelVariantGroupSchema.optional(),
17378
- legacy: boolean().optional()
17516
+ legacy: boolean().optional(),
17517
+ provider: ModelProviderIdSchema.optional()
17379
17518
  });
17380
17519
  var ConfigFieldBridge = custom();
17381
17520
  var PipelineAddonSchemaSchema = object({
@@ -17554,6 +17693,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
17554
17693
  steps: array(PipelineStepInputSchema).min(1),
17555
17694
  frame: FrameInputSchema.optional(),
17556
17695
  /**
17696
+ * Process-local lazy frame. Valid only when caller and provider resolve
17697
+ * in the same execution-group process; split/cross-node callers use
17698
+ * `frame`/`image` inline compatibility instead.
17699
+ */
17700
+ frameRef: FrameRefSchema.optional(),
17701
+ /**
17557
17702
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
17558
17703
  * the decoded pixels live in. One more member of the one-of
17559
17704
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -17809,7 +17954,10 @@ var NativeCropResultSchema = object({
17809
17954
  * Which source served this crop, so a quality-sensitive consumer (the native
17810
17955
  * `keyFrame`) can reject a degraded fallback:
17811
17956
  * - `native` — cut from the decode worker's retained NATIVE surface (the
17812
- * quality path).
17957
+ * quality path). A subject-tile serve is also native-resolution and stays
17958
+ * `native` here: the public enum cannot name `tile` without a breaking cap
17959
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
17960
+ * internal crop result (`nativeHits` vs `tileHits`).
17813
17961
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
17814
17962
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
17815
17963
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -18300,12 +18448,41 @@ var RunnerLocalLoadSchema = object({
18300
18448
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
18301
18449
  * working unchanged when they switch to reading from the runner cap.
18302
18450
  */
18451
+ var FrameLazyCountersSchema = object({
18452
+ framesDecoded: number(),
18453
+ framesAdmitted: number(),
18454
+ framesDroppedPixelFree: number(),
18455
+ viewsMaterialized: number(),
18456
+ viewsSkipped: number(),
18457
+ workerToRunnerBytes: number(),
18458
+ runnerToPoolRawBytes: number(),
18459
+ runnerToPoolJpegBytes: number(),
18460
+ onDemandFullFrameRequests: number(),
18461
+ onDemandCropRequests: number(),
18462
+ nativeHits: number(),
18463
+ nativeMisses: number(),
18464
+ tileHits: number(),
18465
+ tileMisses: number(),
18466
+ fallbackHits: number(),
18467
+ fallbackMisses: number(),
18468
+ retainedWritesAvoided: number(),
18469
+ residentRefs: number(),
18470
+ residentBytes: number(),
18471
+ releases: number(),
18472
+ evictions: number(),
18473
+ staleMisses: number()
18474
+ });
18475
+ var FrameLazyMetricsSchema = object({
18476
+ node: FrameLazyCountersSchema,
18477
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
18478
+ });
18303
18479
  var RunnerLocalMetricsSchema = object({
18304
18480
  nodeId: string(),
18305
18481
  activeCameras: number(),
18306
18482
  throttledCameras: number(),
18307
18483
  avgInferenceTimeMs: number(),
18308
- queueDepth: number()
18484
+ queueDepth: number(),
18485
+ frameLazy: FrameLazyMetricsSchema.optional()
18309
18486
  });
18310
18487
  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({
18311
18488
  handle: FrameHandleSchema,
@@ -19605,6 +19782,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
19605
19782
  location: StorageLocationSchema,
19606
19783
  relativePath: string()
19607
19784
  }), _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" });
19785
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
19786
+ var ProfileSettingsSchemaBridge = unknown().nullable();
19787
+ var ProfileSettingsBagSchema = record(string(), unknown());
19608
19788
  /**
19609
19789
  * A live terminal session hosted by the provider addon. Output and input do
19610
19790
  * NOT flow through the capability — they use the addon data plane
@@ -19634,7 +19814,14 @@ var TerminalSessionInfoSchema = object({
19634
19814
  var TerminalProfileInfoSchema = object({
19635
19815
  profileId: string(),
19636
19816
  label: string(),
19637
- description: string().optional()
19817
+ description: string().optional(),
19818
+ /** Spawn defaults the instance form copies on create. */
19819
+ executable: string().optional(),
19820
+ args: array(string()).readonly().optional(),
19821
+ cwd: string().optional(),
19822
+ environment: array(string()).readonly().optional(),
19823
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
19824
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
19638
19825
  });
19639
19826
  /**
19640
19827
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -19647,7 +19834,12 @@ var TerminalInstanceInfoSchema = object({
19647
19834
  profileId: string(),
19648
19835
  profileLabel: string(),
19649
19836
  name: string(),
19650
- enabled: boolean()
19837
+ enabled: boolean(),
19838
+ executable: string(),
19839
+ args: array(string()).readonly(),
19840
+ cwd: string(),
19841
+ environment: array(string()).readonly(),
19842
+ profileSettings: ProfileSettingsBagSchema
19651
19843
  });
19652
19844
  var TerminalLegacyCameraSchema = object({
19653
19845
  stableId: string(),
@@ -19677,7 +19869,23 @@ var TerminalOutputBatchSchema = object({
19677
19869
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
19678
19870
  targetNodeId: string().min(1),
19679
19871
  profileId: string().min(1),
19680
- name: string().trim().min(1).max(160).optional()
19872
+ name: string().trim().min(1).max(160).optional(),
19873
+ executable: string().max(1024).optional(),
19874
+ args: array(string().max(2048)).max(64).optional(),
19875
+ cwd: string().max(1024).optional(),
19876
+ environment: array(string().max(4096)).max(64).optional(),
19877
+ profileSettings: ProfileSettingsBagSchema.optional()
19878
+ }), TerminalInstanceInfoSchema, {
19879
+ kind: "mutation",
19880
+ auth: "admin"
19881
+ }), method(object({
19882
+ instanceId: string().min(1),
19883
+ name: string().trim().min(1).max(160).optional(),
19884
+ executable: string().max(1024).optional(),
19885
+ args: array(string().max(2048)).max(64).optional(),
19886
+ cwd: string().max(1024).optional(),
19887
+ environment: array(string().max(4096)).max(64).optional(),
19888
+ profileSettings: ProfileSettingsBagSchema.optional()
19681
19889
  }), TerminalInstanceInfoSchema, {
19682
19890
  kind: "mutation",
19683
19891
  auth: "admin"
@@ -19699,7 +19907,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
19699
19907
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
19700
19908
  profileId: string(),
19701
19909
  cols: number().int().positive(),
19702
- rows: number().int().positive()
19910
+ rows: number().int().positive(),
19911
+ executable: string().max(1024).optional(),
19912
+ args: array(string().max(2048)).max(64).optional(),
19913
+ cwd: string().max(1024).optional(),
19914
+ environment: array(string().max(4096)).max(64).optional()
19703
19915
  }), TerminalSessionInfoSchema, {
19704
19916
  kind: "mutation",
19705
19917
  auth: "admin"
@@ -22481,10 +22693,10 @@ DeviceType.LawnMower, method(object({ deviceId: number().int().nonnegative() }),
22481
22693
  *
22482
22694
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
22483
22695
  * to receive an ordered list of candidate base URLs it should race
22484
- * on connect — LAN IPv4 first (lowest latency when on same network),
22485
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
22486
- * race them with short timeouts and stick with the winner for the
22487
- * session.
22696
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
22697
+ * when on the same network), then public hostname (if a tunnel is
22698
+ * up). The SDK can race them with short timeouts and stick with the
22699
+ * winner for the session.
22488
22700
  *
22489
22701
  * Why hub-only: agents are not directly addressable by the operator's
22490
22702
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -22639,6 +22851,17 @@ var NotificationEndpointSchema = object({
22639
22851
  /** What the ranking currently resolves to (null when nothing is reachable). */
22640
22852
  resolved: string().nullable()
22641
22853
  });
22854
+ /**
22855
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
22856
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
22857
+ * currently expands to, so the UI can show the effective set either way.
22858
+ */
22859
+ var ViewerEndpointsSchema = object({
22860
+ /** The operator's explicit race set, or empty for AUTO. */
22861
+ baseUrls: array(string()).readonly(),
22862
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
22863
+ resolved: array(string()).readonly()
22864
+ });
22642
22865
  var AllowedAddressesSchema = object({
22643
22866
  /**
22644
22867
  * Allowlist of interface addresses operators have explicitly opted
@@ -22647,6 +22870,20 @@ var AllowedAddressesSchema = object({
22647
22870
  * Network Addresses admin page and persisted by the addon.
22648
22871
  */
22649
22872
  addresses: array(string()).readonly() });
22873
+ var TlsStatusSchema = object({
22874
+ mode: _enum([
22875
+ "generated",
22876
+ "uploaded",
22877
+ "disabled"
22878
+ ]),
22879
+ leafFingerprintSha256: string().nullable(),
22880
+ caFingerprintSha256: string().nullable(),
22881
+ validTo: string().nullable(),
22882
+ sans: array(string()),
22883
+ caCertPem: string().nullable(),
22884
+ reissueError: string().nullable(),
22885
+ restartRequired: boolean()
22886
+ });
22650
22887
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
22651
22888
  /**
22652
22889
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -22656,17 +22893,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
22656
22893
  */
22657
22894
  port: number().int().min(1).max(65535).optional(),
22658
22895
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
22659
- * candidate. Default `true`. */
22896
+ * candidate. Default `false` — loopback is not a client route. */
22660
22897
  includeLoopback: boolean().optional(),
22661
- /** Skip IPv6 entries. Some legacy clients can't parse them.
22662
- * Default `false`. */
22898
+ /** Skip IPv6 entries. Default `false` the palette includes stable
22899
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
22900
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
22663
22901
  ipv4Only: boolean().optional(),
22664
22902
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
22665
22903
  * Pass `'https'` when the caller is itself loaded over HTTPS
22666
22904
  * to avoid mixed-content blocks in the browser. The public
22667
22905
  * tunnel always emits `https://` regardless. */
22668
22906
  scheme: _enum(["http", "https"]).optional()
22669
- }), 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" });
22907
+ }), 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, {
22908
+ kind: "mutation",
22909
+ auth: "admin"
22910
+ }), method(object({
22911
+ certPem: string().min(1),
22912
+ keyPem: string().min(1),
22913
+ caPem: string().optional()
22914
+ }), TlsStatusSchema, {
22915
+ kind: "mutation",
22916
+ auth: "admin"
22917
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
22918
+ kind: "mutation",
22919
+ auth: "admin"
22920
+ });
22670
22921
  object({
22671
22922
  /** Lifecycle state of the lock. `jammed` means the motor reported
22672
22923
  * failure to reach the target — operator intervention required. */
@@ -23847,7 +24098,12 @@ var PlateInfoSchema = object({
23847
24098
  plateBbox: BoundingBoxSchema.optional(),
23848
24099
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
23849
24100
  keyFrameMediaKey: string().optional(),
23850
- base64: string().optional()
24101
+ base64: string().optional(),
24102
+ /**
24103
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
24104
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
24105
+ */
24106
+ cropUrl: string().optional()
23851
24107
  });
23852
24108
  var MediaFileLiteSchema = object({
23853
24109
  key: string(),
@@ -27487,6 +27743,12 @@ Object.freeze({
27487
27743
  addonId: null,
27488
27744
  access: "view"
27489
27745
  },
27746
+ "deviceManager.getBindingsBatch": {
27747
+ capName: "device-manager",
27748
+ capScope: "system",
27749
+ addonId: null,
27750
+ access: "view"
27751
+ },
27490
27752
  "deviceManager.getChildren": {
27491
27753
  capName: "device-manager",
27492
27754
  capScope: "system",
@@ -27547,6 +27809,12 @@ Object.freeze({
27547
27809
  addonId: null,
27548
27810
  access: "view"
27549
27811
  },
27812
+ "deviceManager.getLinkedDevicesBatch": {
27813
+ capName: "device-manager",
27814
+ capScope: "system",
27815
+ addonId: null,
27816
+ access: "view"
27817
+ },
27550
27818
  "deviceManager.getRoleDisplayDefaults": {
27551
27819
  capName: "device-manager",
27552
27820
  capScope: "system",
@@ -28429,6 +28697,12 @@ Object.freeze({
28429
28697
  addonId: null,
28430
28698
  access: "create"
28431
28699
  },
28700
+ "localNetwork.downloadCa": {
28701
+ capName: "local-network",
28702
+ capScope: "system",
28703
+ addonId: null,
28704
+ access: "view"
28705
+ },
28432
28706
  "localNetwork.getAllowedAddresses": {
28433
28707
  capName: "local-network",
28434
28708
  capScope: "system",
@@ -28453,18 +28727,42 @@ Object.freeze({
28453
28727
  addonId: null,
28454
28728
  access: "view"
28455
28729
  },
28730
+ "localNetwork.getTlsStatus": {
28731
+ capName: "local-network",
28732
+ capScope: "system",
28733
+ addonId: null,
28734
+ access: "view"
28735
+ },
28736
+ "localNetwork.getViewerEndpoints": {
28737
+ capName: "local-network",
28738
+ capScope: "system",
28739
+ addonId: null,
28740
+ access: "view"
28741
+ },
28456
28742
  "localNetwork.list": {
28457
28743
  capName: "local-network",
28458
28744
  capScope: "system",
28459
28745
  addonId: null,
28460
28746
  access: "view"
28461
28747
  },
28748
+ "localNetwork.regenerateCertificate": {
28749
+ capName: "local-network",
28750
+ capScope: "system",
28751
+ addonId: null,
28752
+ access: "create"
28753
+ },
28462
28754
  "localNetwork.resetAllowlistToBestMatch": {
28463
28755
  capName: "local-network",
28464
28756
  capScope: "system",
28465
28757
  addonId: null,
28466
28758
  access: "delete"
28467
28759
  },
28760
+ "localNetwork.revertToGeneratedCertificate": {
28761
+ capName: "local-network",
28762
+ capScope: "system",
28763
+ addonId: null,
28764
+ access: "create"
28765
+ },
28468
28766
  "localNetwork.setAllowedAddresses": {
28469
28767
  capName: "local-network",
28470
28768
  capScope: "system",
@@ -28477,6 +28775,18 @@ Object.freeze({
28477
28775
  addonId: null,
28478
28776
  access: "create"
28479
28777
  },
28778
+ "localNetwork.setViewerEndpoints": {
28779
+ capName: "local-network",
28780
+ capScope: "system",
28781
+ addonId: null,
28782
+ access: "create"
28783
+ },
28784
+ "localNetwork.uploadCertificate": {
28785
+ capName: "local-network",
28786
+ capScope: "system",
28787
+ addonId: null,
28788
+ access: "create"
28789
+ },
28480
28790
  "lockControl.lock": {
28481
28791
  capName: "lock-control",
28482
28792
  capScope: "device",
@@ -29275,6 +29585,12 @@ Object.freeze({
29275
29585
  addonId: null,
29276
29586
  access: "view"
29277
29587
  },
29588
+ "pipelineAnalytics.getGroup": {
29589
+ capName: "pipeline-analytics",
29590
+ capScope: "device",
29591
+ addonId: null,
29592
+ access: "view"
29593
+ },
29278
29594
  "pipelineAnalytics.getKeyEvents": {
29279
29595
  capName: "pipeline-analytics",
29280
29596
  capScope: "device",
@@ -29359,6 +29675,12 @@ Object.freeze({
29359
29675
  addonId: null,
29360
29676
  access: "view"
29361
29677
  },
29678
+ "pipelineAnalytics.listGroups": {
29679
+ capName: "pipeline-analytics",
29680
+ capScope: "device",
29681
+ addonId: null,
29682
+ access: "view"
29683
+ },
29362
29684
  "pipelineAnalytics.listOpsLog": {
29363
29685
  capName: "pipeline-analytics",
29364
29686
  capScope: "device",
@@ -31357,6 +31679,12 @@ Object.freeze({
31357
31679
  addonId: null,
31358
31680
  access: "create"
31359
31681
  },
31682
+ "terminalSession.updateInstance": {
31683
+ capName: "terminal-session",
31684
+ capScope: "system",
31685
+ addonId: null,
31686
+ access: "create"
31687
+ },
31360
31688
  "terminalSession.writeInput": {
31361
31689
  capName: "terminal-session",
31362
31690
  capScope: "system",
@@ -32134,6 +32462,11 @@ Object.freeze({
32134
32462
  form: "single",
32135
32463
  optional: false
32136
32464
  }],
32465
+ "deviceManager.getBindingsBatch": [{
32466
+ name: "deviceIds",
32467
+ form: "array",
32468
+ optional: false
32469
+ }],
32137
32470
  "deviceManager.getChildren": [{
32138
32471
  name: "parentDeviceId",
32139
32472
  form: "single",
@@ -32179,6 +32512,11 @@ Object.freeze({
32179
32512
  form: "single",
32180
32513
  optional: false
32181
32514
  }],
32515
+ "deviceManager.getLinkedDevicesBatch": [{
32516
+ name: "deviceIds",
32517
+ form: "array",
32518
+ optional: false
32519
+ }],
32182
32520
  "deviceManager.getSettingsSchema": [{
32183
32521
  name: "deviceId",
32184
32522
  form: "single",
@@ -32199,6 +32537,11 @@ Object.freeze({
32199
32537
  form: "single",
32200
32538
  optional: false
32201
32539
  }],
32540
+ "deviceManager.listAll": [{
32541
+ name: "deviceIds",
32542
+ form: "array",
32543
+ optional: true
32544
+ }],
32202
32545
  "deviceManager.loadConfig": [{
32203
32546
  name: "deviceId",
32204
32547
  form: "single",
@@ -32772,6 +33115,11 @@ Object.freeze({
32772
33115
  form: "single",
32773
33116
  optional: false
32774
33117
  }],
33118
+ "pipelineAnalytics.getGroup": [{
33119
+ name: "deviceId",
33120
+ form: "single",
33121
+ optional: false
33122
+ }],
32775
33123
  "pipelineAnalytics.getKeyEvents": [{
32776
33124
  name: "deviceId",
32777
33125
  form: "single",
@@ -32827,6 +33175,11 @@ Object.freeze({
32827
33175
  form: "array",
32828
33176
  optional: false
32829
33177
  }],
33178
+ "pipelineAnalytics.listGroups": [{
33179
+ name: "deviceIds",
33180
+ form: "array",
33181
+ optional: false
33182
+ }],
32830
33183
  "pipelineAnalytics.listOpsLog": [{
32831
33184
  name: "deviceId",
32832
33185
  form: "single",
@@ -33844,6 +34197,35 @@ Object.freeze(Object.fromEntries([{
33844
34197
  }]
33845
34198
  }].map((s) => [s.stepId, s.defaultModelId])));
33846
34199
  string().min(1);
34200
+ var CLUSTER_STEP_SETTING_FIELDS = [{
34201
+ stepId: "face-embedding",
34202
+ key: "minLandmarkFaceSize",
34203
+ label: "Min face size for recognition (detection px)",
34204
+ 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.",
34205
+ type: "slider",
34206
+ min: 0,
34207
+ max: 64,
34208
+ step: 2,
34209
+ default: 24
34210
+ }];
34211
+ function clusterStepSettingKey(stepId, fieldKey) {
34212
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
34213
+ }
34214
+ var ClusterSettingNumberSchema = number().finite();
34215
+ function readClusterStepSettings(config) {
34216
+ const out = {};
34217
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
34218
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
34219
+ const value = parsed.success ? parsed.data : field.default;
34220
+ const existing = out[field.stepId] ?? {};
34221
+ out[field.stepId] = {
34222
+ ...existing,
34223
+ [field.key]: value
34224
+ };
34225
+ }
34226
+ return out;
34227
+ }
34228
+ readClusterStepSettings({});
33847
34229
  object({
33848
34230
  /**
33849
34231
  * Fraction of the box's own size added on EACH side before cutting.