@camstack/addon-auth 1.2.29 → 1.2.31

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.
@@ -5800,6 +5800,13 @@ var BaseAddon = class {
5800
5800
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5801
5801
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5802
5802
  _registeredCapNames = [];
5803
+ /**
5804
+ * True only after `readAddonStore` actually answered. Constructor
5805
+ * defaults look like stored config when the store is down — a forked
5806
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5807
+ * mode, 2026-08-25) is not "the operator chose this".
5808
+ */
5809
+ settingsStoreReady = false;
5803
5810
  /** Default config values. Provided via constructor. */
5804
5811
  defaults;
5805
5812
  constructor(defaults) {
@@ -6200,7 +6207,9 @@ var BaseAddon = class {
6200
6207
  ];
6201
6208
  let lastErr;
6202
6209
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6203
- return await settings.readAddonStore() ?? {};
6210
+ const stored = await settings.readAddonStore() ?? {};
6211
+ this.settingsStoreReady = true;
6212
+ return stored;
6204
6213
  } catch (err) {
6205
6214
  lastErr = err;
6206
6215
  const msg = err instanceof Error ? err.message : String(err);
@@ -6208,6 +6217,7 @@ var BaseAddon = class {
6208
6217
  if (attempt === delaysMs.length) break;
6209
6218
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6210
6219
  }
6220
+ this.settingsStoreReady = false;
6211
6221
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6212
6222
  return {};
6213
6223
  }
@@ -8134,6 +8144,15 @@ var LabelDefinitionSchema = object({
8134
8144
  description: string().optional(),
8135
8145
  icon: string().optional()
8136
8146
  });
8147
+ var ClassMapDefinitionSchema = object({
8148
+ mapping: record(string(), _enum([
8149
+ "person",
8150
+ "vehicle",
8151
+ "animal",
8152
+ "package"
8153
+ ])),
8154
+ preserveOriginal: boolean()
8155
+ });
8137
8156
  var MODEL_FORMATS = [
8138
8157
  "onnx",
8139
8158
  "coreml",
@@ -8217,6 +8236,12 @@ var ModelVariantGroupSchema = object({
8217
8236
  */
8218
8237
  resolution: number().int().positive().optional()
8219
8238
  });
8239
+ var ModelProviderIdSchema = _enum([
8240
+ "camstack",
8241
+ "frigate",
8242
+ "scrypted",
8243
+ "custom"
8244
+ ]);
8220
8245
  var ModelCatalogEntrySchema = object({
8221
8246
  id: string(),
8222
8247
  name: string(),
@@ -8312,7 +8337,19 @@ var ModelCatalogEntrySchema = object({
8312
8337
  * `id` stays the source of truth for resolution/download/persistence; grouping
8313
8338
  * is a presentation overlay resolved back to an `id`.
8314
8339
  */
8315
- group: ModelVariantGroupSchema.optional()
8340
+ group: ModelVariantGroupSchema.optional(),
8341
+ /**
8342
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8343
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8344
+ * persisted before this field existed (`inferModelProvider` fills those).
8345
+ */
8346
+ provider: ModelProviderIdSchema.optional(),
8347
+ /**
8348
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8349
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8350
+ * labels already ARE the CamStack macros (Scrypted identity map).
8351
+ */
8352
+ classMap: ClassMapDefinitionSchema.optional()
8316
8353
  });
8317
8354
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8318
8355
  format: literal("openvino"),
@@ -8341,7 +8378,8 @@ var ModelConvertMetadataSchema = object({
8341
8378
  "ocr",
8342
8379
  "segmentation"
8343
8380
  ]),
8344
- faceAlignment: boolean().optional()
8381
+ faceAlignment: boolean().optional(),
8382
+ classMap: ClassMapDefinitionSchema.optional()
8345
8383
  });
8346
8384
  var ConvertResultSchema = object({
8347
8385
  entry: ModelCatalogEntrySchema,
@@ -11872,6 +11910,27 @@ var LinkedDeviceSchema = object({
11872
11910
  features: array(string()),
11873
11911
  producesTrackedEvents: boolean().optional()
11874
11912
  });
11913
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
11914
+ * The batch answer needs the tag; the single-device answer already has it
11915
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
11916
+ var LinkedDevicesForDeviceSchema = object({
11917
+ deviceId: number(),
11918
+ mode: LinkedDevicesModeSchema,
11919
+ devices: array(LinkedDeviceSchema)
11920
+ });
11921
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
11922
+ * `getAllBindings` all answer in. Declared once: three copies of the same
11923
+ * object literal is exactly how the three drift apart. */
11924
+ var DeviceBindingsForDeviceSchema = object({
11925
+ deviceId: number(),
11926
+ entries: array(object({
11927
+ capName: string(),
11928
+ kind: _enum(["native", "wrapped"]),
11929
+ providerAddonId: string(),
11930
+ providerNodeId: string(),
11931
+ nativeAddonId: string()
11932
+ }))
11933
+ });
11875
11934
  var SavedDeviceRowSchema = object({
11876
11935
  /** Numeric id reserved at allocateDeviceId time. */
11877
11936
  id: number(),
@@ -12097,11 +12156,25 @@ method(object({
12097
12156
  projection: _enum(["full", "slim"]).optional(),
12098
12157
  /** Return only camera devices. Filtering server-side instead of
12099
12158
  * shipping 293 rows to find 12. */
12100
- isCamera: boolean().optional()
12159
+ isCamera: boolean().optional(),
12160
+ /**
12161
+ * Return only these device ids. For the caller that already KNOWS the
12162
+ * handful it wants and needs a field the id-bearing answer does not
12163
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12164
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12165
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12166
+ * refetches on the reconcile interval, on a phone.
12167
+ *
12168
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12169
+ * keys rather than rejecting them (verified against the live hub
12170
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12171
+ * it answers today and the caller filters as it already does.
12172
+ */
12173
+ deviceIds: array(number()).optional()
12101
12174
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12102
12175
  mode: LinkedDevicesModeSchema,
12103
12176
  devices: array(LinkedDeviceSchema)
12104
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12177
+ })), 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({
12105
12178
  deviceId: number(),
12106
12179
  values: record(string(), unknown())
12107
12180
  }), object({ success: literal(true) }), {
@@ -12128,25 +12201,7 @@ method(object({
12128
12201
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
12129
12202
  kind: "mutation",
12130
12203
  auth: "admin"
12131
- }), method(object({ deviceId: number() }), object({
12132
- deviceId: number(),
12133
- entries: array(object({
12134
- capName: string(),
12135
- kind: _enum(["native", "wrapped"]),
12136
- providerAddonId: string(),
12137
- providerNodeId: string(),
12138
- nativeAddonId: string()
12139
- }))
12140
- })), method(object({}), array(object({
12141
- deviceId: number(),
12142
- entries: array(object({
12143
- capName: string(),
12144
- kind: _enum(["native", "wrapped"]),
12145
- providerAddonId: string(),
12146
- providerNodeId: string(),
12147
- nativeAddonId: string()
12148
- }))
12149
- }))), method(object({
12204
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12150
12205
  deviceId: number(),
12151
12206
  capName: string(),
12152
12207
  wrapperAddonId: string(),
@@ -14526,12 +14581,15 @@ var NcOccupancyConditionSchema = object({
14526
14581
  * there is no second switch that can disagree with the first and every rule
14527
14582
  * authored before the decision migrates for free (`audioModeOf`):
14528
14583
  *
14529
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14530
- * classifier labels with one of them. No window, no percentage:
14531
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14532
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14533
- * the analyzer's (`classificationMinScore`, per device) a label only
14534
- * reaches this condition if the classifier was already confident enough.
14584
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14585
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14586
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14587
+ * frames is the wrong question for a classifier that labels 1–3 frames
14588
+ * per episode. The count window is the brake that drops a single-frame
14589
+ * false positive; the rule's own `throttle` cooldown is the other. The
14590
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14591
+ * per device) — a label only reaches this condition if the classifier was
14592
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14535
14593
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14536
14594
  * the condition: at least `hitPercent`% of the samples over
14537
14595
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14558,14 +14616,22 @@ var NcOccupancyConditionSchema = object({
14558
14616
  * an operator who typed `dog` mean the same thing.
14559
14617
  */
14560
14618
  var NcAudioConditionSchema = object({
14561
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14619
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14562
14620
  labels: array(string().min(1)).min(1).optional(),
14563
14621
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14564
14622
  dbThreshold: number().min(-96).max(0).optional(),
14565
14623
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14566
14624
  hitPercent: number().int().min(1).max(100).default(60),
14567
14625
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14568
- samplingSeconds: number().int().min(1).max(300).default(10)
14626
+ samplingSeconds: number().int().min(1).max(300).default(10),
14627
+ /**
14628
+ * LABEL MODE: how many labelled frames must land inside
14629
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14630
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14631
+ */
14632
+ confirmHits: number().int().min(1).max(20).optional(),
14633
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14634
+ confirmWindowSec: number().int().min(1).max(60).optional()
14569
14635
  });
14570
14636
  /**
14571
14637
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -16939,6 +17005,46 @@ var RecentTracksPageSchema = object({
16939
17005
  /** Cursor for the next page, or null when this page is the last. */
16940
17006
  nextCursor: string().nullable()
16941
17007
  });
17008
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
17009
+ var LIST_GROUPS_MAX_LIMIT = 100;
17010
+ var AnalyticsGroupRecordSchema = object({
17011
+ id: string(),
17012
+ deviceId: number().int(),
17013
+ openedAt: number().int(),
17014
+ closedAt: number().int(),
17015
+ timestamp: number().int(),
17016
+ memberCount: number().int(),
17017
+ memberTrackIds: array(string()).readonly(),
17018
+ className: string(),
17019
+ classes: array(string()).readonly(),
17020
+ /** Relative event-media path, or null when the group has no picture yet. */
17021
+ mediaUrl: string().nullable(),
17022
+ singleton: boolean()
17023
+ });
17024
+ var AnalyticsGroupMemberSchema = object({
17025
+ trackId: string(),
17026
+ deviceId: number().int(),
17027
+ className: string(),
17028
+ firstSeen: number().int(),
17029
+ lastSeen: number().int(),
17030
+ mediaUrl: string().nullable()
17031
+ });
17032
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
17033
+ var ListGroupsQueryInput = object({
17034
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
17035
+ deviceIds: array(number()),
17036
+ /** Window lower bound on `closedAt` (inclusive). */
17037
+ since: number().optional(),
17038
+ /** Window upper bound on `openedAt` (inclusive). */
17039
+ until: number().optional(),
17040
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
17041
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
17042
+ cursor: string().optional()
17043
+ });
17044
+ var ListGroupsPageSchema = object({
17045
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
17046
+ nextCursor: string().nullable()
17047
+ });
16942
17048
  var KeyEventQueryInput = object({
16943
17049
  deviceId: number(),
16944
17050
  /** Window lower bound (track firstSeen ≥ since). */
@@ -17014,7 +17120,9 @@ var TrackCascadeCountsSchema = object({
17014
17120
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
17015
17121
  plates: number().int(),
17016
17122
  /** Per-track CLIP search vectors removed (best-effort). */
17017
- embeddings: number().int()
17123
+ embeddings: number().int(),
17124
+ /** Group membership + group rows removed with their last member (best-effort). */
17125
+ groups: number().int()
17018
17126
  });
17019
17127
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
17020
17128
  var DiskReconcileCountsSchema = object({
@@ -17160,7 +17268,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17160
17268
  * stationary registry). Default false: the timeline lists passages,
17161
17269
  * not parking records (operator decision, 2026-08-15). */
17162
17270
  includeStationary: boolean().optional()
17163
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17271
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17272
+ deviceId: number(),
17273
+ groupId: string().min(1)
17274
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17164
17275
  kind: "mutation",
17165
17276
  auth: "admin"
17166
17277
  }), 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({
@@ -17378,6 +17489,33 @@ var NativeCropRefSchema = object({
17378
17489
  h: number()
17379
17490
  })
17380
17491
  });
17492
+ object({
17493
+ crop: object({
17494
+ left: number(),
17495
+ top: number(),
17496
+ width: number().positive(),
17497
+ height: number().positive()
17498
+ }).optional(),
17499
+ content: object({
17500
+ width: number().int().positive(),
17501
+ height: number().int().positive()
17502
+ }),
17503
+ fit: _enum(["stretch", "contain"]),
17504
+ format: _enum([
17505
+ "rgb",
17506
+ "gray",
17507
+ "jpeg"
17508
+ ])
17509
+ });
17510
+ var FrameRefSchema = object({
17511
+ registryId: string().min(1),
17512
+ id: string().min(1),
17513
+ width: number().int().positive(),
17514
+ height: number().int().positive(),
17515
+ format: _enum(["rgb", "gray"]),
17516
+ timestamp: number(),
17517
+ capturedAt: number().optional()
17518
+ });
17381
17519
  var ModelFormatSchema$1 = _enum([
17382
17520
  "onnx",
17383
17521
  "coreml",
@@ -17443,7 +17581,8 @@ var PipelineModelOptionSchema = object({
17443
17581
  sizeMB: number()
17444
17582
  })),
17445
17583
  group: ModelVariantGroupSchema.optional(),
17446
- legacy: boolean().optional()
17584
+ legacy: boolean().optional(),
17585
+ provider: ModelProviderIdSchema.optional()
17447
17586
  });
17448
17587
  var ConfigFieldBridge = custom();
17449
17588
  var PipelineAddonSchemaSchema = object({
@@ -17622,6 +17761,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
17622
17761
  steps: array(PipelineStepInputSchema).min(1),
17623
17762
  frame: FrameInputSchema.optional(),
17624
17763
  /**
17764
+ * Process-local lazy frame. Valid only when caller and provider resolve
17765
+ * in the same execution-group process; split/cross-node callers use
17766
+ * `frame`/`image` inline compatibility instead.
17767
+ */
17768
+ frameRef: FrameRefSchema.optional(),
17769
+ /**
17625
17770
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
17626
17771
  * the decoded pixels live in. One more member of the one-of
17627
17772
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -17877,7 +18022,10 @@ var NativeCropResultSchema = object({
17877
18022
  * Which source served this crop, so a quality-sensitive consumer (the native
17878
18023
  * `keyFrame`) can reject a degraded fallback:
17879
18024
  * - `native` — cut from the decode worker's retained NATIVE surface (the
17880
- * quality path).
18025
+ * quality path). A subject-tile serve is also native-resolution and stays
18026
+ * `native` here: the public enum cannot name `tile` without a breaking cap
18027
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
18028
+ * internal crop result (`nativeHits` vs `tileHits`).
17881
18029
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
17882
18030
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
17883
18031
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -18368,12 +18516,41 @@ var RunnerLocalLoadSchema = object({
18368
18516
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
18369
18517
  * working unchanged when they switch to reading from the runner cap.
18370
18518
  */
18519
+ var FrameLazyCountersSchema = object({
18520
+ framesDecoded: number(),
18521
+ framesAdmitted: number(),
18522
+ framesDroppedPixelFree: number(),
18523
+ viewsMaterialized: number(),
18524
+ viewsSkipped: number(),
18525
+ workerToRunnerBytes: number(),
18526
+ runnerToPoolRawBytes: number(),
18527
+ runnerToPoolJpegBytes: number(),
18528
+ onDemandFullFrameRequests: number(),
18529
+ onDemandCropRequests: number(),
18530
+ nativeHits: number(),
18531
+ nativeMisses: number(),
18532
+ tileHits: number(),
18533
+ tileMisses: number(),
18534
+ fallbackHits: number(),
18535
+ fallbackMisses: number(),
18536
+ retainedWritesAvoided: number(),
18537
+ residentRefs: number(),
18538
+ residentBytes: number(),
18539
+ releases: number(),
18540
+ evictions: number(),
18541
+ staleMisses: number()
18542
+ });
18543
+ var FrameLazyMetricsSchema = object({
18544
+ node: FrameLazyCountersSchema,
18545
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
18546
+ });
18371
18547
  var RunnerLocalMetricsSchema = object({
18372
18548
  nodeId: string(),
18373
18549
  activeCameras: number(),
18374
18550
  throttledCameras: number(),
18375
18551
  avgInferenceTimeMs: number(),
18376
- queueDepth: number()
18552
+ queueDepth: number(),
18553
+ frameLazy: FrameLazyMetricsSchema.optional()
18377
18554
  });
18378
18555
  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({
18379
18556
  handle: FrameHandleSchema,
@@ -19673,6 +19850,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
19673
19850
  location: StorageLocationSchema,
19674
19851
  relativePath: string()
19675
19852
  }), _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" });
19853
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
19854
+ var ProfileSettingsSchemaBridge = unknown().nullable();
19855
+ var ProfileSettingsBagSchema = record(string(), unknown());
19676
19856
  /**
19677
19857
  * A live terminal session hosted by the provider addon. Output and input do
19678
19858
  * NOT flow through the capability — they use the addon data plane
@@ -19702,7 +19882,14 @@ var TerminalSessionInfoSchema = object({
19702
19882
  var TerminalProfileInfoSchema = object({
19703
19883
  profileId: string(),
19704
19884
  label: string(),
19705
- description: string().optional()
19885
+ description: string().optional(),
19886
+ /** Spawn defaults the instance form copies on create. */
19887
+ executable: string().optional(),
19888
+ args: array(string()).readonly().optional(),
19889
+ cwd: string().optional(),
19890
+ environment: array(string()).readonly().optional(),
19891
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
19892
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
19706
19893
  });
19707
19894
  /**
19708
19895
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -19715,7 +19902,12 @@ var TerminalInstanceInfoSchema = object({
19715
19902
  profileId: string(),
19716
19903
  profileLabel: string(),
19717
19904
  name: string(),
19718
- enabled: boolean()
19905
+ enabled: boolean(),
19906
+ executable: string(),
19907
+ args: array(string()).readonly(),
19908
+ cwd: string(),
19909
+ environment: array(string()).readonly(),
19910
+ profileSettings: ProfileSettingsBagSchema
19719
19911
  });
19720
19912
  var TerminalLegacyCameraSchema = object({
19721
19913
  stableId: string(),
@@ -19745,7 +19937,23 @@ var TerminalOutputBatchSchema = object({
19745
19937
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
19746
19938
  targetNodeId: string().min(1),
19747
19939
  profileId: string().min(1),
19748
- name: string().trim().min(1).max(160).optional()
19940
+ name: string().trim().min(1).max(160).optional(),
19941
+ executable: string().max(1024).optional(),
19942
+ args: array(string().max(2048)).max(64).optional(),
19943
+ cwd: string().max(1024).optional(),
19944
+ environment: array(string().max(4096)).max(64).optional(),
19945
+ profileSettings: ProfileSettingsBagSchema.optional()
19946
+ }), TerminalInstanceInfoSchema, {
19947
+ kind: "mutation",
19948
+ auth: "admin"
19949
+ }), method(object({
19950
+ instanceId: string().min(1),
19951
+ name: string().trim().min(1).max(160).optional(),
19952
+ executable: string().max(1024).optional(),
19953
+ args: array(string().max(2048)).max(64).optional(),
19954
+ cwd: string().max(1024).optional(),
19955
+ environment: array(string().max(4096)).max(64).optional(),
19956
+ profileSettings: ProfileSettingsBagSchema.optional()
19749
19957
  }), TerminalInstanceInfoSchema, {
19750
19958
  kind: "mutation",
19751
19959
  auth: "admin"
@@ -19767,7 +19975,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
19767
19975
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
19768
19976
  profileId: string(),
19769
19977
  cols: number().int().positive(),
19770
- rows: number().int().positive()
19978
+ rows: number().int().positive(),
19979
+ executable: string().max(1024).optional(),
19980
+ args: array(string().max(2048)).max(64).optional(),
19981
+ cwd: string().max(1024).optional(),
19982
+ environment: array(string().max(4096)).max(64).optional()
19771
19983
  }), TerminalSessionInfoSchema, {
19772
19984
  kind: "mutation",
19773
19985
  auth: "admin"
@@ -22566,10 +22778,10 @@ DeviceType.LawnMower, method(object({ deviceId: number().int().nonnegative() }),
22566
22778
  *
22567
22779
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
22568
22780
  * to receive an ordered list of candidate base URLs it should race
22569
- * on connect — LAN IPv4 first (lowest latency when on same network),
22570
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
22571
- * race them with short timeouts and stick with the winner for the
22572
- * session.
22781
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
22782
+ * when on the same network), then public hostname (if a tunnel is
22783
+ * up). The SDK can race them with short timeouts and stick with the
22784
+ * winner for the session.
22573
22785
  *
22574
22786
  * Why hub-only: agents are not directly addressable by the operator's
22575
22787
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -22724,6 +22936,17 @@ var NotificationEndpointSchema = object({
22724
22936
  /** What the ranking currently resolves to (null when nothing is reachable). */
22725
22937
  resolved: string().nullable()
22726
22938
  });
22939
+ /**
22940
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
22941
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
22942
+ * currently expands to, so the UI can show the effective set either way.
22943
+ */
22944
+ var ViewerEndpointsSchema = object({
22945
+ /** The operator's explicit race set, or empty for AUTO. */
22946
+ baseUrls: array(string()).readonly(),
22947
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
22948
+ resolved: array(string()).readonly()
22949
+ });
22727
22950
  var AllowedAddressesSchema = object({
22728
22951
  /**
22729
22952
  * Allowlist of interface addresses operators have explicitly opted
@@ -22732,6 +22955,20 @@ var AllowedAddressesSchema = object({
22732
22955
  * Network Addresses admin page and persisted by the addon.
22733
22956
  */
22734
22957
  addresses: array(string()).readonly() });
22958
+ var TlsStatusSchema = object({
22959
+ mode: _enum([
22960
+ "generated",
22961
+ "uploaded",
22962
+ "disabled"
22963
+ ]),
22964
+ leafFingerprintSha256: string().nullable(),
22965
+ caFingerprintSha256: string().nullable(),
22966
+ validTo: string().nullable(),
22967
+ sans: array(string()),
22968
+ caCertPem: string().nullable(),
22969
+ reissueError: string().nullable(),
22970
+ restartRequired: boolean()
22971
+ });
22735
22972
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
22736
22973
  /**
22737
22974
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -22741,17 +22978,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
22741
22978
  */
22742
22979
  port: number().int().min(1).max(65535).optional(),
22743
22980
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
22744
- * candidate. Default `true`. */
22981
+ * candidate. Default `false` — loopback is not a client route. */
22745
22982
  includeLoopback: boolean().optional(),
22746
- /** Skip IPv6 entries. Some legacy clients can't parse them.
22747
- * Default `false`. */
22983
+ /** Skip IPv6 entries. Default `false` the palette includes stable
22984
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
22985
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
22748
22986
  ipv4Only: boolean().optional(),
22749
22987
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
22750
22988
  * Pass `'https'` when the caller is itself loaded over HTTPS
22751
22989
  * to avoid mixed-content blocks in the browser. The public
22752
22990
  * tunnel always emits `https://` regardless. */
22753
22991
  scheme: _enum(["http", "https"]).optional()
22754
- }), 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" });
22992
+ }), 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, {
22993
+ kind: "mutation",
22994
+ auth: "admin"
22995
+ }), method(object({
22996
+ certPem: string().min(1),
22997
+ keyPem: string().min(1),
22998
+ caPem: string().optional()
22999
+ }), TlsStatusSchema, {
23000
+ kind: "mutation",
23001
+ auth: "admin"
23002
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
23003
+ kind: "mutation",
23004
+ auth: "admin"
23005
+ });
22755
23006
  object({
22756
23007
  /** Lifecycle state of the lock. `jammed` means the motor reported
22757
23008
  * failure to reach the target — operator intervention required. */
@@ -23932,7 +24183,12 @@ var PlateInfoSchema = object({
23932
24183
  plateBbox: BoundingBoxSchema.optional(),
23933
24184
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
23934
24185
  keyFrameMediaKey: string().optional(),
23935
- base64: string().optional()
24186
+ base64: string().optional(),
24187
+ /**
24188
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
24189
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
24190
+ */
24191
+ cropUrl: string().optional()
23936
24192
  });
23937
24193
  var MediaFileLiteSchema = object({
23938
24194
  key: string(),
@@ -27572,6 +27828,12 @@ Object.freeze({
27572
27828
  addonId: null,
27573
27829
  access: "view"
27574
27830
  },
27831
+ "deviceManager.getBindingsBatch": {
27832
+ capName: "device-manager",
27833
+ capScope: "system",
27834
+ addonId: null,
27835
+ access: "view"
27836
+ },
27575
27837
  "deviceManager.getChildren": {
27576
27838
  capName: "device-manager",
27577
27839
  capScope: "system",
@@ -27632,6 +27894,12 @@ Object.freeze({
27632
27894
  addonId: null,
27633
27895
  access: "view"
27634
27896
  },
27897
+ "deviceManager.getLinkedDevicesBatch": {
27898
+ capName: "device-manager",
27899
+ capScope: "system",
27900
+ addonId: null,
27901
+ access: "view"
27902
+ },
27635
27903
  "deviceManager.getRoleDisplayDefaults": {
27636
27904
  capName: "device-manager",
27637
27905
  capScope: "system",
@@ -28514,6 +28782,12 @@ Object.freeze({
28514
28782
  addonId: null,
28515
28783
  access: "create"
28516
28784
  },
28785
+ "localNetwork.downloadCa": {
28786
+ capName: "local-network",
28787
+ capScope: "system",
28788
+ addonId: null,
28789
+ access: "view"
28790
+ },
28517
28791
  "localNetwork.getAllowedAddresses": {
28518
28792
  capName: "local-network",
28519
28793
  capScope: "system",
@@ -28538,18 +28812,42 @@ Object.freeze({
28538
28812
  addonId: null,
28539
28813
  access: "view"
28540
28814
  },
28815
+ "localNetwork.getTlsStatus": {
28816
+ capName: "local-network",
28817
+ capScope: "system",
28818
+ addonId: null,
28819
+ access: "view"
28820
+ },
28821
+ "localNetwork.getViewerEndpoints": {
28822
+ capName: "local-network",
28823
+ capScope: "system",
28824
+ addonId: null,
28825
+ access: "view"
28826
+ },
28541
28827
  "localNetwork.list": {
28542
28828
  capName: "local-network",
28543
28829
  capScope: "system",
28544
28830
  addonId: null,
28545
28831
  access: "view"
28546
28832
  },
28833
+ "localNetwork.regenerateCertificate": {
28834
+ capName: "local-network",
28835
+ capScope: "system",
28836
+ addonId: null,
28837
+ access: "create"
28838
+ },
28547
28839
  "localNetwork.resetAllowlistToBestMatch": {
28548
28840
  capName: "local-network",
28549
28841
  capScope: "system",
28550
28842
  addonId: null,
28551
28843
  access: "delete"
28552
28844
  },
28845
+ "localNetwork.revertToGeneratedCertificate": {
28846
+ capName: "local-network",
28847
+ capScope: "system",
28848
+ addonId: null,
28849
+ access: "create"
28850
+ },
28553
28851
  "localNetwork.setAllowedAddresses": {
28554
28852
  capName: "local-network",
28555
28853
  capScope: "system",
@@ -28562,6 +28860,18 @@ Object.freeze({
28562
28860
  addonId: null,
28563
28861
  access: "create"
28564
28862
  },
28863
+ "localNetwork.setViewerEndpoints": {
28864
+ capName: "local-network",
28865
+ capScope: "system",
28866
+ addonId: null,
28867
+ access: "create"
28868
+ },
28869
+ "localNetwork.uploadCertificate": {
28870
+ capName: "local-network",
28871
+ capScope: "system",
28872
+ addonId: null,
28873
+ access: "create"
28874
+ },
28565
28875
  "lockControl.lock": {
28566
28876
  capName: "lock-control",
28567
28877
  capScope: "device",
@@ -29360,6 +29670,12 @@ Object.freeze({
29360
29670
  addonId: null,
29361
29671
  access: "view"
29362
29672
  },
29673
+ "pipelineAnalytics.getGroup": {
29674
+ capName: "pipeline-analytics",
29675
+ capScope: "device",
29676
+ addonId: null,
29677
+ access: "view"
29678
+ },
29363
29679
  "pipelineAnalytics.getKeyEvents": {
29364
29680
  capName: "pipeline-analytics",
29365
29681
  capScope: "device",
@@ -29444,6 +29760,12 @@ Object.freeze({
29444
29760
  addonId: null,
29445
29761
  access: "view"
29446
29762
  },
29763
+ "pipelineAnalytics.listGroups": {
29764
+ capName: "pipeline-analytics",
29765
+ capScope: "device",
29766
+ addonId: null,
29767
+ access: "view"
29768
+ },
29447
29769
  "pipelineAnalytics.listOpsLog": {
29448
29770
  capName: "pipeline-analytics",
29449
29771
  capScope: "device",
@@ -31442,6 +31764,12 @@ Object.freeze({
31442
31764
  addonId: null,
31443
31765
  access: "create"
31444
31766
  },
31767
+ "terminalSession.updateInstance": {
31768
+ capName: "terminal-session",
31769
+ capScope: "system",
31770
+ addonId: null,
31771
+ access: "create"
31772
+ },
31445
31773
  "terminalSession.writeInput": {
31446
31774
  capName: "terminal-session",
31447
31775
  capScope: "system",
@@ -32219,6 +32547,11 @@ Object.freeze({
32219
32547
  form: "single",
32220
32548
  optional: false
32221
32549
  }],
32550
+ "deviceManager.getBindingsBatch": [{
32551
+ name: "deviceIds",
32552
+ form: "array",
32553
+ optional: false
32554
+ }],
32222
32555
  "deviceManager.getChildren": [{
32223
32556
  name: "parentDeviceId",
32224
32557
  form: "single",
@@ -32264,6 +32597,11 @@ Object.freeze({
32264
32597
  form: "single",
32265
32598
  optional: false
32266
32599
  }],
32600
+ "deviceManager.getLinkedDevicesBatch": [{
32601
+ name: "deviceIds",
32602
+ form: "array",
32603
+ optional: false
32604
+ }],
32267
32605
  "deviceManager.getSettingsSchema": [{
32268
32606
  name: "deviceId",
32269
32607
  form: "single",
@@ -32284,6 +32622,11 @@ Object.freeze({
32284
32622
  form: "single",
32285
32623
  optional: false
32286
32624
  }],
32625
+ "deviceManager.listAll": [{
32626
+ name: "deviceIds",
32627
+ form: "array",
32628
+ optional: true
32629
+ }],
32287
32630
  "deviceManager.loadConfig": [{
32288
32631
  name: "deviceId",
32289
32632
  form: "single",
@@ -32857,6 +33200,11 @@ Object.freeze({
32857
33200
  form: "single",
32858
33201
  optional: false
32859
33202
  }],
33203
+ "pipelineAnalytics.getGroup": [{
33204
+ name: "deviceId",
33205
+ form: "single",
33206
+ optional: false
33207
+ }],
32860
33208
  "pipelineAnalytics.getKeyEvents": [{
32861
33209
  name: "deviceId",
32862
33210
  form: "single",
@@ -32912,6 +33260,11 @@ Object.freeze({
32912
33260
  form: "array",
32913
33261
  optional: false
32914
33262
  }],
33263
+ "pipelineAnalytics.listGroups": [{
33264
+ name: "deviceIds",
33265
+ form: "array",
33266
+ optional: false
33267
+ }],
32915
33268
  "pipelineAnalytics.listOpsLog": [{
32916
33269
  name: "deviceId",
32917
33270
  form: "single",
@@ -33929,6 +34282,35 @@ Object.freeze(Object.fromEntries([{
33929
34282
  }]
33930
34283
  }].map((s) => [s.stepId, s.defaultModelId])));
33931
34284
  string().min(1);
34285
+ var CLUSTER_STEP_SETTING_FIELDS = [{
34286
+ stepId: "face-embedding",
34287
+ key: "minLandmarkFaceSize",
34288
+ label: "Min face size for recognition (detection px)",
34289
+ 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.",
34290
+ type: "slider",
34291
+ min: 0,
34292
+ max: 64,
34293
+ step: 2,
34294
+ default: 24
34295
+ }];
34296
+ function clusterStepSettingKey(stepId, fieldKey) {
34297
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
34298
+ }
34299
+ var ClusterSettingNumberSchema = number().finite();
34300
+ function readClusterStepSettings(config) {
34301
+ const out = {};
34302
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
34303
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
34304
+ const value = parsed.success ? parsed.data : field.default;
34305
+ const existing = out[field.stepId] ?? {};
34306
+ out[field.stepId] = {
34307
+ ...existing,
34308
+ [field.key]: value
34309
+ };
34310
+ }
34311
+ return out;
34312
+ }
34313
+ readClusterStepSettings({});
33932
34314
  object({
33933
34315
  /**
33934
34316
  * Fraction of the box's own size added on EACH side before cutting.