@camstack/addon-remote-storage 1.2.25 → 1.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.
@@ -5825,6 +5825,13 @@ var BaseAddon = class {
5825
5825
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5826
5826
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5827
5827
  _registeredCapNames = [];
5828
+ /**
5829
+ * True only after `readAddonStore` actually answered. Constructor
5830
+ * defaults look like stored config when the store is down — a forked
5831
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5832
+ * mode, 2026-08-25) is not "the operator chose this".
5833
+ */
5834
+ settingsStoreReady = false;
5828
5835
  /** Default config values. Provided via constructor. */
5829
5836
  defaults;
5830
5837
  constructor(defaults) {
@@ -6225,7 +6232,9 @@ var BaseAddon = class {
6225
6232
  ];
6226
6233
  let lastErr;
6227
6234
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6228
- return await settings.readAddonStore() ?? {};
6235
+ const stored = await settings.readAddonStore() ?? {};
6236
+ this.settingsStoreReady = true;
6237
+ return stored;
6229
6238
  } catch (err) {
6230
6239
  lastErr = err;
6231
6240
  const msg = err instanceof Error ? err.message : String(err);
@@ -6233,6 +6242,7 @@ var BaseAddon = class {
6233
6242
  if (attempt === delaysMs.length) break;
6234
6243
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6235
6244
  }
6245
+ this.settingsStoreReady = false;
6236
6246
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6237
6247
  return {};
6238
6248
  }
@@ -8028,6 +8038,15 @@ var LabelDefinitionSchema = object({
8028
8038
  description: string().optional(),
8029
8039
  icon: string().optional()
8030
8040
  });
8041
+ var ClassMapDefinitionSchema = object({
8042
+ mapping: record(string(), _enum([
8043
+ "person",
8044
+ "vehicle",
8045
+ "animal",
8046
+ "package"
8047
+ ])),
8048
+ preserveOriginal: boolean()
8049
+ });
8031
8050
  var MODEL_FORMATS = [
8032
8051
  "onnx",
8033
8052
  "coreml",
@@ -8111,6 +8130,12 @@ var ModelVariantGroupSchema = object({
8111
8130
  */
8112
8131
  resolution: number().int().positive().optional()
8113
8132
  });
8133
+ var ModelProviderIdSchema = _enum([
8134
+ "camstack",
8135
+ "frigate",
8136
+ "scrypted",
8137
+ "custom"
8138
+ ]);
8114
8139
  var ModelCatalogEntrySchema = object({
8115
8140
  id: string(),
8116
8141
  name: string(),
@@ -8206,7 +8231,19 @@ var ModelCatalogEntrySchema = object({
8206
8231
  * `id` stays the source of truth for resolution/download/persistence; grouping
8207
8232
  * is a presentation overlay resolved back to an `id`.
8208
8233
  */
8209
- group: ModelVariantGroupSchema.optional()
8234
+ group: ModelVariantGroupSchema.optional(),
8235
+ /**
8236
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8237
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8238
+ * persisted before this field existed (`inferModelProvider` fills those).
8239
+ */
8240
+ provider: ModelProviderIdSchema.optional(),
8241
+ /**
8242
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8243
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8244
+ * labels already ARE the CamStack macros (Scrypted identity map).
8245
+ */
8246
+ classMap: ClassMapDefinitionSchema.optional()
8210
8247
  });
8211
8248
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8212
8249
  format: literal("openvino"),
@@ -8235,7 +8272,8 @@ var ModelConvertMetadataSchema = object({
8235
8272
  "ocr",
8236
8273
  "segmentation"
8237
8274
  ]),
8238
- faceAlignment: boolean().optional()
8275
+ faceAlignment: boolean().optional(),
8276
+ classMap: ClassMapDefinitionSchema.optional()
8239
8277
  });
8240
8278
  var ConvertResultSchema = object({
8241
8279
  entry: ModelCatalogEntrySchema,
@@ -11728,6 +11766,27 @@ var LinkedDeviceSchema = object({
11728
11766
  features: array(string()),
11729
11767
  producesTrackedEvents: boolean().optional()
11730
11768
  });
11769
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
11770
+ * The batch answer needs the tag; the single-device answer already has it
11771
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
11772
+ var LinkedDevicesForDeviceSchema = object({
11773
+ deviceId: number(),
11774
+ mode: LinkedDevicesModeSchema,
11775
+ devices: array(LinkedDeviceSchema)
11776
+ });
11777
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
11778
+ * `getAllBindings` all answer in. Declared once: three copies of the same
11779
+ * object literal is exactly how the three drift apart. */
11780
+ var DeviceBindingsForDeviceSchema = object({
11781
+ deviceId: number(),
11782
+ entries: array(object({
11783
+ capName: string(),
11784
+ kind: _enum(["native", "wrapped"]),
11785
+ providerAddonId: string(),
11786
+ providerNodeId: string(),
11787
+ nativeAddonId: string()
11788
+ }))
11789
+ });
11731
11790
  var SavedDeviceRowSchema = object({
11732
11791
  /** Numeric id reserved at allocateDeviceId time. */
11733
11792
  id: number(),
@@ -11953,11 +12012,25 @@ method(object({
11953
12012
  projection: _enum(["full", "slim"]).optional(),
11954
12013
  /** Return only camera devices. Filtering server-side instead of
11955
12014
  * shipping 293 rows to find 12. */
11956
- isCamera: boolean().optional()
12015
+ isCamera: boolean().optional(),
12016
+ /**
12017
+ * Return only these device ids. For the caller that already KNOWS the
12018
+ * handful it wants and needs a field the id-bearing answer does not
12019
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12020
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12021
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12022
+ * refetches on the reconcile interval, on a phone.
12023
+ *
12024
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12025
+ * keys rather than rejecting them (verified against the live hub
12026
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12027
+ * it answers today and the caller filters as it already does.
12028
+ */
12029
+ deviceIds: array(number()).optional()
11957
12030
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
11958
12031
  mode: LinkedDevicesModeSchema,
11959
12032
  devices: array(LinkedDeviceSchema)
11960
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12033
+ })), 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({
11961
12034
  deviceId: number(),
11962
12035
  values: record(string(), unknown())
11963
12036
  }), object({ success: literal(true) }), {
@@ -11984,25 +12057,7 @@ method(object({
11984
12057
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
11985
12058
  kind: "mutation",
11986
12059
  auth: "admin"
11987
- }), method(object({ deviceId: number() }), object({
11988
- deviceId: number(),
11989
- entries: array(object({
11990
- capName: string(),
11991
- kind: _enum(["native", "wrapped"]),
11992
- providerAddonId: string(),
11993
- providerNodeId: string(),
11994
- nativeAddonId: string()
11995
- }))
11996
- })), method(object({}), array(object({
11997
- deviceId: number(),
11998
- entries: array(object({
11999
- capName: string(),
12000
- kind: _enum(["native", "wrapped"]),
12001
- providerAddonId: string(),
12002
- providerNodeId: string(),
12003
- nativeAddonId: string()
12004
- }))
12005
- }))), method(object({
12060
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12006
12061
  deviceId: number(),
12007
12062
  capName: string(),
12008
12063
  wrapperAddonId: string(),
@@ -14374,12 +14429,15 @@ var NcOccupancyConditionSchema = object({
14374
14429
  * there is no second switch that can disagree with the first and every rule
14375
14430
  * authored before the decision migrates for free (`audioModeOf`):
14376
14431
  *
14377
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14378
- * classifier labels with one of them. No window, no percentage:
14379
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14380
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14381
- * the analyzer's (`classificationMinScore`, per device) a label only
14382
- * reaches this condition if the classifier was already confident enough.
14432
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14433
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14434
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14435
+ * frames is the wrong question for a classifier that labels 1–3 frames
14436
+ * per episode. The count window is the brake that drops a single-frame
14437
+ * false positive; the rule's own `throttle` cooldown is the other. The
14438
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14439
+ * per device) — a label only reaches this condition if the classifier was
14440
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14383
14441
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14384
14442
  * the condition: at least `hitPercent`% of the samples over
14385
14443
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14406,14 +14464,22 @@ var NcOccupancyConditionSchema = object({
14406
14464
  * an operator who typed `dog` mean the same thing.
14407
14465
  */
14408
14466
  var NcAudioConditionSchema = object({
14409
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14467
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14410
14468
  labels: array(string().min(1)).min(1).optional(),
14411
14469
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14412
14470
  dbThreshold: number().min(-96).max(0).optional(),
14413
14471
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14414
14472
  hitPercent: number().int().min(1).max(100).default(60),
14415
14473
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14416
- samplingSeconds: number().int().min(1).max(300).default(10)
14474
+ samplingSeconds: number().int().min(1).max(300).default(10),
14475
+ /**
14476
+ * LABEL MODE: how many labelled frames must land inside
14477
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14478
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14479
+ */
14480
+ confirmHits: number().int().min(1).max(20).optional(),
14481
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14482
+ confirmWindowSec: number().int().min(1).max(60).optional()
14417
14483
  });
14418
14484
  /**
14419
14485
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -16787,6 +16853,46 @@ var RecentTracksPageSchema = object({
16787
16853
  /** Cursor for the next page, or null when this page is the last. */
16788
16854
  nextCursor: string().nullable()
16789
16855
  });
16856
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
16857
+ var LIST_GROUPS_MAX_LIMIT = 100;
16858
+ var AnalyticsGroupRecordSchema = object({
16859
+ id: string(),
16860
+ deviceId: number().int(),
16861
+ openedAt: number().int(),
16862
+ closedAt: number().int(),
16863
+ timestamp: number().int(),
16864
+ memberCount: number().int(),
16865
+ memberTrackIds: array(string()).readonly(),
16866
+ className: string(),
16867
+ classes: array(string()).readonly(),
16868
+ /** Relative event-media path, or null when the group has no picture yet. */
16869
+ mediaUrl: string().nullable(),
16870
+ singleton: boolean()
16871
+ });
16872
+ var AnalyticsGroupMemberSchema = object({
16873
+ trackId: string(),
16874
+ deviceId: number().int(),
16875
+ className: string(),
16876
+ firstSeen: number().int(),
16877
+ lastSeen: number().int(),
16878
+ mediaUrl: string().nullable()
16879
+ });
16880
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
16881
+ var ListGroupsQueryInput = object({
16882
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
16883
+ deviceIds: array(number()),
16884
+ /** Window lower bound on `closedAt` (inclusive). */
16885
+ since: number().optional(),
16886
+ /** Window upper bound on `openedAt` (inclusive). */
16887
+ until: number().optional(),
16888
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
16889
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
16890
+ cursor: string().optional()
16891
+ });
16892
+ var ListGroupsPageSchema = object({
16893
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
16894
+ nextCursor: string().nullable()
16895
+ });
16790
16896
  var KeyEventQueryInput = object({
16791
16897
  deviceId: number(),
16792
16898
  /** Window lower bound (track firstSeen ≥ since). */
@@ -16862,7 +16968,9 @@ var TrackCascadeCountsSchema = object({
16862
16968
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
16863
16969
  plates: number().int(),
16864
16970
  /** Per-track CLIP search vectors removed (best-effort). */
16865
- embeddings: number().int()
16971
+ embeddings: number().int(),
16972
+ /** Group membership + group rows removed with their last member (best-effort). */
16973
+ groups: number().int()
16866
16974
  });
16867
16975
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
16868
16976
  var DiskReconcileCountsSchema = object({
@@ -17008,7 +17116,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17008
17116
  * stationary registry). Default false: the timeline lists passages,
17009
17117
  * not parking records (operator decision, 2026-08-15). */
17010
17118
  includeStationary: boolean().optional()
17011
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17119
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17120
+ deviceId: number(),
17121
+ groupId: string().min(1)
17122
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17012
17123
  kind: "mutation",
17013
17124
  auth: "admin"
17014
17125
  }), 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({
@@ -17226,6 +17337,33 @@ var NativeCropRefSchema = object({
17226
17337
  h: number()
17227
17338
  })
17228
17339
  });
17340
+ object({
17341
+ crop: object({
17342
+ left: number(),
17343
+ top: number(),
17344
+ width: number().positive(),
17345
+ height: number().positive()
17346
+ }).optional(),
17347
+ content: object({
17348
+ width: number().int().positive(),
17349
+ height: number().int().positive()
17350
+ }),
17351
+ fit: _enum(["stretch", "contain"]),
17352
+ format: _enum([
17353
+ "rgb",
17354
+ "gray",
17355
+ "jpeg"
17356
+ ])
17357
+ });
17358
+ var FrameRefSchema = object({
17359
+ registryId: string().min(1),
17360
+ id: string().min(1),
17361
+ width: number().int().positive(),
17362
+ height: number().int().positive(),
17363
+ format: _enum(["rgb", "gray"]),
17364
+ timestamp: number(),
17365
+ capturedAt: number().optional()
17366
+ });
17229
17367
  var ModelFormatSchema$1 = _enum([
17230
17368
  "onnx",
17231
17369
  "coreml",
@@ -17291,7 +17429,8 @@ var PipelineModelOptionSchema = object({
17291
17429
  sizeMB: number()
17292
17430
  })),
17293
17431
  group: ModelVariantGroupSchema.optional(),
17294
- legacy: boolean().optional()
17432
+ legacy: boolean().optional(),
17433
+ provider: ModelProviderIdSchema.optional()
17295
17434
  });
17296
17435
  var ConfigFieldBridge = custom();
17297
17436
  var PipelineAddonSchemaSchema = object({
@@ -17470,6 +17609,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
17470
17609
  steps: array(PipelineStepInputSchema).min(1),
17471
17610
  frame: FrameInputSchema.optional(),
17472
17611
  /**
17612
+ * Process-local lazy frame. Valid only when caller and provider resolve
17613
+ * in the same execution-group process; split/cross-node callers use
17614
+ * `frame`/`image` inline compatibility instead.
17615
+ */
17616
+ frameRef: FrameRefSchema.optional(),
17617
+ /**
17473
17618
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
17474
17619
  * the decoded pixels live in. One more member of the one-of
17475
17620
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -17725,7 +17870,10 @@ var NativeCropResultSchema = object({
17725
17870
  * Which source served this crop, so a quality-sensitive consumer (the native
17726
17871
  * `keyFrame`) can reject a degraded fallback:
17727
17872
  * - `native` — cut from the decode worker's retained NATIVE surface (the
17728
- * quality path).
17873
+ * quality path). A subject-tile serve is also native-resolution and stays
17874
+ * `native` here: the public enum cannot name `tile` without a breaking cap
17875
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
17876
+ * internal crop result (`nativeHits` vs `tileHits`).
17729
17877
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
17730
17878
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
17731
17879
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -18216,12 +18364,41 @@ var RunnerLocalLoadSchema = object({
18216
18364
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
18217
18365
  * working unchanged when they switch to reading from the runner cap.
18218
18366
  */
18367
+ var FrameLazyCountersSchema = object({
18368
+ framesDecoded: number(),
18369
+ framesAdmitted: number(),
18370
+ framesDroppedPixelFree: number(),
18371
+ viewsMaterialized: number(),
18372
+ viewsSkipped: number(),
18373
+ workerToRunnerBytes: number(),
18374
+ runnerToPoolRawBytes: number(),
18375
+ runnerToPoolJpegBytes: number(),
18376
+ onDemandFullFrameRequests: number(),
18377
+ onDemandCropRequests: number(),
18378
+ nativeHits: number(),
18379
+ nativeMisses: number(),
18380
+ tileHits: number(),
18381
+ tileMisses: number(),
18382
+ fallbackHits: number(),
18383
+ fallbackMisses: number(),
18384
+ retainedWritesAvoided: number(),
18385
+ residentRefs: number(),
18386
+ residentBytes: number(),
18387
+ releases: number(),
18388
+ evictions: number(),
18389
+ staleMisses: number()
18390
+ });
18391
+ var FrameLazyMetricsSchema = object({
18392
+ node: FrameLazyCountersSchema,
18393
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
18394
+ });
18219
18395
  var RunnerLocalMetricsSchema = object({
18220
18396
  nodeId: string(),
18221
18397
  activeCameras: number(),
18222
18398
  throttledCameras: number(),
18223
18399
  avgInferenceTimeMs: number(),
18224
- queueDepth: number()
18400
+ queueDepth: number(),
18401
+ frameLazy: FrameLazyMetricsSchema.optional()
18225
18402
  });
18226
18403
  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({
18227
18404
  handle: FrameHandleSchema,
@@ -19566,6 +19743,9 @@ var storageProviderCapability = {
19566
19743
  endDownload: method(EndDownloadInputSchema, _void(), { kind: "mutation" })
19567
19744
  }
19568
19745
  };
19746
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
19747
+ var ProfileSettingsSchemaBridge = unknown().nullable();
19748
+ var ProfileSettingsBagSchema = record(string(), unknown());
19569
19749
  /**
19570
19750
  * A live terminal session hosted by the provider addon. Output and input do
19571
19751
  * NOT flow through the capability — they use the addon data plane
@@ -19595,7 +19775,14 @@ var TerminalSessionInfoSchema = object({
19595
19775
  var TerminalProfileInfoSchema = object({
19596
19776
  profileId: string(),
19597
19777
  label: string(),
19598
- description: string().optional()
19778
+ description: string().optional(),
19779
+ /** Spawn defaults the instance form copies on create. */
19780
+ executable: string().optional(),
19781
+ args: array(string()).readonly().optional(),
19782
+ cwd: string().optional(),
19783
+ environment: array(string()).readonly().optional(),
19784
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
19785
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
19599
19786
  });
19600
19787
  /**
19601
19788
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -19608,7 +19795,12 @@ var TerminalInstanceInfoSchema = object({
19608
19795
  profileId: string(),
19609
19796
  profileLabel: string(),
19610
19797
  name: string(),
19611
- enabled: boolean()
19798
+ enabled: boolean(),
19799
+ executable: string(),
19800
+ args: array(string()).readonly(),
19801
+ cwd: string(),
19802
+ environment: array(string()).readonly(),
19803
+ profileSettings: ProfileSettingsBagSchema
19612
19804
  });
19613
19805
  var TerminalLegacyCameraSchema = object({
19614
19806
  stableId: string(),
@@ -19638,7 +19830,23 @@ var TerminalOutputBatchSchema = object({
19638
19830
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
19639
19831
  targetNodeId: string().min(1),
19640
19832
  profileId: string().min(1),
19641
- name: string().trim().min(1).max(160).optional()
19833
+ name: string().trim().min(1).max(160).optional(),
19834
+ executable: string().max(1024).optional(),
19835
+ args: array(string().max(2048)).max(64).optional(),
19836
+ cwd: string().max(1024).optional(),
19837
+ environment: array(string().max(4096)).max(64).optional(),
19838
+ profileSettings: ProfileSettingsBagSchema.optional()
19839
+ }), TerminalInstanceInfoSchema, {
19840
+ kind: "mutation",
19841
+ auth: "admin"
19842
+ }), method(object({
19843
+ instanceId: string().min(1),
19844
+ name: string().trim().min(1).max(160).optional(),
19845
+ executable: string().max(1024).optional(),
19846
+ args: array(string().max(2048)).max(64).optional(),
19847
+ cwd: string().max(1024).optional(),
19848
+ environment: array(string().max(4096)).max(64).optional(),
19849
+ profileSettings: ProfileSettingsBagSchema.optional()
19642
19850
  }), TerminalInstanceInfoSchema, {
19643
19851
  kind: "mutation",
19644
19852
  auth: "admin"
@@ -19660,7 +19868,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
19660
19868
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
19661
19869
  profileId: string(),
19662
19870
  cols: number().int().positive(),
19663
- rows: number().int().positive()
19871
+ rows: number().int().positive(),
19872
+ executable: string().max(1024).optional(),
19873
+ args: array(string().max(2048)).max(64).optional(),
19874
+ cwd: string().max(1024).optional(),
19875
+ environment: array(string().max(4096)).max(64).optional()
19664
19876
  }), TerminalSessionInfoSchema, {
19665
19877
  kind: "mutation",
19666
19878
  auth: "admin"
@@ -22442,10 +22654,10 @@ DeviceType.LawnMower, method(object({ deviceId: number().int().nonnegative() }),
22442
22654
  *
22443
22655
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
22444
22656
  * to receive an ordered list of candidate base URLs it should race
22445
- * on connect — LAN IPv4 first (lowest latency when on same network),
22446
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
22447
- * race them with short timeouts and stick with the winner for the
22448
- * session.
22657
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
22658
+ * when on the same network), then public hostname (if a tunnel is
22659
+ * up). The SDK can race them with short timeouts and stick with the
22660
+ * winner for the session.
22449
22661
  *
22450
22662
  * Why hub-only: agents are not directly addressable by the operator's
22451
22663
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -22600,6 +22812,17 @@ var NotificationEndpointSchema = object({
22600
22812
  /** What the ranking currently resolves to (null when nothing is reachable). */
22601
22813
  resolved: string().nullable()
22602
22814
  });
22815
+ /**
22816
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
22817
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
22818
+ * currently expands to, so the UI can show the effective set either way.
22819
+ */
22820
+ var ViewerEndpointsSchema = object({
22821
+ /** The operator's explicit race set, or empty for AUTO. */
22822
+ baseUrls: array(string()).readonly(),
22823
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
22824
+ resolved: array(string()).readonly()
22825
+ });
22603
22826
  var AllowedAddressesSchema = object({
22604
22827
  /**
22605
22828
  * Allowlist of interface addresses operators have explicitly opted
@@ -22608,6 +22831,20 @@ var AllowedAddressesSchema = object({
22608
22831
  * Network Addresses admin page and persisted by the addon.
22609
22832
  */
22610
22833
  addresses: array(string()).readonly() });
22834
+ var TlsStatusSchema = object({
22835
+ mode: _enum([
22836
+ "generated",
22837
+ "uploaded",
22838
+ "disabled"
22839
+ ]),
22840
+ leafFingerprintSha256: string().nullable(),
22841
+ caFingerprintSha256: string().nullable(),
22842
+ validTo: string().nullable(),
22843
+ sans: array(string()),
22844
+ caCertPem: string().nullable(),
22845
+ reissueError: string().nullable(),
22846
+ restartRequired: boolean()
22847
+ });
22611
22848
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
22612
22849
  /**
22613
22850
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -22617,17 +22854,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
22617
22854
  */
22618
22855
  port: number().int().min(1).max(65535).optional(),
22619
22856
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
22620
- * candidate. Default `true`. */
22857
+ * candidate. Default `false` — loopback is not a client route. */
22621
22858
  includeLoopback: boolean().optional(),
22622
- /** Skip IPv6 entries. Some legacy clients can't parse them.
22623
- * Default `false`. */
22859
+ /** Skip IPv6 entries. Default `false` the palette includes stable
22860
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
22861
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
22624
22862
  ipv4Only: boolean().optional(),
22625
22863
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
22626
22864
  * Pass `'https'` when the caller is itself loaded over HTTPS
22627
22865
  * to avoid mixed-content blocks in the browser. The public
22628
22866
  * tunnel always emits `https://` regardless. */
22629
22867
  scheme: _enum(["http", "https"]).optional()
22630
- }), 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" });
22868
+ }), 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, {
22869
+ kind: "mutation",
22870
+ auth: "admin"
22871
+ }), method(object({
22872
+ certPem: string().min(1),
22873
+ keyPem: string().min(1),
22874
+ caPem: string().optional()
22875
+ }), TlsStatusSchema, {
22876
+ kind: "mutation",
22877
+ auth: "admin"
22878
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
22879
+ kind: "mutation",
22880
+ auth: "admin"
22881
+ });
22631
22882
  object({
22632
22883
  /** Lifecycle state of the lock. `jammed` means the motor reported
22633
22884
  * failure to reach the target — operator intervention required. */
@@ -23808,7 +24059,12 @@ var PlateInfoSchema = object({
23808
24059
  plateBbox: BoundingBoxSchema.optional(),
23809
24060
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
23810
24061
  keyFrameMediaKey: string().optional(),
23811
- base64: string().optional()
24062
+ base64: string().optional(),
24063
+ /**
24064
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
24065
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
24066
+ */
24067
+ cropUrl: string().optional()
23812
24068
  });
23813
24069
  var MediaFileLiteSchema = object({
23814
24070
  key: string(),
@@ -27448,6 +27704,12 @@ Object.freeze({
27448
27704
  addonId: null,
27449
27705
  access: "view"
27450
27706
  },
27707
+ "deviceManager.getBindingsBatch": {
27708
+ capName: "device-manager",
27709
+ capScope: "system",
27710
+ addonId: null,
27711
+ access: "view"
27712
+ },
27451
27713
  "deviceManager.getChildren": {
27452
27714
  capName: "device-manager",
27453
27715
  capScope: "system",
@@ -27508,6 +27770,12 @@ Object.freeze({
27508
27770
  addonId: null,
27509
27771
  access: "view"
27510
27772
  },
27773
+ "deviceManager.getLinkedDevicesBatch": {
27774
+ capName: "device-manager",
27775
+ capScope: "system",
27776
+ addonId: null,
27777
+ access: "view"
27778
+ },
27511
27779
  "deviceManager.getRoleDisplayDefaults": {
27512
27780
  capName: "device-manager",
27513
27781
  capScope: "system",
@@ -28390,6 +28658,12 @@ Object.freeze({
28390
28658
  addonId: null,
28391
28659
  access: "create"
28392
28660
  },
28661
+ "localNetwork.downloadCa": {
28662
+ capName: "local-network",
28663
+ capScope: "system",
28664
+ addonId: null,
28665
+ access: "view"
28666
+ },
28393
28667
  "localNetwork.getAllowedAddresses": {
28394
28668
  capName: "local-network",
28395
28669
  capScope: "system",
@@ -28414,18 +28688,42 @@ Object.freeze({
28414
28688
  addonId: null,
28415
28689
  access: "view"
28416
28690
  },
28691
+ "localNetwork.getTlsStatus": {
28692
+ capName: "local-network",
28693
+ capScope: "system",
28694
+ addonId: null,
28695
+ access: "view"
28696
+ },
28697
+ "localNetwork.getViewerEndpoints": {
28698
+ capName: "local-network",
28699
+ capScope: "system",
28700
+ addonId: null,
28701
+ access: "view"
28702
+ },
28417
28703
  "localNetwork.list": {
28418
28704
  capName: "local-network",
28419
28705
  capScope: "system",
28420
28706
  addonId: null,
28421
28707
  access: "view"
28422
28708
  },
28709
+ "localNetwork.regenerateCertificate": {
28710
+ capName: "local-network",
28711
+ capScope: "system",
28712
+ addonId: null,
28713
+ access: "create"
28714
+ },
28423
28715
  "localNetwork.resetAllowlistToBestMatch": {
28424
28716
  capName: "local-network",
28425
28717
  capScope: "system",
28426
28718
  addonId: null,
28427
28719
  access: "delete"
28428
28720
  },
28721
+ "localNetwork.revertToGeneratedCertificate": {
28722
+ capName: "local-network",
28723
+ capScope: "system",
28724
+ addonId: null,
28725
+ access: "create"
28726
+ },
28429
28727
  "localNetwork.setAllowedAddresses": {
28430
28728
  capName: "local-network",
28431
28729
  capScope: "system",
@@ -28438,6 +28736,18 @@ Object.freeze({
28438
28736
  addonId: null,
28439
28737
  access: "create"
28440
28738
  },
28739
+ "localNetwork.setViewerEndpoints": {
28740
+ capName: "local-network",
28741
+ capScope: "system",
28742
+ addonId: null,
28743
+ access: "create"
28744
+ },
28745
+ "localNetwork.uploadCertificate": {
28746
+ capName: "local-network",
28747
+ capScope: "system",
28748
+ addonId: null,
28749
+ access: "create"
28750
+ },
28441
28751
  "lockControl.lock": {
28442
28752
  capName: "lock-control",
28443
28753
  capScope: "device",
@@ -29236,6 +29546,12 @@ Object.freeze({
29236
29546
  addonId: null,
29237
29547
  access: "view"
29238
29548
  },
29549
+ "pipelineAnalytics.getGroup": {
29550
+ capName: "pipeline-analytics",
29551
+ capScope: "device",
29552
+ addonId: null,
29553
+ access: "view"
29554
+ },
29239
29555
  "pipelineAnalytics.getKeyEvents": {
29240
29556
  capName: "pipeline-analytics",
29241
29557
  capScope: "device",
@@ -29320,6 +29636,12 @@ Object.freeze({
29320
29636
  addonId: null,
29321
29637
  access: "view"
29322
29638
  },
29639
+ "pipelineAnalytics.listGroups": {
29640
+ capName: "pipeline-analytics",
29641
+ capScope: "device",
29642
+ addonId: null,
29643
+ access: "view"
29644
+ },
29323
29645
  "pipelineAnalytics.listOpsLog": {
29324
29646
  capName: "pipeline-analytics",
29325
29647
  capScope: "device",
@@ -31318,6 +31640,12 @@ Object.freeze({
31318
31640
  addonId: null,
31319
31641
  access: "create"
31320
31642
  },
31643
+ "terminalSession.updateInstance": {
31644
+ capName: "terminal-session",
31645
+ capScope: "system",
31646
+ addonId: null,
31647
+ access: "create"
31648
+ },
31321
31649
  "terminalSession.writeInput": {
31322
31650
  capName: "terminal-session",
31323
31651
  capScope: "system",
@@ -32095,6 +32423,11 @@ Object.freeze({
32095
32423
  form: "single",
32096
32424
  optional: false
32097
32425
  }],
32426
+ "deviceManager.getBindingsBatch": [{
32427
+ name: "deviceIds",
32428
+ form: "array",
32429
+ optional: false
32430
+ }],
32098
32431
  "deviceManager.getChildren": [{
32099
32432
  name: "parentDeviceId",
32100
32433
  form: "single",
@@ -32140,6 +32473,11 @@ Object.freeze({
32140
32473
  form: "single",
32141
32474
  optional: false
32142
32475
  }],
32476
+ "deviceManager.getLinkedDevicesBatch": [{
32477
+ name: "deviceIds",
32478
+ form: "array",
32479
+ optional: false
32480
+ }],
32143
32481
  "deviceManager.getSettingsSchema": [{
32144
32482
  name: "deviceId",
32145
32483
  form: "single",
@@ -32160,6 +32498,11 @@ Object.freeze({
32160
32498
  form: "single",
32161
32499
  optional: false
32162
32500
  }],
32501
+ "deviceManager.listAll": [{
32502
+ name: "deviceIds",
32503
+ form: "array",
32504
+ optional: true
32505
+ }],
32163
32506
  "deviceManager.loadConfig": [{
32164
32507
  name: "deviceId",
32165
32508
  form: "single",
@@ -32733,6 +33076,11 @@ Object.freeze({
32733
33076
  form: "single",
32734
33077
  optional: false
32735
33078
  }],
33079
+ "pipelineAnalytics.getGroup": [{
33080
+ name: "deviceId",
33081
+ form: "single",
33082
+ optional: false
33083
+ }],
32736
33084
  "pipelineAnalytics.getKeyEvents": [{
32737
33085
  name: "deviceId",
32738
33086
  form: "single",
@@ -32788,6 +33136,11 @@ Object.freeze({
32788
33136
  form: "array",
32789
33137
  optional: false
32790
33138
  }],
33139
+ "pipelineAnalytics.listGroups": [{
33140
+ name: "deviceIds",
33141
+ form: "array",
33142
+ optional: false
33143
+ }],
32791
33144
  "pipelineAnalytics.listOpsLog": [{
32792
33145
  name: "deviceId",
32793
33146
  form: "single",
@@ -33805,6 +34158,35 @@ Object.freeze(Object.fromEntries([{
33805
34158
  }]
33806
34159
  }].map((s) => [s.stepId, s.defaultModelId])));
33807
34160
  string().min(1);
34161
+ var CLUSTER_STEP_SETTING_FIELDS = [{
34162
+ stepId: "face-embedding",
34163
+ key: "minLandmarkFaceSize",
34164
+ label: "Min face size for recognition (detection px)",
34165
+ 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.",
34166
+ type: "slider",
34167
+ min: 0,
34168
+ max: 64,
34169
+ step: 2,
34170
+ default: 24
34171
+ }];
34172
+ function clusterStepSettingKey(stepId, fieldKey) {
34173
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
34174
+ }
34175
+ var ClusterSettingNumberSchema = number().finite();
34176
+ function readClusterStepSettings(config) {
34177
+ const out = {};
34178
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
34179
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
34180
+ const value = parsed.success ? parsed.data : field.default;
34181
+ const existing = out[field.stepId] ?? {};
34182
+ out[field.stepId] = {
34183
+ ...existing,
34184
+ [field.key]: value
34185
+ };
34186
+ }
34187
+ return out;
34188
+ }
34189
+ readClusterStepSettings({});
33808
34190
  object({
33809
34191
  /**
33810
34192
  * Fraction of the box's own size added on EACH side before cutting.