@camstack/addon-notifiers 1.2.30 → 1.2.32

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
@@ -5804,6 +5804,13 @@ var BaseAddon = class {
5804
5804
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5805
5805
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5806
5806
  _registeredCapNames = [];
5807
+ /**
5808
+ * True only after `readAddonStore` actually answered. Constructor
5809
+ * defaults look like stored config when the store is down — a forked
5810
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5811
+ * mode, 2026-08-25) is not "the operator chose this".
5812
+ */
5813
+ settingsStoreReady = false;
5807
5814
  /** Default config values. Provided via constructor. */
5808
5815
  defaults;
5809
5816
  constructor(defaults) {
@@ -6204,7 +6211,9 @@ var BaseAddon = class {
6204
6211
  ];
6205
6212
  let lastErr;
6206
6213
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6207
- return await settings.readAddonStore() ?? {};
6214
+ const stored = await settings.readAddonStore() ?? {};
6215
+ this.settingsStoreReady = true;
6216
+ return stored;
6208
6217
  } catch (err) {
6209
6218
  lastErr = err;
6210
6219
  const msg = err instanceof Error ? err.message : String(err);
@@ -6212,6 +6221,7 @@ var BaseAddon = class {
6212
6221
  if (attempt === delaysMs.length) break;
6213
6222
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6214
6223
  }
6224
+ this.settingsStoreReady = false;
6215
6225
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6216
6226
  return {};
6217
6227
  }
@@ -8126,6 +8136,15 @@ var LabelDefinitionSchema = object({
8126
8136
  description: string().optional(),
8127
8137
  icon: string().optional()
8128
8138
  });
8139
+ var ClassMapDefinitionSchema = object({
8140
+ mapping: record(string(), _enum([
8141
+ "person",
8142
+ "vehicle",
8143
+ "animal",
8144
+ "package"
8145
+ ])),
8146
+ preserveOriginal: boolean()
8147
+ });
8129
8148
  var MODEL_FORMATS = [
8130
8149
  "onnx",
8131
8150
  "coreml",
@@ -8209,6 +8228,12 @@ var ModelVariantGroupSchema = object({
8209
8228
  */
8210
8229
  resolution: number().int().positive().optional()
8211
8230
  });
8231
+ var ModelProviderIdSchema = _enum([
8232
+ "camstack",
8233
+ "frigate",
8234
+ "scrypted",
8235
+ "custom"
8236
+ ]);
8212
8237
  var ModelCatalogEntrySchema = object({
8213
8238
  id: string(),
8214
8239
  name: string(),
@@ -8304,7 +8329,19 @@ var ModelCatalogEntrySchema = object({
8304
8329
  * `id` stays the source of truth for resolution/download/persistence; grouping
8305
8330
  * is a presentation overlay resolved back to an `id`.
8306
8331
  */
8307
- group: ModelVariantGroupSchema.optional()
8332
+ group: ModelVariantGroupSchema.optional(),
8333
+ /**
8334
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8335
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8336
+ * persisted before this field existed (`inferModelProvider` fills those).
8337
+ */
8338
+ provider: ModelProviderIdSchema.optional(),
8339
+ /**
8340
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8341
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8342
+ * labels already ARE the CamStack macros (Scrypted identity map).
8343
+ */
8344
+ classMap: ClassMapDefinitionSchema.optional()
8308
8345
  });
8309
8346
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8310
8347
  format: literal("openvino"),
@@ -8333,7 +8370,8 @@ var ModelConvertMetadataSchema = object({
8333
8370
  "ocr",
8334
8371
  "segmentation"
8335
8372
  ]),
8336
- faceAlignment: boolean().optional()
8373
+ faceAlignment: boolean().optional(),
8374
+ classMap: ClassMapDefinitionSchema.optional()
8337
8375
  });
8338
8376
  var ConvertResultSchema = object({
8339
8377
  entry: ModelCatalogEntrySchema,
@@ -11867,6 +11905,27 @@ var LinkedDeviceSchema = object({
11867
11905
  features: array(string()),
11868
11906
  producesTrackedEvents: boolean().optional()
11869
11907
  });
11908
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
11909
+ * The batch answer needs the tag; the single-device answer already has it
11910
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
11911
+ var LinkedDevicesForDeviceSchema = object({
11912
+ deviceId: number(),
11913
+ mode: LinkedDevicesModeSchema,
11914
+ devices: array(LinkedDeviceSchema)
11915
+ });
11916
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
11917
+ * `getAllBindings` all answer in. Declared once: three copies of the same
11918
+ * object literal is exactly how the three drift apart. */
11919
+ var DeviceBindingsForDeviceSchema = object({
11920
+ deviceId: number(),
11921
+ entries: array(object({
11922
+ capName: string(),
11923
+ kind: _enum(["native", "wrapped"]),
11924
+ providerAddonId: string(),
11925
+ providerNodeId: string(),
11926
+ nativeAddonId: string()
11927
+ }))
11928
+ });
11870
11929
  var SavedDeviceRowSchema = object({
11871
11930
  /** Numeric id reserved at allocateDeviceId time. */
11872
11931
  id: number(),
@@ -12092,11 +12151,25 @@ method(object({
12092
12151
  projection: _enum(["full", "slim"]).optional(),
12093
12152
  /** Return only camera devices. Filtering server-side instead of
12094
12153
  * shipping 293 rows to find 12. */
12095
- isCamera: boolean().optional()
12154
+ isCamera: boolean().optional(),
12155
+ /**
12156
+ * Return only these device ids. For the caller that already KNOWS the
12157
+ * handful it wants and needs a field the id-bearing answer does not
12158
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12159
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12160
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12161
+ * refetches on the reconcile interval, on a phone.
12162
+ *
12163
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12164
+ * keys rather than rejecting them (verified against the live hub
12165
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12166
+ * it answers today and the caller filters as it already does.
12167
+ */
12168
+ deviceIds: array(number()).optional()
12096
12169
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12097
12170
  mode: LinkedDevicesModeSchema,
12098
12171
  devices: array(LinkedDeviceSchema)
12099
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12172
+ })), 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({
12100
12173
  deviceId: number(),
12101
12174
  values: record(string(), unknown())
12102
12175
  }), object({ success: literal(true) }), {
@@ -12123,25 +12196,7 @@ method(object({
12123
12196
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
12124
12197
  kind: "mutation",
12125
12198
  auth: "admin"
12126
- }), method(object({ deviceId: number() }), object({
12127
- deviceId: number(),
12128
- entries: array(object({
12129
- capName: string(),
12130
- kind: _enum(["native", "wrapped"]),
12131
- providerAddonId: string(),
12132
- providerNodeId: string(),
12133
- nativeAddonId: string()
12134
- }))
12135
- })), method(object({}), array(object({
12136
- deviceId: number(),
12137
- entries: array(object({
12138
- capName: string(),
12139
- kind: _enum(["native", "wrapped"]),
12140
- providerAddonId: string(),
12141
- providerNodeId: string(),
12142
- nativeAddonId: string()
12143
- }))
12144
- }))), method(object({
12199
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12145
12200
  deviceId: number(),
12146
12201
  capName: string(),
12147
12202
  wrapperAddonId: string(),
@@ -14527,12 +14582,15 @@ var NcOccupancyConditionSchema = object({
14527
14582
  * there is no second switch that can disagree with the first and every rule
14528
14583
  * authored before the decision migrates for free (`audioModeOf`):
14529
14584
  *
14530
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14531
- * classifier labels with one of them. No window, no percentage:
14532
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14533
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14534
- * the analyzer's (`classificationMinScore`, per device) a label only
14535
- * reaches this condition if the classifier was already confident enough.
14585
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14586
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14587
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14588
+ * frames is the wrong question for a classifier that labels 1–3 frames
14589
+ * per episode. The count window is the brake that drops a single-frame
14590
+ * false positive; the rule's own `throttle` cooldown is the other. The
14591
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14592
+ * per device) — a label only reaches this condition if the classifier was
14593
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14536
14594
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14537
14595
  * the condition: at least `hitPercent`% of the samples over
14538
14596
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14559,14 +14617,22 @@ var NcOccupancyConditionSchema = object({
14559
14617
  * an operator who typed `dog` mean the same thing.
14560
14618
  */
14561
14619
  var NcAudioConditionSchema = object({
14562
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14620
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14563
14621
  labels: array(string().min(1)).min(1).optional(),
14564
14622
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14565
14623
  dbThreshold: number().min(-96).max(0).optional(),
14566
14624
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14567
14625
  hitPercent: number().int().min(1).max(100).default(60),
14568
14626
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14569
- samplingSeconds: number().int().min(1).max(300).default(10)
14627
+ samplingSeconds: number().int().min(1).max(300).default(10),
14628
+ /**
14629
+ * LABEL MODE: how many labelled frames must land inside
14630
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14631
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14632
+ */
14633
+ confirmHits: number().int().min(1).max(20).optional(),
14634
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14635
+ confirmWindowSec: number().int().min(1).max(60).optional()
14570
14636
  });
14571
14637
  /**
14572
14638
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -16940,6 +17006,46 @@ var RecentTracksPageSchema = object({
16940
17006
  /** Cursor for the next page, or null when this page is the last. */
16941
17007
  nextCursor: string().nullable()
16942
17008
  });
17009
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
17010
+ var LIST_GROUPS_MAX_LIMIT = 100;
17011
+ var AnalyticsGroupRecordSchema = object({
17012
+ id: string(),
17013
+ deviceId: number().int(),
17014
+ openedAt: number().int(),
17015
+ closedAt: number().int(),
17016
+ timestamp: number().int(),
17017
+ memberCount: number().int(),
17018
+ memberTrackIds: array(string()).readonly(),
17019
+ className: string(),
17020
+ classes: array(string()).readonly(),
17021
+ /** Relative event-media path, or null when the group has no picture yet. */
17022
+ mediaUrl: string().nullable(),
17023
+ singleton: boolean()
17024
+ });
17025
+ var AnalyticsGroupMemberSchema = object({
17026
+ trackId: string(),
17027
+ deviceId: number().int(),
17028
+ className: string(),
17029
+ firstSeen: number().int(),
17030
+ lastSeen: number().int(),
17031
+ mediaUrl: string().nullable()
17032
+ });
17033
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
17034
+ var ListGroupsQueryInput = object({
17035
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
17036
+ deviceIds: array(number()),
17037
+ /** Window lower bound on `closedAt` (inclusive). */
17038
+ since: number().optional(),
17039
+ /** Window upper bound on `openedAt` (inclusive). */
17040
+ until: number().optional(),
17041
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
17042
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
17043
+ cursor: string().optional()
17044
+ });
17045
+ var ListGroupsPageSchema = object({
17046
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
17047
+ nextCursor: string().nullable()
17048
+ });
16943
17049
  var KeyEventQueryInput = object({
16944
17050
  deviceId: number(),
16945
17051
  /** Window lower bound (track firstSeen ≥ since). */
@@ -17015,7 +17121,9 @@ var TrackCascadeCountsSchema = object({
17015
17121
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
17016
17122
  plates: number().int(),
17017
17123
  /** Per-track CLIP search vectors removed (best-effort). */
17018
- embeddings: number().int()
17124
+ embeddings: number().int(),
17125
+ /** Group membership + group rows removed with their last member (best-effort). */
17126
+ groups: number().int()
17019
17127
  });
17020
17128
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
17021
17129
  var DiskReconcileCountsSchema = object({
@@ -17161,7 +17269,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17161
17269
  * stationary registry). Default false: the timeline lists passages,
17162
17270
  * not parking records (operator decision, 2026-08-15). */
17163
17271
  includeStationary: boolean().optional()
17164
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17272
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17273
+ deviceId: number(),
17274
+ groupId: string().min(1)
17275
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17165
17276
  kind: "mutation",
17166
17277
  auth: "admin"
17167
17278
  }), 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({
@@ -17379,6 +17490,33 @@ var NativeCropRefSchema = object({
17379
17490
  h: number()
17380
17491
  })
17381
17492
  });
17493
+ object({
17494
+ crop: object({
17495
+ left: number(),
17496
+ top: number(),
17497
+ width: number().positive(),
17498
+ height: number().positive()
17499
+ }).optional(),
17500
+ content: object({
17501
+ width: number().int().positive(),
17502
+ height: number().int().positive()
17503
+ }),
17504
+ fit: _enum(["stretch", "contain"]),
17505
+ format: _enum([
17506
+ "rgb",
17507
+ "gray",
17508
+ "jpeg"
17509
+ ])
17510
+ });
17511
+ var FrameRefSchema = object({
17512
+ registryId: string().min(1),
17513
+ id: string().min(1),
17514
+ width: number().int().positive(),
17515
+ height: number().int().positive(),
17516
+ format: _enum(["rgb", "gray"]),
17517
+ timestamp: number(),
17518
+ capturedAt: number().optional()
17519
+ });
17382
17520
  var ModelFormatSchema$1 = _enum([
17383
17521
  "onnx",
17384
17522
  "coreml",
@@ -17444,7 +17582,8 @@ var PipelineModelOptionSchema = object({
17444
17582
  sizeMB: number()
17445
17583
  })),
17446
17584
  group: ModelVariantGroupSchema.optional(),
17447
- legacy: boolean().optional()
17585
+ legacy: boolean().optional(),
17586
+ provider: ModelProviderIdSchema.optional()
17448
17587
  });
17449
17588
  var ConfigFieldBridge = custom();
17450
17589
  var PipelineAddonSchemaSchema = object({
@@ -17623,6 +17762,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
17623
17762
  steps: array(PipelineStepInputSchema).min(1),
17624
17763
  frame: FrameInputSchema.optional(),
17625
17764
  /**
17765
+ * Process-local lazy frame. Valid only when caller and provider resolve
17766
+ * in the same execution-group process; split/cross-node callers use
17767
+ * `frame`/`image` inline compatibility instead.
17768
+ */
17769
+ frameRef: FrameRefSchema.optional(),
17770
+ /**
17626
17771
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
17627
17772
  * the decoded pixels live in. One more member of the one-of
17628
17773
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -17878,7 +18023,10 @@ var NativeCropResultSchema = object({
17878
18023
  * Which source served this crop, so a quality-sensitive consumer (the native
17879
18024
  * `keyFrame`) can reject a degraded fallback:
17880
18025
  * - `native` — cut from the decode worker's retained NATIVE surface (the
17881
- * quality path).
18026
+ * quality path). A subject-tile serve is also native-resolution and stays
18027
+ * `native` here: the public enum cannot name `tile` without a breaking cap
18028
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
18029
+ * internal crop result (`nativeHits` vs `tileHits`).
17882
18030
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
17883
18031
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
17884
18032
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -18369,12 +18517,41 @@ var RunnerLocalLoadSchema = object({
18369
18517
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
18370
18518
  * working unchanged when they switch to reading from the runner cap.
18371
18519
  */
18520
+ var FrameLazyCountersSchema = object({
18521
+ framesDecoded: number(),
18522
+ framesAdmitted: number(),
18523
+ framesDroppedPixelFree: number(),
18524
+ viewsMaterialized: number(),
18525
+ viewsSkipped: number(),
18526
+ workerToRunnerBytes: number(),
18527
+ runnerToPoolRawBytes: number(),
18528
+ runnerToPoolJpegBytes: number(),
18529
+ onDemandFullFrameRequests: number(),
18530
+ onDemandCropRequests: number(),
18531
+ nativeHits: number(),
18532
+ nativeMisses: number(),
18533
+ tileHits: number(),
18534
+ tileMisses: number(),
18535
+ fallbackHits: number(),
18536
+ fallbackMisses: number(),
18537
+ retainedWritesAvoided: number(),
18538
+ residentRefs: number(),
18539
+ residentBytes: number(),
18540
+ releases: number(),
18541
+ evictions: number(),
18542
+ staleMisses: number()
18543
+ });
18544
+ var FrameLazyMetricsSchema = object({
18545
+ node: FrameLazyCountersSchema,
18546
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
18547
+ });
18372
18548
  var RunnerLocalMetricsSchema = object({
18373
18549
  nodeId: string(),
18374
18550
  activeCameras: number(),
18375
18551
  throttledCameras: number(),
18376
18552
  avgInferenceTimeMs: number(),
18377
- queueDepth: number()
18553
+ queueDepth: number(),
18554
+ frameLazy: FrameLazyMetricsSchema.optional()
18378
18555
  });
18379
18556
  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({
18380
18557
  handle: FrameHandleSchema,
@@ -19674,6 +19851,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
19674
19851
  location: StorageLocationSchema,
19675
19852
  relativePath: string()
19676
19853
  }), _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" });
19854
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
19855
+ var ProfileSettingsSchemaBridge = unknown().nullable();
19856
+ var ProfileSettingsBagSchema = record(string(), unknown());
19677
19857
  /**
19678
19858
  * A live terminal session hosted by the provider addon. Output and input do
19679
19859
  * NOT flow through the capability — they use the addon data plane
@@ -19703,7 +19883,14 @@ var TerminalSessionInfoSchema = object({
19703
19883
  var TerminalProfileInfoSchema = object({
19704
19884
  profileId: string(),
19705
19885
  label: string(),
19706
- description: string().optional()
19886
+ description: string().optional(),
19887
+ /** Spawn defaults the instance form copies on create. */
19888
+ executable: string().optional(),
19889
+ args: array(string()).readonly().optional(),
19890
+ cwd: string().optional(),
19891
+ environment: array(string()).readonly().optional(),
19892
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
19893
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
19707
19894
  });
19708
19895
  /**
19709
19896
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -19716,7 +19903,12 @@ var TerminalInstanceInfoSchema = object({
19716
19903
  profileId: string(),
19717
19904
  profileLabel: string(),
19718
19905
  name: string(),
19719
- enabled: boolean()
19906
+ enabled: boolean(),
19907
+ executable: string(),
19908
+ args: array(string()).readonly(),
19909
+ cwd: string(),
19910
+ environment: array(string()).readonly(),
19911
+ profileSettings: ProfileSettingsBagSchema
19720
19912
  });
19721
19913
  var TerminalLegacyCameraSchema = object({
19722
19914
  stableId: string(),
@@ -19746,7 +19938,23 @@ var TerminalOutputBatchSchema = object({
19746
19938
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
19747
19939
  targetNodeId: string().min(1),
19748
19940
  profileId: string().min(1),
19749
- name: string().trim().min(1).max(160).optional()
19941
+ name: string().trim().min(1).max(160).optional(),
19942
+ executable: string().max(1024).optional(),
19943
+ args: array(string().max(2048)).max(64).optional(),
19944
+ cwd: string().max(1024).optional(),
19945
+ environment: array(string().max(4096)).max(64).optional(),
19946
+ profileSettings: ProfileSettingsBagSchema.optional()
19947
+ }), TerminalInstanceInfoSchema, {
19948
+ kind: "mutation",
19949
+ auth: "admin"
19950
+ }), method(object({
19951
+ instanceId: string().min(1),
19952
+ name: string().trim().min(1).max(160).optional(),
19953
+ executable: string().max(1024).optional(),
19954
+ args: array(string().max(2048)).max(64).optional(),
19955
+ cwd: string().max(1024).optional(),
19956
+ environment: array(string().max(4096)).max(64).optional(),
19957
+ profileSettings: ProfileSettingsBagSchema.optional()
19750
19958
  }), TerminalInstanceInfoSchema, {
19751
19959
  kind: "mutation",
19752
19960
  auth: "admin"
@@ -19768,7 +19976,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
19768
19976
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
19769
19977
  profileId: string(),
19770
19978
  cols: number().int().positive(),
19771
- rows: number().int().positive()
19979
+ rows: number().int().positive(),
19980
+ executable: string().max(1024).optional(),
19981
+ args: array(string().max(2048)).max(64).optional(),
19982
+ cwd: string().max(1024).optional(),
19983
+ environment: array(string().max(4096)).max(64).optional()
19772
19984
  }), TerminalSessionInfoSchema, {
19773
19985
  kind: "mutation",
19774
19986
  auth: "admin"
@@ -22550,10 +22762,10 @@ DeviceType.LawnMower, method(object({ deviceId: number().int().nonnegative() }),
22550
22762
  *
22551
22763
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
22552
22764
  * to receive an ordered list of candidate base URLs it should race
22553
- * on connect — LAN IPv4 first (lowest latency when on same network),
22554
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
22555
- * race them with short timeouts and stick with the winner for the
22556
- * session.
22765
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
22766
+ * when on the same network), then public hostname (if a tunnel is
22767
+ * up). The SDK can race them with short timeouts and stick with the
22768
+ * winner for the session.
22557
22769
  *
22558
22770
  * Why hub-only: agents are not directly addressable by the operator's
22559
22771
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -22708,6 +22920,17 @@ var NotificationEndpointSchema = object({
22708
22920
  /** What the ranking currently resolves to (null when nothing is reachable). */
22709
22921
  resolved: string().nullable()
22710
22922
  });
22923
+ /**
22924
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
22925
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
22926
+ * currently expands to, so the UI can show the effective set either way.
22927
+ */
22928
+ var ViewerEndpointsSchema = object({
22929
+ /** The operator's explicit race set, or empty for AUTO. */
22930
+ baseUrls: array(string()).readonly(),
22931
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
22932
+ resolved: array(string()).readonly()
22933
+ });
22711
22934
  var AllowedAddressesSchema = object({
22712
22935
  /**
22713
22936
  * Allowlist of interface addresses operators have explicitly opted
@@ -22716,6 +22939,20 @@ var AllowedAddressesSchema = object({
22716
22939
  * Network Addresses admin page and persisted by the addon.
22717
22940
  */
22718
22941
  addresses: array(string()).readonly() });
22942
+ var TlsStatusSchema = object({
22943
+ mode: _enum([
22944
+ "generated",
22945
+ "uploaded",
22946
+ "disabled"
22947
+ ]),
22948
+ leafFingerprintSha256: string().nullable(),
22949
+ caFingerprintSha256: string().nullable(),
22950
+ validTo: string().nullable(),
22951
+ sans: array(string()),
22952
+ caCertPem: string().nullable(),
22953
+ reissueError: string().nullable(),
22954
+ restartRequired: boolean()
22955
+ });
22719
22956
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
22720
22957
  /**
22721
22958
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -22725,17 +22962,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
22725
22962
  */
22726
22963
  port: number().int().min(1).max(65535).optional(),
22727
22964
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
22728
- * candidate. Default `true`. */
22965
+ * candidate. Default `false` — loopback is not a client route. */
22729
22966
  includeLoopback: boolean().optional(),
22730
- /** Skip IPv6 entries. Some legacy clients can't parse them.
22731
- * Default `false`. */
22967
+ /** Skip IPv6 entries. Default `false` the palette includes stable
22968
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
22969
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
22732
22970
  ipv4Only: boolean().optional(),
22733
22971
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
22734
22972
  * Pass `'https'` when the caller is itself loaded over HTTPS
22735
22973
  * to avoid mixed-content blocks in the browser. The public
22736
22974
  * tunnel always emits `https://` regardless. */
22737
22975
  scheme: _enum(["http", "https"]).optional()
22738
- }), 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" });
22976
+ }), 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, {
22977
+ kind: "mutation",
22978
+ auth: "admin"
22979
+ }), method(object({
22980
+ certPem: string().min(1),
22981
+ keyPem: string().min(1),
22982
+ caPem: string().optional()
22983
+ }), TlsStatusSchema, {
22984
+ kind: "mutation",
22985
+ auth: "admin"
22986
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
22987
+ kind: "mutation",
22988
+ auth: "admin"
22989
+ });
22739
22990
  object({
22740
22991
  /** Lifecycle state of the lock. `jammed` means the motor reported
22741
22992
  * failure to reach the target — operator intervention required. */
@@ -23916,7 +24167,12 @@ var PlateInfoSchema = object({
23916
24167
  plateBbox: BoundingBoxSchema.optional(),
23917
24168
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
23918
24169
  keyFrameMediaKey: string().optional(),
23919
- base64: string().optional()
24170
+ base64: string().optional(),
24171
+ /**
24172
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
24173
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
24174
+ */
24175
+ cropUrl: string().optional()
23920
24176
  });
23921
24177
  var MediaFileLiteSchema = object({
23922
24178
  key: string(),
@@ -27556,6 +27812,12 @@ Object.freeze({
27556
27812
  addonId: null,
27557
27813
  access: "view"
27558
27814
  },
27815
+ "deviceManager.getBindingsBatch": {
27816
+ capName: "device-manager",
27817
+ capScope: "system",
27818
+ addonId: null,
27819
+ access: "view"
27820
+ },
27559
27821
  "deviceManager.getChildren": {
27560
27822
  capName: "device-manager",
27561
27823
  capScope: "system",
@@ -27616,6 +27878,12 @@ Object.freeze({
27616
27878
  addonId: null,
27617
27879
  access: "view"
27618
27880
  },
27881
+ "deviceManager.getLinkedDevicesBatch": {
27882
+ capName: "device-manager",
27883
+ capScope: "system",
27884
+ addonId: null,
27885
+ access: "view"
27886
+ },
27619
27887
  "deviceManager.getRoleDisplayDefaults": {
27620
27888
  capName: "device-manager",
27621
27889
  capScope: "system",
@@ -28498,6 +28766,12 @@ Object.freeze({
28498
28766
  addonId: null,
28499
28767
  access: "create"
28500
28768
  },
28769
+ "localNetwork.downloadCa": {
28770
+ capName: "local-network",
28771
+ capScope: "system",
28772
+ addonId: null,
28773
+ access: "view"
28774
+ },
28501
28775
  "localNetwork.getAllowedAddresses": {
28502
28776
  capName: "local-network",
28503
28777
  capScope: "system",
@@ -28522,18 +28796,42 @@ Object.freeze({
28522
28796
  addonId: null,
28523
28797
  access: "view"
28524
28798
  },
28799
+ "localNetwork.getTlsStatus": {
28800
+ capName: "local-network",
28801
+ capScope: "system",
28802
+ addonId: null,
28803
+ access: "view"
28804
+ },
28805
+ "localNetwork.getViewerEndpoints": {
28806
+ capName: "local-network",
28807
+ capScope: "system",
28808
+ addonId: null,
28809
+ access: "view"
28810
+ },
28525
28811
  "localNetwork.list": {
28526
28812
  capName: "local-network",
28527
28813
  capScope: "system",
28528
28814
  addonId: null,
28529
28815
  access: "view"
28530
28816
  },
28817
+ "localNetwork.regenerateCertificate": {
28818
+ capName: "local-network",
28819
+ capScope: "system",
28820
+ addonId: null,
28821
+ access: "create"
28822
+ },
28531
28823
  "localNetwork.resetAllowlistToBestMatch": {
28532
28824
  capName: "local-network",
28533
28825
  capScope: "system",
28534
28826
  addonId: null,
28535
28827
  access: "delete"
28536
28828
  },
28829
+ "localNetwork.revertToGeneratedCertificate": {
28830
+ capName: "local-network",
28831
+ capScope: "system",
28832
+ addonId: null,
28833
+ access: "create"
28834
+ },
28537
28835
  "localNetwork.setAllowedAddresses": {
28538
28836
  capName: "local-network",
28539
28837
  capScope: "system",
@@ -28546,6 +28844,18 @@ Object.freeze({
28546
28844
  addonId: null,
28547
28845
  access: "create"
28548
28846
  },
28847
+ "localNetwork.setViewerEndpoints": {
28848
+ capName: "local-network",
28849
+ capScope: "system",
28850
+ addonId: null,
28851
+ access: "create"
28852
+ },
28853
+ "localNetwork.uploadCertificate": {
28854
+ capName: "local-network",
28855
+ capScope: "system",
28856
+ addonId: null,
28857
+ access: "create"
28858
+ },
28549
28859
  "lockControl.lock": {
28550
28860
  capName: "lock-control",
28551
28861
  capScope: "device",
@@ -29344,6 +29654,12 @@ Object.freeze({
29344
29654
  addonId: null,
29345
29655
  access: "view"
29346
29656
  },
29657
+ "pipelineAnalytics.getGroup": {
29658
+ capName: "pipeline-analytics",
29659
+ capScope: "device",
29660
+ addonId: null,
29661
+ access: "view"
29662
+ },
29347
29663
  "pipelineAnalytics.getKeyEvents": {
29348
29664
  capName: "pipeline-analytics",
29349
29665
  capScope: "device",
@@ -29428,6 +29744,12 @@ Object.freeze({
29428
29744
  addonId: null,
29429
29745
  access: "view"
29430
29746
  },
29747
+ "pipelineAnalytics.listGroups": {
29748
+ capName: "pipeline-analytics",
29749
+ capScope: "device",
29750
+ addonId: null,
29751
+ access: "view"
29752
+ },
29431
29753
  "pipelineAnalytics.listOpsLog": {
29432
29754
  capName: "pipeline-analytics",
29433
29755
  capScope: "device",
@@ -31426,6 +31748,12 @@ Object.freeze({
31426
31748
  addonId: null,
31427
31749
  access: "create"
31428
31750
  },
31751
+ "terminalSession.updateInstance": {
31752
+ capName: "terminal-session",
31753
+ capScope: "system",
31754
+ addonId: null,
31755
+ access: "create"
31756
+ },
31429
31757
  "terminalSession.writeInput": {
31430
31758
  capName: "terminal-session",
31431
31759
  capScope: "system",
@@ -32203,6 +32531,11 @@ Object.freeze({
32203
32531
  form: "single",
32204
32532
  optional: false
32205
32533
  }],
32534
+ "deviceManager.getBindingsBatch": [{
32535
+ name: "deviceIds",
32536
+ form: "array",
32537
+ optional: false
32538
+ }],
32206
32539
  "deviceManager.getChildren": [{
32207
32540
  name: "parentDeviceId",
32208
32541
  form: "single",
@@ -32248,6 +32581,11 @@ Object.freeze({
32248
32581
  form: "single",
32249
32582
  optional: false
32250
32583
  }],
32584
+ "deviceManager.getLinkedDevicesBatch": [{
32585
+ name: "deviceIds",
32586
+ form: "array",
32587
+ optional: false
32588
+ }],
32251
32589
  "deviceManager.getSettingsSchema": [{
32252
32590
  name: "deviceId",
32253
32591
  form: "single",
@@ -32268,6 +32606,11 @@ Object.freeze({
32268
32606
  form: "single",
32269
32607
  optional: false
32270
32608
  }],
32609
+ "deviceManager.listAll": [{
32610
+ name: "deviceIds",
32611
+ form: "array",
32612
+ optional: true
32613
+ }],
32271
32614
  "deviceManager.loadConfig": [{
32272
32615
  name: "deviceId",
32273
32616
  form: "single",
@@ -32841,6 +33184,11 @@ Object.freeze({
32841
33184
  form: "single",
32842
33185
  optional: false
32843
33186
  }],
33187
+ "pipelineAnalytics.getGroup": [{
33188
+ name: "deviceId",
33189
+ form: "single",
33190
+ optional: false
33191
+ }],
32844
33192
  "pipelineAnalytics.getKeyEvents": [{
32845
33193
  name: "deviceId",
32846
33194
  form: "single",
@@ -32896,6 +33244,11 @@ Object.freeze({
32896
33244
  form: "array",
32897
33245
  optional: false
32898
33246
  }],
33247
+ "pipelineAnalytics.listGroups": [{
33248
+ name: "deviceIds",
33249
+ form: "array",
33250
+ optional: false
33251
+ }],
32899
33252
  "pipelineAnalytics.listOpsLog": [{
32900
33253
  name: "deviceId",
32901
33254
  form: "single",
@@ -34120,6 +34473,35 @@ Object.freeze(Object.fromEntries([{
34120
34473
  }]
34121
34474
  }].map((s) => [s.stepId, s.defaultModelId])));
34122
34475
  string().min(1);
34476
+ var CLUSTER_STEP_SETTING_FIELDS = [{
34477
+ stepId: "face-embedding",
34478
+ key: "minLandmarkFaceSize",
34479
+ label: "Min face size for recognition (detection px)",
34480
+ 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.",
34481
+ type: "slider",
34482
+ min: 0,
34483
+ max: 64,
34484
+ step: 2,
34485
+ default: 24
34486
+ }];
34487
+ function clusterStepSettingKey(stepId, fieldKey) {
34488
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
34489
+ }
34490
+ var ClusterSettingNumberSchema = number().finite();
34491
+ function readClusterStepSettings(config) {
34492
+ const out = {};
34493
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
34494
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
34495
+ const value = parsed.success ? parsed.data : field.default;
34496
+ const existing = out[field.stepId] ?? {};
34497
+ out[field.stepId] = {
34498
+ ...existing,
34499
+ [field.key]: value
34500
+ };
34501
+ }
34502
+ return out;
34503
+ }
34504
+ readClusterStepSettings({});
34123
34505
  object({
34124
34506
  /**
34125
34507
  * Fraction of the box's own size added on EACH side before cutting.