@camstack/addon-provider-petkit 0.2.26 → 0.2.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +432 -50
  2. package/dist/addon.mjs +432 -50
  3. package/package.json +1 -1
package/dist/addon.mjs CHANGED
@@ -6902,6 +6902,13 @@ var BaseAddon = class {
6902
6902
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
6903
6903
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
6904
6904
  _registeredCapNames = [];
6905
+ /**
6906
+ * True only after `readAddonStore` actually answered. Constructor
6907
+ * defaults look like stored config when the store is down — a forked
6908
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
6909
+ * mode, 2026-08-25) is not "the operator chose this".
6910
+ */
6911
+ settingsStoreReady = false;
6905
6912
  /** Default config values. Provided via constructor. */
6906
6913
  defaults;
6907
6914
  constructor(defaults) {
@@ -7302,7 +7309,9 @@ var BaseAddon = class {
7302
7309
  ];
7303
7310
  let lastErr;
7304
7311
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
7305
- return await settings.readAddonStore() ?? {};
7312
+ const stored = await settings.readAddonStore() ?? {};
7313
+ this.settingsStoreReady = true;
7314
+ return stored;
7306
7315
  } catch (err) {
7307
7316
  lastErr = err;
7308
7317
  const msg = err instanceof Error ? err.message : String(err);
@@ -7310,6 +7319,7 @@ var BaseAddon = class {
7310
7319
  if (attempt === delaysMs.length) break;
7311
7320
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
7312
7321
  }
7322
+ this.settingsStoreReady = false;
7313
7323
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
7314
7324
  return {};
7315
7325
  }
@@ -9121,6 +9131,15 @@ var LabelDefinitionSchema = object({
9121
9131
  description: string().optional(),
9122
9132
  icon: string().optional()
9123
9133
  });
9134
+ var ClassMapDefinitionSchema = object({
9135
+ mapping: record(string(), _enum([
9136
+ "person",
9137
+ "vehicle",
9138
+ "animal",
9139
+ "package"
9140
+ ])),
9141
+ preserveOriginal: boolean()
9142
+ });
9124
9143
  var MODEL_FORMATS = [
9125
9144
  "onnx",
9126
9145
  "coreml",
@@ -9204,6 +9223,12 @@ var ModelVariantGroupSchema = object({
9204
9223
  */
9205
9224
  resolution: number().int().positive().optional()
9206
9225
  });
9226
+ var ModelProviderIdSchema = _enum([
9227
+ "camstack",
9228
+ "frigate",
9229
+ "scrypted",
9230
+ "custom"
9231
+ ]);
9207
9232
  var ModelCatalogEntrySchema = object({
9208
9233
  id: string(),
9209
9234
  name: string(),
@@ -9299,7 +9324,19 @@ var ModelCatalogEntrySchema = object({
9299
9324
  * `id` stays the source of truth for resolution/download/persistence; grouping
9300
9325
  * is a presentation overlay resolved back to an `id`.
9301
9326
  */
9302
- group: ModelVariantGroupSchema.optional()
9327
+ group: ModelVariantGroupSchema.optional(),
9328
+ /**
9329
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
9330
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
9331
+ * persisted before this field existed (`inferModelProvider` fills those).
9332
+ */
9333
+ provider: ModelProviderIdSchema.optional(),
9334
+ /**
9335
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
9336
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
9337
+ * labels already ARE the CamStack macros (Scrypted identity map).
9338
+ */
9339
+ classMap: ClassMapDefinitionSchema.optional()
9303
9340
  });
9304
9341
  var ConvertTargetSchema = discriminatedUnion("format", [object({
9305
9342
  format: literal("openvino"),
@@ -9328,7 +9365,8 @@ var ModelConvertMetadataSchema = object({
9328
9365
  "ocr",
9329
9366
  "segmentation"
9330
9367
  ]),
9331
- faceAlignment: boolean().optional()
9368
+ faceAlignment: boolean().optional(),
9369
+ classMap: ClassMapDefinitionSchema.optional()
9332
9370
  });
9333
9371
  var ConvertResultSchema = object({
9334
9372
  entry: ModelCatalogEntrySchema,
@@ -13102,6 +13140,27 @@ var LinkedDeviceSchema = object({
13102
13140
  features: array(string()),
13103
13141
  producesTrackedEvents: boolean().optional()
13104
13142
  });
13143
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
13144
+ * The batch answer needs the tag; the single-device answer already has it
13145
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
13146
+ var LinkedDevicesForDeviceSchema = object({
13147
+ deviceId: number(),
13148
+ mode: LinkedDevicesModeSchema,
13149
+ devices: array(LinkedDeviceSchema)
13150
+ });
13151
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
13152
+ * `getAllBindings` all answer in. Declared once: three copies of the same
13153
+ * object literal is exactly how the three drift apart. */
13154
+ var DeviceBindingsForDeviceSchema = object({
13155
+ deviceId: number(),
13156
+ entries: array(object({
13157
+ capName: string(),
13158
+ kind: _enum(["native", "wrapped"]),
13159
+ providerAddonId: string(),
13160
+ providerNodeId: string(),
13161
+ nativeAddonId: string()
13162
+ }))
13163
+ });
13105
13164
  var SavedDeviceRowSchema = object({
13106
13165
  /** Numeric id reserved at allocateDeviceId time. */
13107
13166
  id: number(),
@@ -13327,11 +13386,25 @@ method(object({
13327
13386
  projection: _enum(["full", "slim"]).optional(),
13328
13387
  /** Return only camera devices. Filtering server-side instead of
13329
13388
  * shipping 293 rows to find 12. */
13330
- isCamera: boolean().optional()
13389
+ isCamera: boolean().optional(),
13390
+ /**
13391
+ * Return only these device ids. For the caller that already KNOWS the
13392
+ * handful it wants and needs a field the id-bearing answer does not
13393
+ * carry — the viewer's linked-devices panel joins `type` and `online`
13394
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
13395
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
13396
+ * refetches on the reconcile interval, on a phone.
13397
+ *
13398
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
13399
+ * keys rather than rejecting them (verified against the live hub
13400
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
13401
+ * it answers today and the caller filters as it already does.
13402
+ */
13403
+ deviceIds: array(number()).optional()
13331
13404
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
13332
13405
  mode: LinkedDevicesModeSchema,
13333
13406
  devices: array(LinkedDeviceSchema)
13334
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
13407
+ })), 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({
13335
13408
  deviceId: number(),
13336
13409
  values: record(string(), unknown())
13337
13410
  }), object({ success: literal(true) }), {
@@ -13358,25 +13431,7 @@ method(object({
13358
13431
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
13359
13432
  kind: "mutation",
13360
13433
  auth: "admin"
13361
- }), method(object({ deviceId: number() }), object({
13362
- deviceId: number(),
13363
- entries: array(object({
13364
- capName: string(),
13365
- kind: _enum(["native", "wrapped"]),
13366
- providerAddonId: string(),
13367
- providerNodeId: string(),
13368
- nativeAddonId: string()
13369
- }))
13370
- })), method(object({}), array(object({
13371
- deviceId: number(),
13372
- entries: array(object({
13373
- capName: string(),
13374
- kind: _enum(["native", "wrapped"]),
13375
- providerAddonId: string(),
13376
- providerNodeId: string(),
13377
- nativeAddonId: string()
13378
- }))
13379
- }))), method(object({
13434
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
13380
13435
  deviceId: number(),
13381
13436
  capName: string(),
13382
13437
  wrapperAddonId: string(),
@@ -15786,12 +15841,15 @@ var NcOccupancyConditionSchema = object({
15786
15841
  * there is no second switch that can disagree with the first and every rule
15787
15842
  * authored before the decision migrates for free (`audioModeOf`):
15788
15843
  *
15789
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
15790
- * classifier labels with one of them. No window, no percentage:
15791
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
15792
- * `throttle` cooldown is the only brake. The per-label confidence floor is
15793
- * the analyzer's (`classificationMinScore`, per device) a label only
15794
- * reaches this condition if the classifier was already confident enough.
15844
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
15845
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
15846
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
15847
+ * frames is the wrong question for a classifier that labels 1–3 frames
15848
+ * per episode. The count window is the brake that drops a single-frame
15849
+ * false positive; the rule's own `throttle` cooldown is the other. The
15850
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
15851
+ * per device) — a label only reaches this condition if the classifier was
15852
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
15795
15853
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
15796
15854
  * the condition: at least `hitPercent`% of the samples over
15797
15855
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -15818,14 +15876,22 @@ var NcOccupancyConditionSchema = object({
15818
15876
  * an operator who typed `dog` mean the same thing.
15819
15877
  */
15820
15878
  var NcAudioConditionSchema = object({
15821
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
15879
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
15822
15880
  labels: array(string().min(1)).min(1).optional(),
15823
15881
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
15824
15882
  dbThreshold: number().min(-96).max(0).optional(),
15825
15883
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
15826
15884
  hitPercent: number().int().min(1).max(100).default(60),
15827
15885
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
15828
- samplingSeconds: number().int().min(1).max(300).default(10)
15886
+ samplingSeconds: number().int().min(1).max(300).default(10),
15887
+ /**
15888
+ * LABEL MODE: how many labelled frames must land inside
15889
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
15890
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
15891
+ */
15892
+ confirmHits: number().int().min(1).max(20).optional(),
15893
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
15894
+ confirmWindowSec: number().int().min(1).max(60).optional()
15829
15895
  });
15830
15896
  /**
15831
15897
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -18199,6 +18265,46 @@ var RecentTracksPageSchema = object({
18199
18265
  /** Cursor for the next page, or null when this page is the last. */
18200
18266
  nextCursor: string().nullable()
18201
18267
  });
18268
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
18269
+ var LIST_GROUPS_MAX_LIMIT = 100;
18270
+ var AnalyticsGroupRecordSchema = object({
18271
+ id: string(),
18272
+ deviceId: number().int(),
18273
+ openedAt: number().int(),
18274
+ closedAt: number().int(),
18275
+ timestamp: number().int(),
18276
+ memberCount: number().int(),
18277
+ memberTrackIds: array(string()).readonly(),
18278
+ className: string(),
18279
+ classes: array(string()).readonly(),
18280
+ /** Relative event-media path, or null when the group has no picture yet. */
18281
+ mediaUrl: string().nullable(),
18282
+ singleton: boolean()
18283
+ });
18284
+ var AnalyticsGroupMemberSchema = object({
18285
+ trackId: string(),
18286
+ deviceId: number().int(),
18287
+ className: string(),
18288
+ firstSeen: number().int(),
18289
+ lastSeen: number().int(),
18290
+ mediaUrl: string().nullable()
18291
+ });
18292
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
18293
+ var ListGroupsQueryInput = object({
18294
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
18295
+ deviceIds: array(number()),
18296
+ /** Window lower bound on `closedAt` (inclusive). */
18297
+ since: number().optional(),
18298
+ /** Window upper bound on `openedAt` (inclusive). */
18299
+ until: number().optional(),
18300
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
18301
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
18302
+ cursor: string().optional()
18303
+ });
18304
+ var ListGroupsPageSchema = object({
18305
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
18306
+ nextCursor: string().nullable()
18307
+ });
18202
18308
  var KeyEventQueryInput = object({
18203
18309
  deviceId: number(),
18204
18310
  /** Window lower bound (track firstSeen ≥ since). */
@@ -18274,7 +18380,9 @@ var TrackCascadeCountsSchema = object({
18274
18380
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
18275
18381
  plates: number().int(),
18276
18382
  /** Per-track CLIP search vectors removed (best-effort). */
18277
- embeddings: number().int()
18383
+ embeddings: number().int(),
18384
+ /** Group membership + group rows removed with their last member (best-effort). */
18385
+ groups: number().int()
18278
18386
  });
18279
18387
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
18280
18388
  var DiskReconcileCountsSchema = object({
@@ -18420,7 +18528,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18420
18528
  * stationary registry). Default false: the timeline lists passages,
18421
18529
  * not parking records (operator decision, 2026-08-15). */
18422
18530
  includeStationary: boolean().optional()
18423
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
18531
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
18532
+ deviceId: number(),
18533
+ groupId: string().min(1)
18534
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
18424
18535
  kind: "mutation",
18425
18536
  auth: "admin"
18426
18537
  }), 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({
@@ -18638,6 +18749,33 @@ var NativeCropRefSchema = object({
18638
18749
  h: number()
18639
18750
  })
18640
18751
  });
18752
+ object({
18753
+ crop: object({
18754
+ left: number(),
18755
+ top: number(),
18756
+ width: number().positive(),
18757
+ height: number().positive()
18758
+ }).optional(),
18759
+ content: object({
18760
+ width: number().int().positive(),
18761
+ height: number().int().positive()
18762
+ }),
18763
+ fit: _enum(["stretch", "contain"]),
18764
+ format: _enum([
18765
+ "rgb",
18766
+ "gray",
18767
+ "jpeg"
18768
+ ])
18769
+ });
18770
+ var FrameRefSchema = object({
18771
+ registryId: string().min(1),
18772
+ id: string().min(1),
18773
+ width: number().int().positive(),
18774
+ height: number().int().positive(),
18775
+ format: _enum(["rgb", "gray"]),
18776
+ timestamp: number(),
18777
+ capturedAt: number().optional()
18778
+ });
18641
18779
  var ModelFormatSchema$1 = _enum([
18642
18780
  "onnx",
18643
18781
  "coreml",
@@ -18703,7 +18841,8 @@ var PipelineModelOptionSchema = object({
18703
18841
  sizeMB: number()
18704
18842
  })),
18705
18843
  group: ModelVariantGroupSchema.optional(),
18706
- legacy: boolean().optional()
18844
+ legacy: boolean().optional(),
18845
+ provider: ModelProviderIdSchema.optional()
18707
18846
  });
18708
18847
  var ConfigFieldBridge = custom();
18709
18848
  var PipelineAddonSchemaSchema = object({
@@ -18882,6 +19021,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
18882
19021
  steps: array(PipelineStepInputSchema).min(1),
18883
19022
  frame: FrameInputSchema.optional(),
18884
19023
  /**
19024
+ * Process-local lazy frame. Valid only when caller and provider resolve
19025
+ * in the same execution-group process; split/cross-node callers use
19026
+ * `frame`/`image` inline compatibility instead.
19027
+ */
19028
+ frameRef: FrameRefSchema.optional(),
19029
+ /**
18885
19030
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
18886
19031
  * the decoded pixels live in. One more member of the one-of
18887
19032
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -19177,7 +19322,10 @@ var NativeCropResultSchema = object({
19177
19322
  * Which source served this crop, so a quality-sensitive consumer (the native
19178
19323
  * `keyFrame`) can reject a degraded fallback:
19179
19324
  * - `native` — cut from the decode worker's retained NATIVE surface (the
19180
- * quality path).
19325
+ * quality path). A subject-tile serve is also native-resolution and stays
19326
+ * `native` here: the public enum cannot name `tile` without a breaking cap
19327
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
19328
+ * internal crop result (`nativeHits` vs `tileHits`).
19181
19329
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
19182
19330
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
19183
19331
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -19668,12 +19816,41 @@ var RunnerLocalLoadSchema = object({
19668
19816
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
19669
19817
  * working unchanged when they switch to reading from the runner cap.
19670
19818
  */
19819
+ var FrameLazyCountersSchema = object({
19820
+ framesDecoded: number(),
19821
+ framesAdmitted: number(),
19822
+ framesDroppedPixelFree: number(),
19823
+ viewsMaterialized: number(),
19824
+ viewsSkipped: number(),
19825
+ workerToRunnerBytes: number(),
19826
+ runnerToPoolRawBytes: number(),
19827
+ runnerToPoolJpegBytes: number(),
19828
+ onDemandFullFrameRequests: number(),
19829
+ onDemandCropRequests: number(),
19830
+ nativeHits: number(),
19831
+ nativeMisses: number(),
19832
+ tileHits: number(),
19833
+ tileMisses: number(),
19834
+ fallbackHits: number(),
19835
+ fallbackMisses: number(),
19836
+ retainedWritesAvoided: number(),
19837
+ residentRefs: number(),
19838
+ residentBytes: number(),
19839
+ releases: number(),
19840
+ evictions: number(),
19841
+ staleMisses: number()
19842
+ });
19843
+ var FrameLazyMetricsSchema = object({
19844
+ node: FrameLazyCountersSchema,
19845
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
19846
+ });
19671
19847
  var RunnerLocalMetricsSchema = object({
19672
19848
  nodeId: string(),
19673
19849
  activeCameras: number(),
19674
19850
  throttledCameras: number(),
19675
19851
  avgInferenceTimeMs: number(),
19676
- queueDepth: number()
19852
+ queueDepth: number(),
19853
+ frameLazy: FrameLazyMetricsSchema.optional()
19677
19854
  });
19678
19855
  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({
19679
19856
  handle: FrameHandleSchema,
@@ -20973,6 +21150,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
20973
21150
  location: StorageLocationSchema,
20974
21151
  relativePath: string()
20975
21152
  }), _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" });
21153
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
21154
+ var ProfileSettingsSchemaBridge = unknown().nullable();
21155
+ var ProfileSettingsBagSchema = record(string(), unknown());
20976
21156
  /**
20977
21157
  * A live terminal session hosted by the provider addon. Output and input do
20978
21158
  * NOT flow through the capability — they use the addon data plane
@@ -21002,7 +21182,14 @@ var TerminalSessionInfoSchema = object({
21002
21182
  var TerminalProfileInfoSchema = object({
21003
21183
  profileId: string(),
21004
21184
  label: string(),
21005
- description: string().optional()
21185
+ description: string().optional(),
21186
+ /** Spawn defaults the instance form copies on create. */
21187
+ executable: string().optional(),
21188
+ args: array(string()).readonly().optional(),
21189
+ cwd: string().optional(),
21190
+ environment: array(string()).readonly().optional(),
21191
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
21192
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
21006
21193
  });
21007
21194
  /**
21008
21195
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -21015,7 +21202,12 @@ var TerminalInstanceInfoSchema = object({
21015
21202
  profileId: string(),
21016
21203
  profileLabel: string(),
21017
21204
  name: string(),
21018
- enabled: boolean()
21205
+ enabled: boolean(),
21206
+ executable: string(),
21207
+ args: array(string()).readonly(),
21208
+ cwd: string(),
21209
+ environment: array(string()).readonly(),
21210
+ profileSettings: ProfileSettingsBagSchema
21019
21211
  });
21020
21212
  var TerminalLegacyCameraSchema = object({
21021
21213
  stableId: string(),
@@ -21045,7 +21237,23 @@ var TerminalOutputBatchSchema = object({
21045
21237
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
21046
21238
  targetNodeId: string().min(1),
21047
21239
  profileId: string().min(1),
21048
- name: string().trim().min(1).max(160).optional()
21240
+ name: string().trim().min(1).max(160).optional(),
21241
+ executable: string().max(1024).optional(),
21242
+ args: array(string().max(2048)).max(64).optional(),
21243
+ cwd: string().max(1024).optional(),
21244
+ environment: array(string().max(4096)).max(64).optional(),
21245
+ profileSettings: ProfileSettingsBagSchema.optional()
21246
+ }), TerminalInstanceInfoSchema, {
21247
+ kind: "mutation",
21248
+ auth: "admin"
21249
+ }), method(object({
21250
+ instanceId: string().min(1),
21251
+ name: string().trim().min(1).max(160).optional(),
21252
+ executable: string().max(1024).optional(),
21253
+ args: array(string().max(2048)).max(64).optional(),
21254
+ cwd: string().max(1024).optional(),
21255
+ environment: array(string().max(4096)).max(64).optional(),
21256
+ profileSettings: ProfileSettingsBagSchema.optional()
21049
21257
  }), TerminalInstanceInfoSchema, {
21050
21258
  kind: "mutation",
21051
21259
  auth: "admin"
@@ -21067,7 +21275,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
21067
21275
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
21068
21276
  profileId: string(),
21069
21277
  cols: number().int().positive(),
21070
- rows: number().int().positive()
21278
+ rows: number().int().positive(),
21279
+ executable: string().max(1024).optional(),
21280
+ args: array(string().max(2048)).max(64).optional(),
21281
+ cwd: string().max(1024).optional(),
21282
+ environment: array(string().max(4096)).max(64).optional()
21071
21283
  }), TerminalSessionInfoSchema, {
21072
21284
  kind: "mutation",
21073
21285
  auth: "admin"
@@ -24950,10 +25162,10 @@ var lawnMowerControlCapability = {
24950
25162
  *
24951
25163
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
24952
25164
  * to receive an ordered list of candidate base URLs it should race
24953
- * on connect — LAN IPv4 first (lowest latency when on same network),
24954
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
24955
- * race them with short timeouts and stick with the winner for the
24956
- * session.
25165
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
25166
+ * when on the same network), then public hostname (if a tunnel is
25167
+ * up). The SDK can race them with short timeouts and stick with the
25168
+ * winner for the session.
24957
25169
  *
24958
25170
  * Why hub-only: agents are not directly addressable by the operator's
24959
25171
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -25108,6 +25320,17 @@ var NotificationEndpointSchema = object({
25108
25320
  /** What the ranking currently resolves to (null when nothing is reachable). */
25109
25321
  resolved: string().nullable()
25110
25322
  });
25323
+ /**
25324
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
25325
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
25326
+ * currently expands to, so the UI can show the effective set either way.
25327
+ */
25328
+ var ViewerEndpointsSchema = object({
25329
+ /** The operator's explicit race set, or empty for AUTO. */
25330
+ baseUrls: array(string()).readonly(),
25331
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
25332
+ resolved: array(string()).readonly()
25333
+ });
25111
25334
  var AllowedAddressesSchema = object({
25112
25335
  /**
25113
25336
  * Allowlist of interface addresses operators have explicitly opted
@@ -25116,6 +25339,20 @@ var AllowedAddressesSchema = object({
25116
25339
  * Network Addresses admin page and persisted by the addon.
25117
25340
  */
25118
25341
  addresses: array(string()).readonly() });
25342
+ var TlsStatusSchema = object({
25343
+ mode: _enum([
25344
+ "generated",
25345
+ "uploaded",
25346
+ "disabled"
25347
+ ]),
25348
+ leafFingerprintSha256: string().nullable(),
25349
+ caFingerprintSha256: string().nullable(),
25350
+ validTo: string().nullable(),
25351
+ sans: array(string()),
25352
+ caCertPem: string().nullable(),
25353
+ reissueError: string().nullable(),
25354
+ restartRequired: boolean()
25355
+ });
25119
25356
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
25120
25357
  /**
25121
25358
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -25125,17 +25362,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
25125
25362
  */
25126
25363
  port: number().int().min(1).max(65535).optional(),
25127
25364
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
25128
- * candidate. Default `true`. */
25365
+ * candidate. Default `false` — loopback is not a client route. */
25129
25366
  includeLoopback: boolean().optional(),
25130
- /** Skip IPv6 entries. Some legacy clients can't parse them.
25131
- * Default `false`. */
25367
+ /** Skip IPv6 entries. Default `false` the palette includes stable
25368
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
25369
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
25132
25370
  ipv4Only: boolean().optional(),
25133
25371
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
25134
25372
  * Pass `'https'` when the caller is itself loaded over HTTPS
25135
25373
  * to avoid mixed-content blocks in the browser. The public
25136
25374
  * tunnel always emits `https://` regardless. */
25137
25375
  scheme: _enum(["http", "https"]).optional()
25138
- }), 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" });
25376
+ }), 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, {
25377
+ kind: "mutation",
25378
+ auth: "admin"
25379
+ }), method(object({
25380
+ certPem: string().min(1),
25381
+ keyPem: string().min(1),
25382
+ caPem: string().optional()
25383
+ }), TlsStatusSchema, {
25384
+ kind: "mutation",
25385
+ auth: "admin"
25386
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
25387
+ kind: "mutation",
25388
+ auth: "admin"
25389
+ });
25139
25390
  var LockControlStatusSchema = object({
25140
25391
  /** Lifecycle state of the lock. `jammed` means the motor reported
25141
25392
  * failure to reach the target — operator intervention required. */
@@ -26696,7 +26947,12 @@ var PlateInfoSchema = object({
26696
26947
  plateBbox: BoundingBoxSchema.optional(),
26697
26948
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
26698
26949
  keyFrameMediaKey: string().optional(),
26699
- base64: string().optional()
26950
+ base64: string().optional(),
26951
+ /**
26952
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
26953
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
26954
+ */
26955
+ cropUrl: string().optional()
26700
26956
  });
26701
26957
  var MediaFileLiteSchema = object({
26702
26958
  key: string(),
@@ -32220,6 +32476,12 @@ Object.freeze({
32220
32476
  addonId: null,
32221
32477
  access: "view"
32222
32478
  },
32479
+ "deviceManager.getBindingsBatch": {
32480
+ capName: "device-manager",
32481
+ capScope: "system",
32482
+ addonId: null,
32483
+ access: "view"
32484
+ },
32223
32485
  "deviceManager.getChildren": {
32224
32486
  capName: "device-manager",
32225
32487
  capScope: "system",
@@ -32280,6 +32542,12 @@ Object.freeze({
32280
32542
  addonId: null,
32281
32543
  access: "view"
32282
32544
  },
32545
+ "deviceManager.getLinkedDevicesBatch": {
32546
+ capName: "device-manager",
32547
+ capScope: "system",
32548
+ addonId: null,
32549
+ access: "view"
32550
+ },
32283
32551
  "deviceManager.getRoleDisplayDefaults": {
32284
32552
  capName: "device-manager",
32285
32553
  capScope: "system",
@@ -33162,6 +33430,12 @@ Object.freeze({
33162
33430
  addonId: null,
33163
33431
  access: "create"
33164
33432
  },
33433
+ "localNetwork.downloadCa": {
33434
+ capName: "local-network",
33435
+ capScope: "system",
33436
+ addonId: null,
33437
+ access: "view"
33438
+ },
33165
33439
  "localNetwork.getAllowedAddresses": {
33166
33440
  capName: "local-network",
33167
33441
  capScope: "system",
@@ -33186,18 +33460,42 @@ Object.freeze({
33186
33460
  addonId: null,
33187
33461
  access: "view"
33188
33462
  },
33463
+ "localNetwork.getTlsStatus": {
33464
+ capName: "local-network",
33465
+ capScope: "system",
33466
+ addonId: null,
33467
+ access: "view"
33468
+ },
33469
+ "localNetwork.getViewerEndpoints": {
33470
+ capName: "local-network",
33471
+ capScope: "system",
33472
+ addonId: null,
33473
+ access: "view"
33474
+ },
33189
33475
  "localNetwork.list": {
33190
33476
  capName: "local-network",
33191
33477
  capScope: "system",
33192
33478
  addonId: null,
33193
33479
  access: "view"
33194
33480
  },
33481
+ "localNetwork.regenerateCertificate": {
33482
+ capName: "local-network",
33483
+ capScope: "system",
33484
+ addonId: null,
33485
+ access: "create"
33486
+ },
33195
33487
  "localNetwork.resetAllowlistToBestMatch": {
33196
33488
  capName: "local-network",
33197
33489
  capScope: "system",
33198
33490
  addonId: null,
33199
33491
  access: "delete"
33200
33492
  },
33493
+ "localNetwork.revertToGeneratedCertificate": {
33494
+ capName: "local-network",
33495
+ capScope: "system",
33496
+ addonId: null,
33497
+ access: "create"
33498
+ },
33201
33499
  "localNetwork.setAllowedAddresses": {
33202
33500
  capName: "local-network",
33203
33501
  capScope: "system",
@@ -33210,6 +33508,18 @@ Object.freeze({
33210
33508
  addonId: null,
33211
33509
  access: "create"
33212
33510
  },
33511
+ "localNetwork.setViewerEndpoints": {
33512
+ capName: "local-network",
33513
+ capScope: "system",
33514
+ addonId: null,
33515
+ access: "create"
33516
+ },
33517
+ "localNetwork.uploadCertificate": {
33518
+ capName: "local-network",
33519
+ capScope: "system",
33520
+ addonId: null,
33521
+ access: "create"
33522
+ },
33213
33523
  "lockControl.lock": {
33214
33524
  capName: "lock-control",
33215
33525
  capScope: "device",
@@ -34008,6 +34318,12 @@ Object.freeze({
34008
34318
  addonId: null,
34009
34319
  access: "view"
34010
34320
  },
34321
+ "pipelineAnalytics.getGroup": {
34322
+ capName: "pipeline-analytics",
34323
+ capScope: "device",
34324
+ addonId: null,
34325
+ access: "view"
34326
+ },
34011
34327
  "pipelineAnalytics.getKeyEvents": {
34012
34328
  capName: "pipeline-analytics",
34013
34329
  capScope: "device",
@@ -34092,6 +34408,12 @@ Object.freeze({
34092
34408
  addonId: null,
34093
34409
  access: "view"
34094
34410
  },
34411
+ "pipelineAnalytics.listGroups": {
34412
+ capName: "pipeline-analytics",
34413
+ capScope: "device",
34414
+ addonId: null,
34415
+ access: "view"
34416
+ },
34095
34417
  "pipelineAnalytics.listOpsLog": {
34096
34418
  capName: "pipeline-analytics",
34097
34419
  capScope: "device",
@@ -36090,6 +36412,12 @@ Object.freeze({
36090
36412
  addonId: null,
36091
36413
  access: "create"
36092
36414
  },
36415
+ "terminalSession.updateInstance": {
36416
+ capName: "terminal-session",
36417
+ capScope: "system",
36418
+ addonId: null,
36419
+ access: "create"
36420
+ },
36093
36421
  "terminalSession.writeInput": {
36094
36422
  capName: "terminal-session",
36095
36423
  capScope: "system",
@@ -36867,6 +37195,11 @@ Object.freeze({
36867
37195
  form: "single",
36868
37196
  optional: false
36869
37197
  }],
37198
+ "deviceManager.getBindingsBatch": [{
37199
+ name: "deviceIds",
37200
+ form: "array",
37201
+ optional: false
37202
+ }],
36870
37203
  "deviceManager.getChildren": [{
36871
37204
  name: "parentDeviceId",
36872
37205
  form: "single",
@@ -36912,6 +37245,11 @@ Object.freeze({
36912
37245
  form: "single",
36913
37246
  optional: false
36914
37247
  }],
37248
+ "deviceManager.getLinkedDevicesBatch": [{
37249
+ name: "deviceIds",
37250
+ form: "array",
37251
+ optional: false
37252
+ }],
36915
37253
  "deviceManager.getSettingsSchema": [{
36916
37254
  name: "deviceId",
36917
37255
  form: "single",
@@ -36932,6 +37270,11 @@ Object.freeze({
36932
37270
  form: "single",
36933
37271
  optional: false
36934
37272
  }],
37273
+ "deviceManager.listAll": [{
37274
+ name: "deviceIds",
37275
+ form: "array",
37276
+ optional: true
37277
+ }],
36935
37278
  "deviceManager.loadConfig": [{
36936
37279
  name: "deviceId",
36937
37280
  form: "single",
@@ -37505,6 +37848,11 @@ Object.freeze({
37505
37848
  form: "single",
37506
37849
  optional: false
37507
37850
  }],
37851
+ "pipelineAnalytics.getGroup": [{
37852
+ name: "deviceId",
37853
+ form: "single",
37854
+ optional: false
37855
+ }],
37508
37856
  "pipelineAnalytics.getKeyEvents": [{
37509
37857
  name: "deviceId",
37510
37858
  form: "single",
@@ -37560,6 +37908,11 @@ Object.freeze({
37560
37908
  form: "array",
37561
37909
  optional: false
37562
37910
  }],
37911
+ "pipelineAnalytics.listGroups": [{
37912
+ name: "deviceIds",
37913
+ form: "array",
37914
+ optional: false
37915
+ }],
37563
37916
  "pipelineAnalytics.listOpsLog": [{
37564
37917
  name: "deviceId",
37565
37918
  form: "single",
@@ -38577,6 +38930,35 @@ Object.freeze(Object.fromEntries([{
38577
38930
  }]
38578
38931
  }].map((s) => [s.stepId, s.defaultModelId])));
38579
38932
  string().min(1);
38933
+ var CLUSTER_STEP_SETTING_FIELDS = [{
38934
+ stepId: "face-embedding",
38935
+ key: "minLandmarkFaceSize",
38936
+ label: "Min face size for recognition (detection px)",
38937
+ 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.",
38938
+ type: "slider",
38939
+ min: 0,
38940
+ max: 64,
38941
+ step: 2,
38942
+ default: 24
38943
+ }];
38944
+ function clusterStepSettingKey(stepId, fieldKey) {
38945
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
38946
+ }
38947
+ var ClusterSettingNumberSchema = number().finite();
38948
+ function readClusterStepSettings(config) {
38949
+ const out = {};
38950
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
38951
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
38952
+ const value = parsed.success ? parsed.data : field.default;
38953
+ const existing = out[field.stepId] ?? {};
38954
+ out[field.stepId] = {
38955
+ ...existing,
38956
+ [field.key]: value
38957
+ };
38958
+ }
38959
+ return out;
38960
+ }
38961
+ readClusterStepSettings({});
38580
38962
  object({
38581
38963
  /**
38582
38964
  * Fraction of the box's own size added on EACH side before cutting.