@camstack/addon-provider-rademacher 0.2.25 → 0.2.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +432 -50
  2. package/dist/addon.mjs +432 -50
  3. package/package.json +1 -1
package/dist/addon.mjs CHANGED
@@ -6785,6 +6785,13 @@ var BaseAddon = class {
6785
6785
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
6786
6786
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
6787
6787
  _registeredCapNames = [];
6788
+ /**
6789
+ * True only after `readAddonStore` actually answered. Constructor
6790
+ * defaults look like stored config when the store is down — a forked
6791
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
6792
+ * mode, 2026-08-25) is not "the operator chose this".
6793
+ */
6794
+ settingsStoreReady = false;
6788
6795
  /** Default config values. Provided via constructor. */
6789
6796
  defaults;
6790
6797
  constructor(defaults) {
@@ -7185,7 +7192,9 @@ var BaseAddon = class {
7185
7192
  ];
7186
7193
  let lastErr;
7187
7194
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
7188
- return await settings.readAddonStore() ?? {};
7195
+ const stored = await settings.readAddonStore() ?? {};
7196
+ this.settingsStoreReady = true;
7197
+ return stored;
7189
7198
  } catch (err) {
7190
7199
  lastErr = err;
7191
7200
  const msg = err instanceof Error ? err.message : String(err);
@@ -7193,6 +7202,7 @@ var BaseAddon = class {
7193
7202
  if (attempt === delaysMs.length) break;
7194
7203
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
7195
7204
  }
7205
+ this.settingsStoreReady = false;
7196
7206
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
7197
7207
  return {};
7198
7208
  }
@@ -9004,6 +9014,15 @@ var LabelDefinitionSchema = object({
9004
9014
  description: string().optional(),
9005
9015
  icon: string().optional()
9006
9016
  });
9017
+ var ClassMapDefinitionSchema = object({
9018
+ mapping: record(string(), _enum([
9019
+ "person",
9020
+ "vehicle",
9021
+ "animal",
9022
+ "package"
9023
+ ])),
9024
+ preserveOriginal: boolean()
9025
+ });
9007
9026
  var MODEL_FORMATS = [
9008
9027
  "onnx",
9009
9028
  "coreml",
@@ -9087,6 +9106,12 @@ var ModelVariantGroupSchema = object({
9087
9106
  */
9088
9107
  resolution: number().int().positive().optional()
9089
9108
  });
9109
+ var ModelProviderIdSchema = _enum([
9110
+ "camstack",
9111
+ "frigate",
9112
+ "scrypted",
9113
+ "custom"
9114
+ ]);
9090
9115
  var ModelCatalogEntrySchema = object({
9091
9116
  id: string(),
9092
9117
  name: string(),
@@ -9182,7 +9207,19 @@ var ModelCatalogEntrySchema = object({
9182
9207
  * `id` stays the source of truth for resolution/download/persistence; grouping
9183
9208
  * is a presentation overlay resolved back to an `id`.
9184
9209
  */
9185
- group: ModelVariantGroupSchema.optional()
9210
+ group: ModelVariantGroupSchema.optional(),
9211
+ /**
9212
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
9213
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
9214
+ * persisted before this field existed (`inferModelProvider` fills those).
9215
+ */
9216
+ provider: ModelProviderIdSchema.optional(),
9217
+ /**
9218
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
9219
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
9220
+ * labels already ARE the CamStack macros (Scrypted identity map).
9221
+ */
9222
+ classMap: ClassMapDefinitionSchema.optional()
9186
9223
  });
9187
9224
  var ConvertTargetSchema = discriminatedUnion("format", [object({
9188
9225
  format: literal("openvino"),
@@ -9211,7 +9248,8 @@ var ModelConvertMetadataSchema = object({
9211
9248
  "ocr",
9212
9249
  "segmentation"
9213
9250
  ]),
9214
- faceAlignment: boolean().optional()
9251
+ faceAlignment: boolean().optional(),
9252
+ classMap: ClassMapDefinitionSchema.optional()
9215
9253
  });
9216
9254
  var ConvertResultSchema = object({
9217
9255
  entry: ModelCatalogEntrySchema,
@@ -12968,6 +13006,27 @@ var LinkedDeviceSchema = object({
12968
13006
  features: array(string()),
12969
13007
  producesTrackedEvents: boolean().optional()
12970
13008
  });
13009
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
13010
+ * The batch answer needs the tag; the single-device answer already has it
13011
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
13012
+ var LinkedDevicesForDeviceSchema = object({
13013
+ deviceId: number(),
13014
+ mode: LinkedDevicesModeSchema,
13015
+ devices: array(LinkedDeviceSchema)
13016
+ });
13017
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
13018
+ * `getAllBindings` all answer in. Declared once: three copies of the same
13019
+ * object literal is exactly how the three drift apart. */
13020
+ var DeviceBindingsForDeviceSchema = object({
13021
+ deviceId: number(),
13022
+ entries: array(object({
13023
+ capName: string(),
13024
+ kind: _enum(["native", "wrapped"]),
13025
+ providerAddonId: string(),
13026
+ providerNodeId: string(),
13027
+ nativeAddonId: string()
13028
+ }))
13029
+ });
12971
13030
  var SavedDeviceRowSchema = object({
12972
13031
  /** Numeric id reserved at allocateDeviceId time. */
12973
13032
  id: number(),
@@ -13193,11 +13252,25 @@ method(object({
13193
13252
  projection: _enum(["full", "slim"]).optional(),
13194
13253
  /** Return only camera devices. Filtering server-side instead of
13195
13254
  * shipping 293 rows to find 12. */
13196
- isCamera: boolean().optional()
13255
+ isCamera: boolean().optional(),
13256
+ /**
13257
+ * Return only these device ids. For the caller that already KNOWS the
13258
+ * handful it wants and needs a field the id-bearing answer does not
13259
+ * carry — the viewer's linked-devices panel joins `type` and `online`
13260
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
13261
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
13262
+ * refetches on the reconcile interval, on a phone.
13263
+ *
13264
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
13265
+ * keys rather than rejecting them (verified against the live hub
13266
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
13267
+ * it answers today and the caller filters as it already does.
13268
+ */
13269
+ deviceIds: array(number()).optional()
13197
13270
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
13198
13271
  mode: LinkedDevicesModeSchema,
13199
13272
  devices: array(LinkedDeviceSchema)
13200
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
13273
+ })), 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({
13201
13274
  deviceId: number(),
13202
13275
  values: record(string(), unknown())
13203
13276
  }), object({ success: literal(true) }), {
@@ -13224,25 +13297,7 @@ method(object({
13224
13297
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
13225
13298
  kind: "mutation",
13226
13299
  auth: "admin"
13227
- }), method(object({ deviceId: number() }), object({
13228
- deviceId: number(),
13229
- entries: array(object({
13230
- capName: string(),
13231
- kind: _enum(["native", "wrapped"]),
13232
- providerAddonId: string(),
13233
- providerNodeId: string(),
13234
- nativeAddonId: string()
13235
- }))
13236
- })), method(object({}), array(object({
13237
- deviceId: number(),
13238
- entries: array(object({
13239
- capName: string(),
13240
- kind: _enum(["native", "wrapped"]),
13241
- providerAddonId: string(),
13242
- providerNodeId: string(),
13243
- nativeAddonId: string()
13244
- }))
13245
- }))), method(object({
13300
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
13246
13301
  deviceId: number(),
13247
13302
  capName: string(),
13248
13303
  wrapperAddonId: string(),
@@ -15652,12 +15707,15 @@ var NcOccupancyConditionSchema = object({
15652
15707
  * there is no second switch that can disagree with the first and every rule
15653
15708
  * authored before the decision migrates for free (`audioModeOf`):
15654
15709
  *
15655
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
15656
- * classifier labels with one of them. No window, no percentage:
15657
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
15658
- * `throttle` cooldown is the only brake. The per-label confidence floor is
15659
- * the analyzer's (`classificationMinScore`, per device) a label only
15660
- * reaches this condition if the classifier was already confident enough.
15710
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
15711
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
15712
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
15713
+ * frames is the wrong question for a classifier that labels 1–3 frames
15714
+ * per episode. The count window is the brake that drops a single-frame
15715
+ * false positive; the rule's own `throttle` cooldown is the other. The
15716
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
15717
+ * per device) — a label only reaches this condition if the classifier was
15718
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
15661
15719
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
15662
15720
  * the condition: at least `hitPercent`% of the samples over
15663
15721
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -15684,14 +15742,22 @@ var NcOccupancyConditionSchema = object({
15684
15742
  * an operator who typed `dog` mean the same thing.
15685
15743
  */
15686
15744
  var NcAudioConditionSchema = object({
15687
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
15745
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
15688
15746
  labels: array(string().min(1)).min(1).optional(),
15689
15747
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
15690
15748
  dbThreshold: number().min(-96).max(0).optional(),
15691
15749
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
15692
15750
  hitPercent: number().int().min(1).max(100).default(60),
15693
15751
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
15694
- samplingSeconds: number().int().min(1).max(300).default(10)
15752
+ samplingSeconds: number().int().min(1).max(300).default(10),
15753
+ /**
15754
+ * LABEL MODE: how many labelled frames must land inside
15755
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
15756
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
15757
+ */
15758
+ confirmHits: number().int().min(1).max(20).optional(),
15759
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
15760
+ confirmWindowSec: number().int().min(1).max(60).optional()
15695
15761
  });
15696
15762
  /**
15697
15763
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -18065,6 +18131,46 @@ var RecentTracksPageSchema = object({
18065
18131
  /** Cursor for the next page, or null when this page is the last. */
18066
18132
  nextCursor: string().nullable()
18067
18133
  });
18134
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
18135
+ var LIST_GROUPS_MAX_LIMIT = 100;
18136
+ var AnalyticsGroupRecordSchema = object({
18137
+ id: string(),
18138
+ deviceId: number().int(),
18139
+ openedAt: number().int(),
18140
+ closedAt: number().int(),
18141
+ timestamp: number().int(),
18142
+ memberCount: number().int(),
18143
+ memberTrackIds: array(string()).readonly(),
18144
+ className: string(),
18145
+ classes: array(string()).readonly(),
18146
+ /** Relative event-media path, or null when the group has no picture yet. */
18147
+ mediaUrl: string().nullable(),
18148
+ singleton: boolean()
18149
+ });
18150
+ var AnalyticsGroupMemberSchema = object({
18151
+ trackId: string(),
18152
+ deviceId: number().int(),
18153
+ className: string(),
18154
+ firstSeen: number().int(),
18155
+ lastSeen: number().int(),
18156
+ mediaUrl: string().nullable()
18157
+ });
18158
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
18159
+ var ListGroupsQueryInput = object({
18160
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
18161
+ deviceIds: array(number()),
18162
+ /** Window lower bound on `closedAt` (inclusive). */
18163
+ since: number().optional(),
18164
+ /** Window upper bound on `openedAt` (inclusive). */
18165
+ until: number().optional(),
18166
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
18167
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
18168
+ cursor: string().optional()
18169
+ });
18170
+ var ListGroupsPageSchema = object({
18171
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
18172
+ nextCursor: string().nullable()
18173
+ });
18068
18174
  var KeyEventQueryInput = object({
18069
18175
  deviceId: number(),
18070
18176
  /** Window lower bound (track firstSeen ≥ since). */
@@ -18140,7 +18246,9 @@ var TrackCascadeCountsSchema = object({
18140
18246
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
18141
18247
  plates: number().int(),
18142
18248
  /** Per-track CLIP search vectors removed (best-effort). */
18143
- embeddings: number().int()
18249
+ embeddings: number().int(),
18250
+ /** Group membership + group rows removed with their last member (best-effort). */
18251
+ groups: number().int()
18144
18252
  });
18145
18253
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
18146
18254
  var DiskReconcileCountsSchema = object({
@@ -18286,7 +18394,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18286
18394
  * stationary registry). Default false: the timeline lists passages,
18287
18395
  * not parking records (operator decision, 2026-08-15). */
18288
18396
  includeStationary: boolean().optional()
18289
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
18397
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
18398
+ deviceId: number(),
18399
+ groupId: string().min(1)
18400
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
18290
18401
  kind: "mutation",
18291
18402
  auth: "admin"
18292
18403
  }), 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({
@@ -18504,6 +18615,33 @@ var NativeCropRefSchema = object({
18504
18615
  h: number()
18505
18616
  })
18506
18617
  });
18618
+ object({
18619
+ crop: object({
18620
+ left: number(),
18621
+ top: number(),
18622
+ width: number().positive(),
18623
+ height: number().positive()
18624
+ }).optional(),
18625
+ content: object({
18626
+ width: number().int().positive(),
18627
+ height: number().int().positive()
18628
+ }),
18629
+ fit: _enum(["stretch", "contain"]),
18630
+ format: _enum([
18631
+ "rgb",
18632
+ "gray",
18633
+ "jpeg"
18634
+ ])
18635
+ });
18636
+ var FrameRefSchema = object({
18637
+ registryId: string().min(1),
18638
+ id: string().min(1),
18639
+ width: number().int().positive(),
18640
+ height: number().int().positive(),
18641
+ format: _enum(["rgb", "gray"]),
18642
+ timestamp: number(),
18643
+ capturedAt: number().optional()
18644
+ });
18507
18645
  var ModelFormatSchema$1 = _enum([
18508
18646
  "onnx",
18509
18647
  "coreml",
@@ -18569,7 +18707,8 @@ var PipelineModelOptionSchema = object({
18569
18707
  sizeMB: number()
18570
18708
  })),
18571
18709
  group: ModelVariantGroupSchema.optional(),
18572
- legacy: boolean().optional()
18710
+ legacy: boolean().optional(),
18711
+ provider: ModelProviderIdSchema.optional()
18573
18712
  });
18574
18713
  var ConfigFieldBridge = custom();
18575
18714
  var PipelineAddonSchemaSchema = object({
@@ -18748,6 +18887,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
18748
18887
  steps: array(PipelineStepInputSchema).min(1),
18749
18888
  frame: FrameInputSchema.optional(),
18750
18889
  /**
18890
+ * Process-local lazy frame. Valid only when caller and provider resolve
18891
+ * in the same execution-group process; split/cross-node callers use
18892
+ * `frame`/`image` inline compatibility instead.
18893
+ */
18894
+ frameRef: FrameRefSchema.optional(),
18895
+ /**
18751
18896
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
18752
18897
  * the decoded pixels live in. One more member of the one-of
18753
18898
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -19043,7 +19188,10 @@ var NativeCropResultSchema = object({
19043
19188
  * Which source served this crop, so a quality-sensitive consumer (the native
19044
19189
  * `keyFrame`) can reject a degraded fallback:
19045
19190
  * - `native` — cut from the decode worker's retained NATIVE surface (the
19046
- * quality path).
19191
+ * quality path). A subject-tile serve is also native-resolution and stays
19192
+ * `native` here: the public enum cannot name `tile` without a breaking cap
19193
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
19194
+ * internal crop result (`nativeHits` vs `tileHits`).
19047
19195
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
19048
19196
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
19049
19197
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -19534,12 +19682,41 @@ var RunnerLocalLoadSchema = object({
19534
19682
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
19535
19683
  * working unchanged when they switch to reading from the runner cap.
19536
19684
  */
19685
+ var FrameLazyCountersSchema = object({
19686
+ framesDecoded: number(),
19687
+ framesAdmitted: number(),
19688
+ framesDroppedPixelFree: number(),
19689
+ viewsMaterialized: number(),
19690
+ viewsSkipped: number(),
19691
+ workerToRunnerBytes: number(),
19692
+ runnerToPoolRawBytes: number(),
19693
+ runnerToPoolJpegBytes: number(),
19694
+ onDemandFullFrameRequests: number(),
19695
+ onDemandCropRequests: number(),
19696
+ nativeHits: number(),
19697
+ nativeMisses: number(),
19698
+ tileHits: number(),
19699
+ tileMisses: number(),
19700
+ fallbackHits: number(),
19701
+ fallbackMisses: number(),
19702
+ retainedWritesAvoided: number(),
19703
+ residentRefs: number(),
19704
+ residentBytes: number(),
19705
+ releases: number(),
19706
+ evictions: number(),
19707
+ staleMisses: number()
19708
+ });
19709
+ var FrameLazyMetricsSchema = object({
19710
+ node: FrameLazyCountersSchema,
19711
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
19712
+ });
19537
19713
  var RunnerLocalMetricsSchema = object({
19538
19714
  nodeId: string(),
19539
19715
  activeCameras: number(),
19540
19716
  throttledCameras: number(),
19541
19717
  avgInferenceTimeMs: number(),
19542
- queueDepth: number()
19718
+ queueDepth: number(),
19719
+ frameLazy: FrameLazyMetricsSchema.optional()
19543
19720
  });
19544
19721
  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({
19545
19722
  handle: FrameHandleSchema,
@@ -20839,6 +21016,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
20839
21016
  location: StorageLocationSchema,
20840
21017
  relativePath: string()
20841
21018
  }), _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" });
21019
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
21020
+ var ProfileSettingsSchemaBridge = unknown().nullable();
21021
+ var ProfileSettingsBagSchema = record(string(), unknown());
20842
21022
  /**
20843
21023
  * A live terminal session hosted by the provider addon. Output and input do
20844
21024
  * NOT flow through the capability — they use the addon data plane
@@ -20868,7 +21048,14 @@ var TerminalSessionInfoSchema = object({
20868
21048
  var TerminalProfileInfoSchema = object({
20869
21049
  profileId: string(),
20870
21050
  label: string(),
20871
- description: string().optional()
21051
+ description: string().optional(),
21052
+ /** Spawn defaults the instance form copies on create. */
21053
+ executable: string().optional(),
21054
+ args: array(string()).readonly().optional(),
21055
+ cwd: string().optional(),
21056
+ environment: array(string()).readonly().optional(),
21057
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
21058
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
20872
21059
  });
20873
21060
  /**
20874
21061
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -20881,7 +21068,12 @@ var TerminalInstanceInfoSchema = object({
20881
21068
  profileId: string(),
20882
21069
  profileLabel: string(),
20883
21070
  name: string(),
20884
- enabled: boolean()
21071
+ enabled: boolean(),
21072
+ executable: string(),
21073
+ args: array(string()).readonly(),
21074
+ cwd: string(),
21075
+ environment: array(string()).readonly(),
21076
+ profileSettings: ProfileSettingsBagSchema
20885
21077
  });
20886
21078
  var TerminalLegacyCameraSchema = object({
20887
21079
  stableId: string(),
@@ -20911,7 +21103,23 @@ var TerminalOutputBatchSchema = object({
20911
21103
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
20912
21104
  targetNodeId: string().min(1),
20913
21105
  profileId: string().min(1),
20914
- name: string().trim().min(1).max(160).optional()
21106
+ name: string().trim().min(1).max(160).optional(),
21107
+ executable: string().max(1024).optional(),
21108
+ args: array(string().max(2048)).max(64).optional(),
21109
+ cwd: string().max(1024).optional(),
21110
+ environment: array(string().max(4096)).max(64).optional(),
21111
+ profileSettings: ProfileSettingsBagSchema.optional()
21112
+ }), TerminalInstanceInfoSchema, {
21113
+ kind: "mutation",
21114
+ auth: "admin"
21115
+ }), method(object({
21116
+ instanceId: string().min(1),
21117
+ name: string().trim().min(1).max(160).optional(),
21118
+ executable: string().max(1024).optional(),
21119
+ args: array(string().max(2048)).max(64).optional(),
21120
+ cwd: string().max(1024).optional(),
21121
+ environment: array(string().max(4096)).max(64).optional(),
21122
+ profileSettings: ProfileSettingsBagSchema.optional()
20915
21123
  }), TerminalInstanceInfoSchema, {
20916
21124
  kind: "mutation",
20917
21125
  auth: "admin"
@@ -20933,7 +21141,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
20933
21141
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
20934
21142
  profileId: string(),
20935
21143
  cols: number().int().positive(),
20936
- rows: number().int().positive()
21144
+ rows: number().int().positive(),
21145
+ executable: string().max(1024).optional(),
21146
+ args: array(string().max(2048)).max(64).optional(),
21147
+ cwd: string().max(1024).optional(),
21148
+ environment: array(string().max(4096)).max(64).optional()
20937
21149
  }), TerminalSessionInfoSchema, {
20938
21150
  kind: "mutation",
20939
21151
  auth: "admin"
@@ -24808,10 +25020,10 @@ var lawnMowerControlCapability = {
24808
25020
  *
24809
25021
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
24810
25022
  * to receive an ordered list of candidate base URLs it should race
24811
- * on connect — LAN IPv4 first (lowest latency when on same network),
24812
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
24813
- * race them with short timeouts and stick with the winner for the
24814
- * session.
25023
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
25024
+ * when on the same network), then public hostname (if a tunnel is
25025
+ * up). The SDK can race them with short timeouts and stick with the
25026
+ * winner for the session.
24815
25027
  *
24816
25028
  * Why hub-only: agents are not directly addressable by the operator's
24817
25029
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -24966,6 +25178,17 @@ var NotificationEndpointSchema = object({
24966
25178
  /** What the ranking currently resolves to (null when nothing is reachable). */
24967
25179
  resolved: string().nullable()
24968
25180
  });
25181
+ /**
25182
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
25183
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
25184
+ * currently expands to, so the UI can show the effective set either way.
25185
+ */
25186
+ var ViewerEndpointsSchema = object({
25187
+ /** The operator's explicit race set, or empty for AUTO. */
25188
+ baseUrls: array(string()).readonly(),
25189
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
25190
+ resolved: array(string()).readonly()
25191
+ });
24969
25192
  var AllowedAddressesSchema = object({
24970
25193
  /**
24971
25194
  * Allowlist of interface addresses operators have explicitly opted
@@ -24974,6 +25197,20 @@ var AllowedAddressesSchema = object({
24974
25197
  * Network Addresses admin page and persisted by the addon.
24975
25198
  */
24976
25199
  addresses: array(string()).readonly() });
25200
+ var TlsStatusSchema = object({
25201
+ mode: _enum([
25202
+ "generated",
25203
+ "uploaded",
25204
+ "disabled"
25205
+ ]),
25206
+ leafFingerprintSha256: string().nullable(),
25207
+ caFingerprintSha256: string().nullable(),
25208
+ validTo: string().nullable(),
25209
+ sans: array(string()),
25210
+ caCertPem: string().nullable(),
25211
+ reissueError: string().nullable(),
25212
+ restartRequired: boolean()
25213
+ });
24977
25214
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
24978
25215
  /**
24979
25216
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -24983,17 +25220,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
24983
25220
  */
24984
25221
  port: number().int().min(1).max(65535).optional(),
24985
25222
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
24986
- * candidate. Default `true`. */
25223
+ * candidate. Default `false` — loopback is not a client route. */
24987
25224
  includeLoopback: boolean().optional(),
24988
- /** Skip IPv6 entries. Some legacy clients can't parse them.
24989
- * Default `false`. */
25225
+ /** Skip IPv6 entries. Default `false` the palette includes stable
25226
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
25227
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
24990
25228
  ipv4Only: boolean().optional(),
24991
25229
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
24992
25230
  * Pass `'https'` when the caller is itself loaded over HTTPS
24993
25231
  * to avoid mixed-content blocks in the browser. The public
24994
25232
  * tunnel always emits `https://` regardless. */
24995
25233
  scheme: _enum(["http", "https"]).optional()
24996
- }), 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" });
25234
+ }), 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, {
25235
+ kind: "mutation",
25236
+ auth: "admin"
25237
+ }), method(object({
25238
+ certPem: string().min(1),
25239
+ keyPem: string().min(1),
25240
+ caPem: string().optional()
25241
+ }), TlsStatusSchema, {
25242
+ kind: "mutation",
25243
+ auth: "admin"
25244
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
25245
+ kind: "mutation",
25246
+ auth: "admin"
25247
+ });
24997
25248
  var LockControlStatusSchema = object({
24998
25249
  /** Lifecycle state of the lock. `jammed` means the motor reported
24999
25250
  * failure to reach the target — operator intervention required. */
@@ -26554,7 +26805,12 @@ var PlateInfoSchema = object({
26554
26805
  plateBbox: BoundingBoxSchema.optional(),
26555
26806
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
26556
26807
  keyFrameMediaKey: string().optional(),
26557
- base64: string().optional()
26808
+ base64: string().optional(),
26809
+ /**
26810
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
26811
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
26812
+ */
26813
+ cropUrl: string().optional()
26558
26814
  });
26559
26815
  var MediaFileLiteSchema = object({
26560
26816
  key: string(),
@@ -32078,6 +32334,12 @@ Object.freeze({
32078
32334
  addonId: null,
32079
32335
  access: "view"
32080
32336
  },
32337
+ "deviceManager.getBindingsBatch": {
32338
+ capName: "device-manager",
32339
+ capScope: "system",
32340
+ addonId: null,
32341
+ access: "view"
32342
+ },
32081
32343
  "deviceManager.getChildren": {
32082
32344
  capName: "device-manager",
32083
32345
  capScope: "system",
@@ -32138,6 +32400,12 @@ Object.freeze({
32138
32400
  addonId: null,
32139
32401
  access: "view"
32140
32402
  },
32403
+ "deviceManager.getLinkedDevicesBatch": {
32404
+ capName: "device-manager",
32405
+ capScope: "system",
32406
+ addonId: null,
32407
+ access: "view"
32408
+ },
32141
32409
  "deviceManager.getRoleDisplayDefaults": {
32142
32410
  capName: "device-manager",
32143
32411
  capScope: "system",
@@ -33020,6 +33288,12 @@ Object.freeze({
33020
33288
  addonId: null,
33021
33289
  access: "create"
33022
33290
  },
33291
+ "localNetwork.downloadCa": {
33292
+ capName: "local-network",
33293
+ capScope: "system",
33294
+ addonId: null,
33295
+ access: "view"
33296
+ },
33023
33297
  "localNetwork.getAllowedAddresses": {
33024
33298
  capName: "local-network",
33025
33299
  capScope: "system",
@@ -33044,18 +33318,42 @@ Object.freeze({
33044
33318
  addonId: null,
33045
33319
  access: "view"
33046
33320
  },
33321
+ "localNetwork.getTlsStatus": {
33322
+ capName: "local-network",
33323
+ capScope: "system",
33324
+ addonId: null,
33325
+ access: "view"
33326
+ },
33327
+ "localNetwork.getViewerEndpoints": {
33328
+ capName: "local-network",
33329
+ capScope: "system",
33330
+ addonId: null,
33331
+ access: "view"
33332
+ },
33047
33333
  "localNetwork.list": {
33048
33334
  capName: "local-network",
33049
33335
  capScope: "system",
33050
33336
  addonId: null,
33051
33337
  access: "view"
33052
33338
  },
33339
+ "localNetwork.regenerateCertificate": {
33340
+ capName: "local-network",
33341
+ capScope: "system",
33342
+ addonId: null,
33343
+ access: "create"
33344
+ },
33053
33345
  "localNetwork.resetAllowlistToBestMatch": {
33054
33346
  capName: "local-network",
33055
33347
  capScope: "system",
33056
33348
  addonId: null,
33057
33349
  access: "delete"
33058
33350
  },
33351
+ "localNetwork.revertToGeneratedCertificate": {
33352
+ capName: "local-network",
33353
+ capScope: "system",
33354
+ addonId: null,
33355
+ access: "create"
33356
+ },
33059
33357
  "localNetwork.setAllowedAddresses": {
33060
33358
  capName: "local-network",
33061
33359
  capScope: "system",
@@ -33068,6 +33366,18 @@ Object.freeze({
33068
33366
  addonId: null,
33069
33367
  access: "create"
33070
33368
  },
33369
+ "localNetwork.setViewerEndpoints": {
33370
+ capName: "local-network",
33371
+ capScope: "system",
33372
+ addonId: null,
33373
+ access: "create"
33374
+ },
33375
+ "localNetwork.uploadCertificate": {
33376
+ capName: "local-network",
33377
+ capScope: "system",
33378
+ addonId: null,
33379
+ access: "create"
33380
+ },
33071
33381
  "lockControl.lock": {
33072
33382
  capName: "lock-control",
33073
33383
  capScope: "device",
@@ -33866,6 +34176,12 @@ Object.freeze({
33866
34176
  addonId: null,
33867
34177
  access: "view"
33868
34178
  },
34179
+ "pipelineAnalytics.getGroup": {
34180
+ capName: "pipeline-analytics",
34181
+ capScope: "device",
34182
+ addonId: null,
34183
+ access: "view"
34184
+ },
33869
34185
  "pipelineAnalytics.getKeyEvents": {
33870
34186
  capName: "pipeline-analytics",
33871
34187
  capScope: "device",
@@ -33950,6 +34266,12 @@ Object.freeze({
33950
34266
  addonId: null,
33951
34267
  access: "view"
33952
34268
  },
34269
+ "pipelineAnalytics.listGroups": {
34270
+ capName: "pipeline-analytics",
34271
+ capScope: "device",
34272
+ addonId: null,
34273
+ access: "view"
34274
+ },
33953
34275
  "pipelineAnalytics.listOpsLog": {
33954
34276
  capName: "pipeline-analytics",
33955
34277
  capScope: "device",
@@ -35948,6 +36270,12 @@ Object.freeze({
35948
36270
  addonId: null,
35949
36271
  access: "create"
35950
36272
  },
36273
+ "terminalSession.updateInstance": {
36274
+ capName: "terminal-session",
36275
+ capScope: "system",
36276
+ addonId: null,
36277
+ access: "create"
36278
+ },
35951
36279
  "terminalSession.writeInput": {
35952
36280
  capName: "terminal-session",
35953
36281
  capScope: "system",
@@ -36725,6 +37053,11 @@ Object.freeze({
36725
37053
  form: "single",
36726
37054
  optional: false
36727
37055
  }],
37056
+ "deviceManager.getBindingsBatch": [{
37057
+ name: "deviceIds",
37058
+ form: "array",
37059
+ optional: false
37060
+ }],
36728
37061
  "deviceManager.getChildren": [{
36729
37062
  name: "parentDeviceId",
36730
37063
  form: "single",
@@ -36770,6 +37103,11 @@ Object.freeze({
36770
37103
  form: "single",
36771
37104
  optional: false
36772
37105
  }],
37106
+ "deviceManager.getLinkedDevicesBatch": [{
37107
+ name: "deviceIds",
37108
+ form: "array",
37109
+ optional: false
37110
+ }],
36773
37111
  "deviceManager.getSettingsSchema": [{
36774
37112
  name: "deviceId",
36775
37113
  form: "single",
@@ -36790,6 +37128,11 @@ Object.freeze({
36790
37128
  form: "single",
36791
37129
  optional: false
36792
37130
  }],
37131
+ "deviceManager.listAll": [{
37132
+ name: "deviceIds",
37133
+ form: "array",
37134
+ optional: true
37135
+ }],
36793
37136
  "deviceManager.loadConfig": [{
36794
37137
  name: "deviceId",
36795
37138
  form: "single",
@@ -37363,6 +37706,11 @@ Object.freeze({
37363
37706
  form: "single",
37364
37707
  optional: false
37365
37708
  }],
37709
+ "pipelineAnalytics.getGroup": [{
37710
+ name: "deviceId",
37711
+ form: "single",
37712
+ optional: false
37713
+ }],
37366
37714
  "pipelineAnalytics.getKeyEvents": [{
37367
37715
  name: "deviceId",
37368
37716
  form: "single",
@@ -37418,6 +37766,11 @@ Object.freeze({
37418
37766
  form: "array",
37419
37767
  optional: false
37420
37768
  }],
37769
+ "pipelineAnalytics.listGroups": [{
37770
+ name: "deviceIds",
37771
+ form: "array",
37772
+ optional: false
37773
+ }],
37421
37774
  "pipelineAnalytics.listOpsLog": [{
37422
37775
  name: "deviceId",
37423
37776
  form: "single",
@@ -38435,6 +38788,35 @@ Object.freeze(Object.fromEntries([{
38435
38788
  }]
38436
38789
  }].map((s) => [s.stepId, s.defaultModelId])));
38437
38790
  string().min(1);
38791
+ var CLUSTER_STEP_SETTING_FIELDS = [{
38792
+ stepId: "face-embedding",
38793
+ key: "minLandmarkFaceSize",
38794
+ label: "Min face size for recognition (detection px)",
38795
+ 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.",
38796
+ type: "slider",
38797
+ min: 0,
38798
+ max: 64,
38799
+ step: 2,
38800
+ default: 24
38801
+ }];
38802
+ function clusterStepSettingKey(stepId, fieldKey) {
38803
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
38804
+ }
38805
+ var ClusterSettingNumberSchema = number().finite();
38806
+ function readClusterStepSettings(config) {
38807
+ const out = {};
38808
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
38809
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
38810
+ const value = parsed.success ? parsed.data : field.default;
38811
+ const existing = out[field.stepId] ?? {};
38812
+ out[field.stepId] = {
38813
+ ...existing,
38814
+ [field.key]: value
38815
+ };
38816
+ }
38817
+ return out;
38818
+ }
38819
+ readClusterStepSettings({});
38438
38820
  object({
38439
38821
  /**
38440
38822
  * Fraction of the box's own size added on EACH side before cutting.