@camstack/addon-import-alexa 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
@@ -5840,6 +5840,13 @@ var BaseAddon = class {
5840
5840
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5841
5841
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5842
5842
  _registeredCapNames = [];
5843
+ /**
5844
+ * True only after `readAddonStore` actually answered. Constructor
5845
+ * defaults look like stored config when the store is down — a forked
5846
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5847
+ * mode, 2026-08-25) is not "the operator chose this".
5848
+ */
5849
+ settingsStoreReady = false;
5843
5850
  /** Default config values. Provided via constructor. */
5844
5851
  defaults;
5845
5852
  constructor(defaults) {
@@ -6240,7 +6247,9 @@ var BaseAddon = class {
6240
6247
  ];
6241
6248
  let lastErr;
6242
6249
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6243
- return await settings.readAddonStore() ?? {};
6250
+ const stored = await settings.readAddonStore() ?? {};
6251
+ this.settingsStoreReady = true;
6252
+ return stored;
6244
6253
  } catch (err) {
6245
6254
  lastErr = err;
6246
6255
  const msg = err instanceof Error ? err.message : String(err);
@@ -6248,6 +6257,7 @@ var BaseAddon = class {
6248
6257
  if (attempt === delaysMs.length) break;
6249
6258
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6250
6259
  }
6260
+ this.settingsStoreReady = false;
6251
6261
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6252
6262
  return {};
6253
6263
  }
@@ -8178,6 +8188,15 @@ var LabelDefinitionSchema = object({
8178
8188
  description: string().optional(),
8179
8189
  icon: string().optional()
8180
8190
  });
8191
+ var ClassMapDefinitionSchema = object({
8192
+ mapping: record(string(), _enum([
8193
+ "person",
8194
+ "vehicle",
8195
+ "animal",
8196
+ "package"
8197
+ ])),
8198
+ preserveOriginal: boolean()
8199
+ });
8181
8200
  var MODEL_FORMATS = [
8182
8201
  "onnx",
8183
8202
  "coreml",
@@ -8261,6 +8280,12 @@ var ModelVariantGroupSchema = object({
8261
8280
  */
8262
8281
  resolution: number().int().positive().optional()
8263
8282
  });
8283
+ var ModelProviderIdSchema = _enum([
8284
+ "camstack",
8285
+ "frigate",
8286
+ "scrypted",
8287
+ "custom"
8288
+ ]);
8264
8289
  var ModelCatalogEntrySchema = object({
8265
8290
  id: string(),
8266
8291
  name: string(),
@@ -8356,7 +8381,19 @@ var ModelCatalogEntrySchema = object({
8356
8381
  * `id` stays the source of truth for resolution/download/persistence; grouping
8357
8382
  * is a presentation overlay resolved back to an `id`.
8358
8383
  */
8359
- group: ModelVariantGroupSchema.optional()
8384
+ group: ModelVariantGroupSchema.optional(),
8385
+ /**
8386
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8387
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8388
+ * persisted before this field existed (`inferModelProvider` fills those).
8389
+ */
8390
+ provider: ModelProviderIdSchema.optional(),
8391
+ /**
8392
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8393
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8394
+ * labels already ARE the CamStack macros (Scrypted identity map).
8395
+ */
8396
+ classMap: ClassMapDefinitionSchema.optional()
8360
8397
  });
8361
8398
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8362
8399
  format: literal("openvino"),
@@ -8385,7 +8422,8 @@ var ModelConvertMetadataSchema = object({
8385
8422
  "ocr",
8386
8423
  "segmentation"
8387
8424
  ]),
8388
- faceAlignment: boolean().optional()
8425
+ faceAlignment: boolean().optional(),
8426
+ classMap: ClassMapDefinitionSchema.optional()
8389
8427
  });
8390
8428
  var ConvertResultSchema = object({
8391
8429
  entry: ModelCatalogEntrySchema,
@@ -12227,6 +12265,27 @@ var LinkedDeviceSchema = object({
12227
12265
  features: array(string()),
12228
12266
  producesTrackedEvents: boolean().optional()
12229
12267
  });
12268
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
12269
+ * The batch answer needs the tag; the single-device answer already has it
12270
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
12271
+ var LinkedDevicesForDeviceSchema = object({
12272
+ deviceId: number(),
12273
+ mode: LinkedDevicesModeSchema,
12274
+ devices: array(LinkedDeviceSchema)
12275
+ });
12276
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
12277
+ * `getAllBindings` all answer in. Declared once: three copies of the same
12278
+ * object literal is exactly how the three drift apart. */
12279
+ var DeviceBindingsForDeviceSchema = object({
12280
+ deviceId: number(),
12281
+ entries: array(object({
12282
+ capName: string(),
12283
+ kind: _enum(["native", "wrapped"]),
12284
+ providerAddonId: string(),
12285
+ providerNodeId: string(),
12286
+ nativeAddonId: string()
12287
+ }))
12288
+ });
12230
12289
  var SavedDeviceRowSchema = object({
12231
12290
  /** Numeric id reserved at allocateDeviceId time. */
12232
12291
  id: number(),
@@ -12452,11 +12511,25 @@ method(object({
12452
12511
  projection: _enum(["full", "slim"]).optional(),
12453
12512
  /** Return only camera devices. Filtering server-side instead of
12454
12513
  * shipping 293 rows to find 12. */
12455
- isCamera: boolean().optional()
12514
+ isCamera: boolean().optional(),
12515
+ /**
12516
+ * Return only these device ids. For the caller that already KNOWS the
12517
+ * handful it wants and needs a field the id-bearing answer does not
12518
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12519
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12520
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12521
+ * refetches on the reconcile interval, on a phone.
12522
+ *
12523
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12524
+ * keys rather than rejecting them (verified against the live hub
12525
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12526
+ * it answers today and the caller filters as it already does.
12527
+ */
12528
+ deviceIds: array(number()).optional()
12456
12529
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12457
12530
  mode: LinkedDevicesModeSchema,
12458
12531
  devices: array(LinkedDeviceSchema)
12459
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12532
+ })), 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({
12460
12533
  deviceId: number(),
12461
12534
  values: record(string(), unknown())
12462
12535
  }), object({ success: literal(true) }), {
@@ -12483,25 +12556,7 @@ method(object({
12483
12556
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
12484
12557
  kind: "mutation",
12485
12558
  auth: "admin"
12486
- }), method(object({ deviceId: number() }), object({
12487
- deviceId: number(),
12488
- entries: array(object({
12489
- capName: string(),
12490
- kind: _enum(["native", "wrapped"]),
12491
- providerAddonId: string(),
12492
- providerNodeId: string(),
12493
- nativeAddonId: string()
12494
- }))
12495
- })), method(object({}), array(object({
12496
- deviceId: number(),
12497
- entries: array(object({
12498
- capName: string(),
12499
- kind: _enum(["native", "wrapped"]),
12500
- providerAddonId: string(),
12501
- providerNodeId: string(),
12502
- nativeAddonId: string()
12503
- }))
12504
- }))), method(object({
12559
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12505
12560
  deviceId: number(),
12506
12561
  capName: string(),
12507
12562
  wrapperAddonId: string(),
@@ -14911,12 +14966,15 @@ var NcOccupancyConditionSchema = object({
14911
14966
  * there is no second switch that can disagree with the first and every rule
14912
14967
  * authored before the decision migrates for free (`audioModeOf`):
14913
14968
  *
14914
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14915
- * classifier labels with one of them. No window, no percentage:
14916
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14917
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14918
- * the analyzer's (`classificationMinScore`, per device) a label only
14919
- * reaches this condition if the classifier was already confident enough.
14969
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14970
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14971
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14972
+ * frames is the wrong question for a classifier that labels 1–3 frames
14973
+ * per episode. The count window is the brake that drops a single-frame
14974
+ * false positive; the rule's own `throttle` cooldown is the other. The
14975
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14976
+ * per device) — a label only reaches this condition if the classifier was
14977
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14920
14978
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14921
14979
  * the condition: at least `hitPercent`% of the samples over
14922
14980
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14943,14 +15001,22 @@ var NcOccupancyConditionSchema = object({
14943
15001
  * an operator who typed `dog` mean the same thing.
14944
15002
  */
14945
15003
  var NcAudioConditionSchema = object({
14946
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
15004
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14947
15005
  labels: array(string().min(1)).min(1).optional(),
14948
15006
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14949
15007
  dbThreshold: number().min(-96).max(0).optional(),
14950
15008
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14951
15009
  hitPercent: number().int().min(1).max(100).default(60),
14952
15010
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14953
- samplingSeconds: number().int().min(1).max(300).default(10)
15011
+ samplingSeconds: number().int().min(1).max(300).default(10),
15012
+ /**
15013
+ * LABEL MODE: how many labelled frames must land inside
15014
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
15015
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
15016
+ */
15017
+ confirmHits: number().int().min(1).max(20).optional(),
15018
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
15019
+ confirmWindowSec: number().int().min(1).max(60).optional()
14954
15020
  });
14955
15021
  /**
14956
15022
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -17324,6 +17390,46 @@ var RecentTracksPageSchema = object({
17324
17390
  /** Cursor for the next page, or null when this page is the last. */
17325
17391
  nextCursor: string().nullable()
17326
17392
  });
17393
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
17394
+ var LIST_GROUPS_MAX_LIMIT = 100;
17395
+ var AnalyticsGroupRecordSchema = object({
17396
+ id: string(),
17397
+ deviceId: number().int(),
17398
+ openedAt: number().int(),
17399
+ closedAt: number().int(),
17400
+ timestamp: number().int(),
17401
+ memberCount: number().int(),
17402
+ memberTrackIds: array(string()).readonly(),
17403
+ className: string(),
17404
+ classes: array(string()).readonly(),
17405
+ /** Relative event-media path, or null when the group has no picture yet. */
17406
+ mediaUrl: string().nullable(),
17407
+ singleton: boolean()
17408
+ });
17409
+ var AnalyticsGroupMemberSchema = object({
17410
+ trackId: string(),
17411
+ deviceId: number().int(),
17412
+ className: string(),
17413
+ firstSeen: number().int(),
17414
+ lastSeen: number().int(),
17415
+ mediaUrl: string().nullable()
17416
+ });
17417
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
17418
+ var ListGroupsQueryInput = object({
17419
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
17420
+ deviceIds: array(number()),
17421
+ /** Window lower bound on `closedAt` (inclusive). */
17422
+ since: number().optional(),
17423
+ /** Window upper bound on `openedAt` (inclusive). */
17424
+ until: number().optional(),
17425
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
17426
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
17427
+ cursor: string().optional()
17428
+ });
17429
+ var ListGroupsPageSchema = object({
17430
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
17431
+ nextCursor: string().nullable()
17432
+ });
17327
17433
  var KeyEventQueryInput = object({
17328
17434
  deviceId: number(),
17329
17435
  /** Window lower bound (track firstSeen ≥ since). */
@@ -17399,7 +17505,9 @@ var TrackCascadeCountsSchema = object({
17399
17505
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
17400
17506
  plates: number().int(),
17401
17507
  /** Per-track CLIP search vectors removed (best-effort). */
17402
- embeddings: number().int()
17508
+ embeddings: number().int(),
17509
+ /** Group membership + group rows removed with their last member (best-effort). */
17510
+ groups: number().int()
17403
17511
  });
17404
17512
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
17405
17513
  var DiskReconcileCountsSchema = object({
@@ -17545,7 +17653,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17545
17653
  * stationary registry). Default false: the timeline lists passages,
17546
17654
  * not parking records (operator decision, 2026-08-15). */
17547
17655
  includeStationary: boolean().optional()
17548
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17656
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17657
+ deviceId: number(),
17658
+ groupId: string().min(1)
17659
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17549
17660
  kind: "mutation",
17550
17661
  auth: "admin"
17551
17662
  }), 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({
@@ -17763,6 +17874,33 @@ var NativeCropRefSchema = object({
17763
17874
  h: number()
17764
17875
  })
17765
17876
  });
17877
+ object({
17878
+ crop: object({
17879
+ left: number(),
17880
+ top: number(),
17881
+ width: number().positive(),
17882
+ height: number().positive()
17883
+ }).optional(),
17884
+ content: object({
17885
+ width: number().int().positive(),
17886
+ height: number().int().positive()
17887
+ }),
17888
+ fit: _enum(["stretch", "contain"]),
17889
+ format: _enum([
17890
+ "rgb",
17891
+ "gray",
17892
+ "jpeg"
17893
+ ])
17894
+ });
17895
+ var FrameRefSchema = object({
17896
+ registryId: string().min(1),
17897
+ id: string().min(1),
17898
+ width: number().int().positive(),
17899
+ height: number().int().positive(),
17900
+ format: _enum(["rgb", "gray"]),
17901
+ timestamp: number(),
17902
+ capturedAt: number().optional()
17903
+ });
17766
17904
  var ModelFormatSchema$1 = _enum([
17767
17905
  "onnx",
17768
17906
  "coreml",
@@ -17828,7 +17966,8 @@ var PipelineModelOptionSchema = object({
17828
17966
  sizeMB: number()
17829
17967
  })),
17830
17968
  group: ModelVariantGroupSchema.optional(),
17831
- legacy: boolean().optional()
17969
+ legacy: boolean().optional(),
17970
+ provider: ModelProviderIdSchema.optional()
17832
17971
  });
17833
17972
  var ConfigFieldBridge = custom();
17834
17973
  var PipelineAddonSchemaSchema = object({
@@ -18007,6 +18146,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
18007
18146
  steps: array(PipelineStepInputSchema).min(1),
18008
18147
  frame: FrameInputSchema.optional(),
18009
18148
  /**
18149
+ * Process-local lazy frame. Valid only when caller and provider resolve
18150
+ * in the same execution-group process; split/cross-node callers use
18151
+ * `frame`/`image` inline compatibility instead.
18152
+ */
18153
+ frameRef: FrameRefSchema.optional(),
18154
+ /**
18010
18155
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
18011
18156
  * the decoded pixels live in. One more member of the one-of
18012
18157
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -18302,7 +18447,10 @@ var NativeCropResultSchema = object({
18302
18447
  * Which source served this crop, so a quality-sensitive consumer (the native
18303
18448
  * `keyFrame`) can reject a degraded fallback:
18304
18449
  * - `native` — cut from the decode worker's retained NATIVE surface (the
18305
- * quality path).
18450
+ * quality path). A subject-tile serve is also native-resolution and stays
18451
+ * `native` here: the public enum cannot name `tile` without a breaking cap
18452
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
18453
+ * internal crop result (`nativeHits` vs `tileHits`).
18306
18454
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
18307
18455
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
18308
18456
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -18793,12 +18941,41 @@ var RunnerLocalLoadSchema = object({
18793
18941
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
18794
18942
  * working unchanged when they switch to reading from the runner cap.
18795
18943
  */
18944
+ var FrameLazyCountersSchema = object({
18945
+ framesDecoded: number(),
18946
+ framesAdmitted: number(),
18947
+ framesDroppedPixelFree: number(),
18948
+ viewsMaterialized: number(),
18949
+ viewsSkipped: number(),
18950
+ workerToRunnerBytes: number(),
18951
+ runnerToPoolRawBytes: number(),
18952
+ runnerToPoolJpegBytes: number(),
18953
+ onDemandFullFrameRequests: number(),
18954
+ onDemandCropRequests: number(),
18955
+ nativeHits: number(),
18956
+ nativeMisses: number(),
18957
+ tileHits: number(),
18958
+ tileMisses: number(),
18959
+ fallbackHits: number(),
18960
+ fallbackMisses: number(),
18961
+ retainedWritesAvoided: number(),
18962
+ residentRefs: number(),
18963
+ residentBytes: number(),
18964
+ releases: number(),
18965
+ evictions: number(),
18966
+ staleMisses: number()
18967
+ });
18968
+ var FrameLazyMetricsSchema = object({
18969
+ node: FrameLazyCountersSchema,
18970
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
18971
+ });
18796
18972
  var RunnerLocalMetricsSchema = object({
18797
18973
  nodeId: string(),
18798
18974
  activeCameras: number(),
18799
18975
  throttledCameras: number(),
18800
18976
  avgInferenceTimeMs: number(),
18801
- queueDepth: number()
18977
+ queueDepth: number(),
18978
+ frameLazy: FrameLazyMetricsSchema.optional()
18802
18979
  });
18803
18980
  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({
18804
18981
  handle: FrameHandleSchema,
@@ -20098,6 +20275,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
20098
20275
  location: StorageLocationSchema,
20099
20276
  relativePath: string()
20100
20277
  }), _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" });
20278
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
20279
+ var ProfileSettingsSchemaBridge = unknown().nullable();
20280
+ var ProfileSettingsBagSchema = record(string(), unknown());
20101
20281
  /**
20102
20282
  * A live terminal session hosted by the provider addon. Output and input do
20103
20283
  * NOT flow through the capability — they use the addon data plane
@@ -20127,7 +20307,14 @@ var TerminalSessionInfoSchema = object({
20127
20307
  var TerminalProfileInfoSchema = object({
20128
20308
  profileId: string(),
20129
20309
  label: string(),
20130
- description: string().optional()
20310
+ description: string().optional(),
20311
+ /** Spawn defaults the instance form copies on create. */
20312
+ executable: string().optional(),
20313
+ args: array(string()).readonly().optional(),
20314
+ cwd: string().optional(),
20315
+ environment: array(string()).readonly().optional(),
20316
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
20317
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
20131
20318
  });
20132
20319
  /**
20133
20320
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -20140,7 +20327,12 @@ var TerminalInstanceInfoSchema = object({
20140
20327
  profileId: string(),
20141
20328
  profileLabel: string(),
20142
20329
  name: string(),
20143
- enabled: boolean()
20330
+ enabled: boolean(),
20331
+ executable: string(),
20332
+ args: array(string()).readonly(),
20333
+ cwd: string(),
20334
+ environment: array(string()).readonly(),
20335
+ profileSettings: ProfileSettingsBagSchema
20144
20336
  });
20145
20337
  var TerminalLegacyCameraSchema = object({
20146
20338
  stableId: string(),
@@ -20170,7 +20362,23 @@ var TerminalOutputBatchSchema = object({
20170
20362
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
20171
20363
  targetNodeId: string().min(1),
20172
20364
  profileId: string().min(1),
20173
- name: string().trim().min(1).max(160).optional()
20365
+ name: string().trim().min(1).max(160).optional(),
20366
+ executable: string().max(1024).optional(),
20367
+ args: array(string().max(2048)).max(64).optional(),
20368
+ cwd: string().max(1024).optional(),
20369
+ environment: array(string().max(4096)).max(64).optional(),
20370
+ profileSettings: ProfileSettingsBagSchema.optional()
20371
+ }), TerminalInstanceInfoSchema, {
20372
+ kind: "mutation",
20373
+ auth: "admin"
20374
+ }), method(object({
20375
+ instanceId: string().min(1),
20376
+ name: string().trim().min(1).max(160).optional(),
20377
+ executable: string().max(1024).optional(),
20378
+ args: array(string().max(2048)).max(64).optional(),
20379
+ cwd: string().max(1024).optional(),
20380
+ environment: array(string().max(4096)).max(64).optional(),
20381
+ profileSettings: ProfileSettingsBagSchema.optional()
20174
20382
  }), TerminalInstanceInfoSchema, {
20175
20383
  kind: "mutation",
20176
20384
  auth: "admin"
@@ -20192,7 +20400,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
20192
20400
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
20193
20401
  profileId: string(),
20194
20402
  cols: number().int().positive(),
20195
- rows: number().int().positive()
20403
+ rows: number().int().positive(),
20404
+ executable: string().max(1024).optional(),
20405
+ args: array(string().max(2048)).max(64).optional(),
20406
+ cwd: string().max(1024).optional(),
20407
+ environment: array(string().max(4096)).max(64).optional()
20196
20408
  }), TerminalSessionInfoSchema, {
20197
20409
  kind: "mutation",
20198
20410
  auth: "admin"
@@ -24084,10 +24296,10 @@ var lawnMowerControlCapability = {
24084
24296
  *
24085
24297
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
24086
24298
  * to receive an ordered list of candidate base URLs it should race
24087
- * on connect — LAN IPv4 first (lowest latency when on same network),
24088
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
24089
- * race them with short timeouts and stick with the winner for the
24090
- * session.
24299
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
24300
+ * when on the same network), then public hostname (if a tunnel is
24301
+ * up). The SDK can race them with short timeouts and stick with the
24302
+ * winner for the session.
24091
24303
  *
24092
24304
  * Why hub-only: agents are not directly addressable by the operator's
24093
24305
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -24242,6 +24454,17 @@ var NotificationEndpointSchema = object({
24242
24454
  /** What the ranking currently resolves to (null when nothing is reachable). */
24243
24455
  resolved: string().nullable()
24244
24456
  });
24457
+ /**
24458
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
24459
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
24460
+ * currently expands to, so the UI can show the effective set either way.
24461
+ */
24462
+ var ViewerEndpointsSchema = object({
24463
+ /** The operator's explicit race set, or empty for AUTO. */
24464
+ baseUrls: array(string()).readonly(),
24465
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
24466
+ resolved: array(string()).readonly()
24467
+ });
24245
24468
  var AllowedAddressesSchema = object({
24246
24469
  /**
24247
24470
  * Allowlist of interface addresses operators have explicitly opted
@@ -24250,6 +24473,20 @@ var AllowedAddressesSchema = object({
24250
24473
  * Network Addresses admin page and persisted by the addon.
24251
24474
  */
24252
24475
  addresses: array(string()).readonly() });
24476
+ var TlsStatusSchema = object({
24477
+ mode: _enum([
24478
+ "generated",
24479
+ "uploaded",
24480
+ "disabled"
24481
+ ]),
24482
+ leafFingerprintSha256: string().nullable(),
24483
+ caFingerprintSha256: string().nullable(),
24484
+ validTo: string().nullable(),
24485
+ sans: array(string()),
24486
+ caCertPem: string().nullable(),
24487
+ reissueError: string().nullable(),
24488
+ restartRequired: boolean()
24489
+ });
24253
24490
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
24254
24491
  /**
24255
24492
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -24259,17 +24496,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
24259
24496
  */
24260
24497
  port: number().int().min(1).max(65535).optional(),
24261
24498
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
24262
- * candidate. Default `true`. */
24499
+ * candidate. Default `false` — loopback is not a client route. */
24263
24500
  includeLoopback: boolean().optional(),
24264
- /** Skip IPv6 entries. Some legacy clients can't parse them.
24265
- * Default `false`. */
24501
+ /** Skip IPv6 entries. Default `false` the palette includes stable
24502
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
24503
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
24266
24504
  ipv4Only: boolean().optional(),
24267
24505
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
24268
24506
  * Pass `'https'` when the caller is itself loaded over HTTPS
24269
24507
  * to avoid mixed-content blocks in the browser. The public
24270
24508
  * tunnel always emits `https://` regardless. */
24271
24509
  scheme: _enum(["http", "https"]).optional()
24272
- }), 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" });
24510
+ }), 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, {
24511
+ kind: "mutation",
24512
+ auth: "admin"
24513
+ }), method(object({
24514
+ certPem: string().min(1),
24515
+ keyPem: string().min(1),
24516
+ caPem: string().optional()
24517
+ }), TlsStatusSchema, {
24518
+ kind: "mutation",
24519
+ auth: "admin"
24520
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
24521
+ kind: "mutation",
24522
+ auth: "admin"
24523
+ });
24273
24524
  var LockControlStatusSchema = object({
24274
24525
  /** Lifecycle state of the lock. `jammed` means the motor reported
24275
24526
  * failure to reach the target — operator intervention required. */
@@ -25830,7 +26081,12 @@ var PlateInfoSchema = object({
25830
26081
  plateBbox: BoundingBoxSchema.optional(),
25831
26082
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
25832
26083
  keyFrameMediaKey: string().optional(),
25833
- base64: string().optional()
26084
+ base64: string().optional(),
26085
+ /**
26086
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
26087
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
26088
+ */
26089
+ cropUrl: string().optional()
25834
26090
  });
25835
26091
  var MediaFileLiteSchema = object({
25836
26092
  key: string(),
@@ -31354,6 +31610,12 @@ Object.freeze({
31354
31610
  addonId: null,
31355
31611
  access: "view"
31356
31612
  },
31613
+ "deviceManager.getBindingsBatch": {
31614
+ capName: "device-manager",
31615
+ capScope: "system",
31616
+ addonId: null,
31617
+ access: "view"
31618
+ },
31357
31619
  "deviceManager.getChildren": {
31358
31620
  capName: "device-manager",
31359
31621
  capScope: "system",
@@ -31414,6 +31676,12 @@ Object.freeze({
31414
31676
  addonId: null,
31415
31677
  access: "view"
31416
31678
  },
31679
+ "deviceManager.getLinkedDevicesBatch": {
31680
+ capName: "device-manager",
31681
+ capScope: "system",
31682
+ addonId: null,
31683
+ access: "view"
31684
+ },
31417
31685
  "deviceManager.getRoleDisplayDefaults": {
31418
31686
  capName: "device-manager",
31419
31687
  capScope: "system",
@@ -32296,6 +32564,12 @@ Object.freeze({
32296
32564
  addonId: null,
32297
32565
  access: "create"
32298
32566
  },
32567
+ "localNetwork.downloadCa": {
32568
+ capName: "local-network",
32569
+ capScope: "system",
32570
+ addonId: null,
32571
+ access: "view"
32572
+ },
32299
32573
  "localNetwork.getAllowedAddresses": {
32300
32574
  capName: "local-network",
32301
32575
  capScope: "system",
@@ -32320,18 +32594,42 @@ Object.freeze({
32320
32594
  addonId: null,
32321
32595
  access: "view"
32322
32596
  },
32597
+ "localNetwork.getTlsStatus": {
32598
+ capName: "local-network",
32599
+ capScope: "system",
32600
+ addonId: null,
32601
+ access: "view"
32602
+ },
32603
+ "localNetwork.getViewerEndpoints": {
32604
+ capName: "local-network",
32605
+ capScope: "system",
32606
+ addonId: null,
32607
+ access: "view"
32608
+ },
32323
32609
  "localNetwork.list": {
32324
32610
  capName: "local-network",
32325
32611
  capScope: "system",
32326
32612
  addonId: null,
32327
32613
  access: "view"
32328
32614
  },
32615
+ "localNetwork.regenerateCertificate": {
32616
+ capName: "local-network",
32617
+ capScope: "system",
32618
+ addonId: null,
32619
+ access: "create"
32620
+ },
32329
32621
  "localNetwork.resetAllowlistToBestMatch": {
32330
32622
  capName: "local-network",
32331
32623
  capScope: "system",
32332
32624
  addonId: null,
32333
32625
  access: "delete"
32334
32626
  },
32627
+ "localNetwork.revertToGeneratedCertificate": {
32628
+ capName: "local-network",
32629
+ capScope: "system",
32630
+ addonId: null,
32631
+ access: "create"
32632
+ },
32335
32633
  "localNetwork.setAllowedAddresses": {
32336
32634
  capName: "local-network",
32337
32635
  capScope: "system",
@@ -32344,6 +32642,18 @@ Object.freeze({
32344
32642
  addonId: null,
32345
32643
  access: "create"
32346
32644
  },
32645
+ "localNetwork.setViewerEndpoints": {
32646
+ capName: "local-network",
32647
+ capScope: "system",
32648
+ addonId: null,
32649
+ access: "create"
32650
+ },
32651
+ "localNetwork.uploadCertificate": {
32652
+ capName: "local-network",
32653
+ capScope: "system",
32654
+ addonId: null,
32655
+ access: "create"
32656
+ },
32347
32657
  "lockControl.lock": {
32348
32658
  capName: "lock-control",
32349
32659
  capScope: "device",
@@ -33142,6 +33452,12 @@ Object.freeze({
33142
33452
  addonId: null,
33143
33453
  access: "view"
33144
33454
  },
33455
+ "pipelineAnalytics.getGroup": {
33456
+ capName: "pipeline-analytics",
33457
+ capScope: "device",
33458
+ addonId: null,
33459
+ access: "view"
33460
+ },
33145
33461
  "pipelineAnalytics.getKeyEvents": {
33146
33462
  capName: "pipeline-analytics",
33147
33463
  capScope: "device",
@@ -33226,6 +33542,12 @@ Object.freeze({
33226
33542
  addonId: null,
33227
33543
  access: "view"
33228
33544
  },
33545
+ "pipelineAnalytics.listGroups": {
33546
+ capName: "pipeline-analytics",
33547
+ capScope: "device",
33548
+ addonId: null,
33549
+ access: "view"
33550
+ },
33229
33551
  "pipelineAnalytics.listOpsLog": {
33230
33552
  capName: "pipeline-analytics",
33231
33553
  capScope: "device",
@@ -35224,6 +35546,12 @@ Object.freeze({
35224
35546
  addonId: null,
35225
35547
  access: "create"
35226
35548
  },
35549
+ "terminalSession.updateInstance": {
35550
+ capName: "terminal-session",
35551
+ capScope: "system",
35552
+ addonId: null,
35553
+ access: "create"
35554
+ },
35227
35555
  "terminalSession.writeInput": {
35228
35556
  capName: "terminal-session",
35229
35557
  capScope: "system",
@@ -36001,6 +36329,11 @@ Object.freeze({
36001
36329
  form: "single",
36002
36330
  optional: false
36003
36331
  }],
36332
+ "deviceManager.getBindingsBatch": [{
36333
+ name: "deviceIds",
36334
+ form: "array",
36335
+ optional: false
36336
+ }],
36004
36337
  "deviceManager.getChildren": [{
36005
36338
  name: "parentDeviceId",
36006
36339
  form: "single",
@@ -36046,6 +36379,11 @@ Object.freeze({
36046
36379
  form: "single",
36047
36380
  optional: false
36048
36381
  }],
36382
+ "deviceManager.getLinkedDevicesBatch": [{
36383
+ name: "deviceIds",
36384
+ form: "array",
36385
+ optional: false
36386
+ }],
36049
36387
  "deviceManager.getSettingsSchema": [{
36050
36388
  name: "deviceId",
36051
36389
  form: "single",
@@ -36066,6 +36404,11 @@ Object.freeze({
36066
36404
  form: "single",
36067
36405
  optional: false
36068
36406
  }],
36407
+ "deviceManager.listAll": [{
36408
+ name: "deviceIds",
36409
+ form: "array",
36410
+ optional: true
36411
+ }],
36069
36412
  "deviceManager.loadConfig": [{
36070
36413
  name: "deviceId",
36071
36414
  form: "single",
@@ -36639,6 +36982,11 @@ Object.freeze({
36639
36982
  form: "single",
36640
36983
  optional: false
36641
36984
  }],
36985
+ "pipelineAnalytics.getGroup": [{
36986
+ name: "deviceId",
36987
+ form: "single",
36988
+ optional: false
36989
+ }],
36642
36990
  "pipelineAnalytics.getKeyEvents": [{
36643
36991
  name: "deviceId",
36644
36992
  form: "single",
@@ -36694,6 +37042,11 @@ Object.freeze({
36694
37042
  form: "array",
36695
37043
  optional: false
36696
37044
  }],
37045
+ "pipelineAnalytics.listGroups": [{
37046
+ name: "deviceIds",
37047
+ form: "array",
37048
+ optional: false
37049
+ }],
36697
37050
  "pipelineAnalytics.listOpsLog": [{
36698
37051
  name: "deviceId",
36699
37052
  form: "single",
@@ -37711,6 +38064,35 @@ Object.freeze(Object.fromEntries([{
37711
38064
  }]
37712
38065
  }].map((s) => [s.stepId, s.defaultModelId])));
37713
38066
  string().min(1);
38067
+ var CLUSTER_STEP_SETTING_FIELDS = [{
38068
+ stepId: "face-embedding",
38069
+ key: "minLandmarkFaceSize",
38070
+ label: "Min face size for recognition (detection px)",
38071
+ 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.",
38072
+ type: "slider",
38073
+ min: 0,
38074
+ max: 64,
38075
+ step: 2,
38076
+ default: 24
38077
+ }];
38078
+ function clusterStepSettingKey(stepId, fieldKey) {
38079
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
38080
+ }
38081
+ var ClusterSettingNumberSchema = number().finite();
38082
+ function readClusterStepSettings(config) {
38083
+ const out = {};
38084
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
38085
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
38086
+ const value = parsed.success ? parsed.data : field.default;
38087
+ const existing = out[field.stepId] ?? {};
38088
+ out[field.stepId] = {
38089
+ ...existing,
38090
+ [field.key]: value
38091
+ };
38092
+ }
38093
+ return out;
38094
+ }
38095
+ readClusterStepSettings({});
37714
38096
  object({
37715
38097
  /**
37716
38098
  * Fraction of the box's own size added on EACH side before cutting.