@camstack/addon-provider-amcrest 0.2.26 → 0.2.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +258 -17
  2. package/dist/addon.mjs +258 -17
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -4,7 +4,7 @@ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).expor
4
4
  //#endregion
5
5
  let node_crypto = require("node:crypto");
6
6
  let node_os = require("node:os");
7
- //#region ../types/dist/event-category-XfKNtfCc.mjs
7
+ //#region ../types/dist/event-category-CIa_iT6b.mjs
8
8
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
9
9
  EventCategory["SystemBoot"] = "system.boot";
10
10
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -20,6 +20,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
20
20
  */
21
21
  EventCategory["SystemRestartCompleted"] = "system.restart-completed";
22
22
  /**
23
+ * The hub reissued its own TLS certificate at boot (`ensureTlsCert`).
24
+ * Emitted only when the material on disk actually changed, so an
25
+ * operator who trusted the old certificate by hand is told rather than
26
+ * discovering it as a browser error. Payload `TlsCertChangedPayload`.
27
+ *
28
+ * Rule: docs/decisions/adr-0227-*.md
29
+ */
30
+ EventCategory["SystemTlsCertChanged"] = "system.tls-cert-changed";
31
+ /**
23
32
  * A newer addon or server-root package version was found by the
24
33
  * authoritative registry check. Emitted once when any observed
25
34
  * `latestVersion` changes (or a package/node first appears behind);
@@ -8004,6 +8013,15 @@ var LabelDefinitionSchema = object({
8004
8013
  description: string().optional(),
8005
8014
  icon: string().optional()
8006
8015
  });
8016
+ var ClassMapDefinitionSchema = object({
8017
+ mapping: record(string(), _enum([
8018
+ "person",
8019
+ "vehicle",
8020
+ "animal",
8021
+ "package"
8022
+ ])),
8023
+ preserveOriginal: boolean()
8024
+ });
8007
8025
  var MODEL_FORMATS = [
8008
8026
  "onnx",
8009
8027
  "coreml",
@@ -8182,7 +8200,13 @@ var ModelCatalogEntrySchema = object({
8182
8200
  * `id` stays the source of truth for resolution/download/persistence; grouping
8183
8201
  * is a presentation overlay resolved back to an `id`.
8184
8202
  */
8185
- group: ModelVariantGroupSchema.optional()
8203
+ group: ModelVariantGroupSchema.optional(),
8204
+ /**
8205
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8206
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8207
+ * labels already ARE the CamStack macros (Scrypted identity map).
8208
+ */
8209
+ classMap: ClassMapDefinitionSchema.optional()
8186
8210
  });
8187
8211
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8188
8212
  format: literal("openvino"),
@@ -8211,7 +8235,8 @@ var ModelConvertMetadataSchema = object({
8211
8235
  "ocr",
8212
8236
  "segmentation"
8213
8237
  ]),
8214
- faceAlignment: boolean().optional()
8238
+ faceAlignment: boolean().optional(),
8239
+ classMap: ClassMapDefinitionSchema.optional()
8215
8240
  });
8216
8241
  var ConvertResultSchema = object({
8217
8242
  entry: ModelCatalogEntrySchema,
@@ -17504,6 +17529,33 @@ var NativeCropRefSchema = object({
17504
17529
  h: number()
17505
17530
  })
17506
17531
  });
17532
+ object({
17533
+ crop: object({
17534
+ left: number(),
17535
+ top: number(),
17536
+ width: number().positive(),
17537
+ height: number().positive()
17538
+ }).optional(),
17539
+ content: object({
17540
+ width: number().int().positive(),
17541
+ height: number().int().positive()
17542
+ }),
17543
+ fit: _enum(["stretch", "contain"]),
17544
+ format: _enum([
17545
+ "rgb",
17546
+ "gray",
17547
+ "jpeg"
17548
+ ])
17549
+ });
17550
+ var FrameRefSchema = object({
17551
+ registryId: string().min(1),
17552
+ id: string().min(1),
17553
+ width: number().int().positive(),
17554
+ height: number().int().positive(),
17555
+ format: _enum(["rgb", "gray"]),
17556
+ timestamp: number(),
17557
+ capturedAt: number().optional()
17558
+ });
17507
17559
  var ModelFormatSchema$1 = _enum([
17508
17560
  "onnx",
17509
17561
  "coreml",
@@ -17748,6 +17800,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
17748
17800
  steps: array(PipelineStepInputSchema).min(1),
17749
17801
  frame: FrameInputSchema.optional(),
17750
17802
  /**
17803
+ * Process-local lazy frame. Valid only when caller and provider resolve
17804
+ * in the same execution-group process; split/cross-node callers use
17805
+ * `frame`/`image` inline compatibility instead.
17806
+ */
17807
+ frameRef: FrameRefSchema.optional(),
17808
+ /**
17751
17809
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
17752
17810
  * the decoded pixels live in. One more member of the one-of
17753
17811
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -18043,7 +18101,10 @@ var NativeCropResultSchema = object({
18043
18101
  * Which source served this crop, so a quality-sensitive consumer (the native
18044
18102
  * `keyFrame`) can reject a degraded fallback:
18045
18103
  * - `native` — cut from the decode worker's retained NATIVE surface (the
18046
- * quality path).
18104
+ * quality path). A subject-tile serve is also native-resolution and stays
18105
+ * `native` here: the public enum cannot name `tile` without a breaking cap
18106
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
18107
+ * internal crop result (`nativeHits` vs `tileHits`).
18047
18108
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
18048
18109
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
18049
18110
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -18534,12 +18595,41 @@ var RunnerLocalLoadSchema = object({
18534
18595
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
18535
18596
  * working unchanged when they switch to reading from the runner cap.
18536
18597
  */
18598
+ var FrameLazyCountersSchema = object({
18599
+ framesDecoded: number(),
18600
+ framesAdmitted: number(),
18601
+ framesDroppedPixelFree: number(),
18602
+ viewsMaterialized: number(),
18603
+ viewsSkipped: number(),
18604
+ workerToRunnerBytes: number(),
18605
+ runnerToPoolRawBytes: number(),
18606
+ runnerToPoolJpegBytes: number(),
18607
+ onDemandFullFrameRequests: number(),
18608
+ onDemandCropRequests: number(),
18609
+ nativeHits: number(),
18610
+ nativeMisses: number(),
18611
+ tileHits: number(),
18612
+ tileMisses: number(),
18613
+ fallbackHits: number(),
18614
+ fallbackMisses: number(),
18615
+ retainedWritesAvoided: number(),
18616
+ residentRefs: number(),
18617
+ residentBytes: number(),
18618
+ releases: number(),
18619
+ evictions: number(),
18620
+ staleMisses: number()
18621
+ });
18622
+ var FrameLazyMetricsSchema = object({
18623
+ node: FrameLazyCountersSchema,
18624
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
18625
+ });
18537
18626
  var RunnerLocalMetricsSchema = object({
18538
18627
  nodeId: string(),
18539
18628
  activeCameras: number(),
18540
18629
  throttledCameras: number(),
18541
18630
  avgInferenceTimeMs: number(),
18542
- queueDepth: number()
18631
+ queueDepth: number(),
18632
+ frameLazy: FrameLazyMetricsSchema.optional()
18543
18633
  });
18544
18634
  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({
18545
18635
  handle: FrameHandleSchema,
@@ -19943,6 +20033,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
19943
20033
  location: StorageLocationSchema,
19944
20034
  relativePath: string()
19945
20035
  }), _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" });
20036
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
20037
+ var ProfileSettingsSchemaBridge = unknown().nullable();
20038
+ var ProfileSettingsBagSchema = record(string(), unknown());
19946
20039
  /**
19947
20040
  * A live terminal session hosted by the provider addon. Output and input do
19948
20041
  * NOT flow through the capability — they use the addon data plane
@@ -19972,7 +20065,14 @@ var TerminalSessionInfoSchema = object({
19972
20065
  var TerminalProfileInfoSchema = object({
19973
20066
  profileId: string(),
19974
20067
  label: string(),
19975
- description: string().optional()
20068
+ description: string().optional(),
20069
+ /** Spawn defaults the instance form copies on create. */
20070
+ executable: string().optional(),
20071
+ args: array(string()).readonly().optional(),
20072
+ cwd: string().optional(),
20073
+ environment: array(string()).readonly().optional(),
20074
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
20075
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
19976
20076
  });
19977
20077
  /**
19978
20078
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -19985,7 +20085,12 @@ var TerminalInstanceInfoSchema = object({
19985
20085
  profileId: string(),
19986
20086
  profileLabel: string(),
19987
20087
  name: string(),
19988
- enabled: boolean()
20088
+ enabled: boolean(),
20089
+ executable: string(),
20090
+ args: array(string()).readonly(),
20091
+ cwd: string(),
20092
+ environment: array(string()).readonly(),
20093
+ profileSettings: ProfileSettingsBagSchema
19989
20094
  });
19990
20095
  var TerminalLegacyCameraSchema = object({
19991
20096
  stableId: string(),
@@ -20015,7 +20120,23 @@ var TerminalOutputBatchSchema = object({
20015
20120
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
20016
20121
  targetNodeId: string().min(1),
20017
20122
  profileId: string().min(1),
20018
- name: string().trim().min(1).max(160).optional()
20123
+ name: string().trim().min(1).max(160).optional(),
20124
+ executable: string().max(1024).optional(),
20125
+ args: array(string().max(2048)).max(64).optional(),
20126
+ cwd: string().max(1024).optional(),
20127
+ environment: array(string().max(4096)).max(64).optional(),
20128
+ profileSettings: ProfileSettingsBagSchema.optional()
20129
+ }), TerminalInstanceInfoSchema, {
20130
+ kind: "mutation",
20131
+ auth: "admin"
20132
+ }), method(object({
20133
+ instanceId: string().min(1),
20134
+ name: string().trim().min(1).max(160).optional(),
20135
+ executable: string().max(1024).optional(),
20136
+ args: array(string().max(2048)).max(64).optional(),
20137
+ cwd: string().max(1024).optional(),
20138
+ environment: array(string().max(4096)).max(64).optional(),
20139
+ profileSettings: ProfileSettingsBagSchema.optional()
20019
20140
  }), TerminalInstanceInfoSchema, {
20020
20141
  kind: "mutation",
20021
20142
  auth: "admin"
@@ -20037,7 +20158,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
20037
20158
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
20038
20159
  profileId: string(),
20039
20160
  cols: number().int().positive(),
20040
- rows: number().int().positive()
20161
+ rows: number().int().positive(),
20162
+ executable: string().max(1024).optional(),
20163
+ args: array(string().max(2048)).max(64).optional(),
20164
+ cwd: string().max(1024).optional(),
20165
+ environment: array(string().max(4096)).max(64).optional()
20041
20166
  }), TerminalSessionInfoSchema, {
20042
20167
  kind: "mutation",
20043
20168
  auth: "admin"
@@ -23912,10 +24037,10 @@ var lawnMowerControlCapability = {
23912
24037
  *
23913
24038
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
23914
24039
  * to receive an ordered list of candidate base URLs it should race
23915
- * on connect — LAN IPv4 first (lowest latency when on same network),
23916
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
23917
- * race them with short timeouts and stick with the winner for the
23918
- * session.
24040
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
24041
+ * when on the same network), then public hostname (if a tunnel is
24042
+ * up). The SDK can race them with short timeouts and stick with the
24043
+ * winner for the session.
23919
24044
  *
23920
24045
  * Why hub-only: agents are not directly addressable by the operator's
23921
24046
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -24070,6 +24195,17 @@ var NotificationEndpointSchema = object({
24070
24195
  /** What the ranking currently resolves to (null when nothing is reachable). */
24071
24196
  resolved: string().nullable()
24072
24197
  });
24198
+ /**
24199
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
24200
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
24201
+ * currently expands to, so the UI can show the effective set either way.
24202
+ */
24203
+ var ViewerEndpointsSchema = object({
24204
+ /** The operator's explicit race set, or empty for AUTO. */
24205
+ baseUrls: array(string()).readonly(),
24206
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
24207
+ resolved: array(string()).readonly()
24208
+ });
24073
24209
  var AllowedAddressesSchema = object({
24074
24210
  /**
24075
24211
  * Allowlist of interface addresses operators have explicitly opted
@@ -24078,6 +24214,20 @@ var AllowedAddressesSchema = object({
24078
24214
  * Network Addresses admin page and persisted by the addon.
24079
24215
  */
24080
24216
  addresses: array(string()).readonly() });
24217
+ var TlsStatusSchema = object({
24218
+ mode: _enum([
24219
+ "generated",
24220
+ "uploaded",
24221
+ "disabled"
24222
+ ]),
24223
+ leafFingerprintSha256: string().nullable(),
24224
+ caFingerprintSha256: string().nullable(),
24225
+ validTo: string().nullable(),
24226
+ sans: array(string()),
24227
+ caCertPem: string().nullable(),
24228
+ reissueError: string().nullable(),
24229
+ restartRequired: boolean()
24230
+ });
24081
24231
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
24082
24232
  /**
24083
24233
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -24087,17 +24237,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
24087
24237
  */
24088
24238
  port: number().int().min(1).max(65535).optional(),
24089
24239
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
24090
- * candidate. Default `true`. */
24240
+ * candidate. Default `false` — loopback is not a client route. */
24091
24241
  includeLoopback: boolean().optional(),
24092
- /** Skip IPv6 entries. Some legacy clients can't parse them.
24093
- * Default `false`. */
24242
+ /** Skip IPv6 entries. Default `false` the palette includes stable
24243
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
24244
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
24094
24245
  ipv4Only: boolean().optional(),
24095
24246
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
24096
24247
  * Pass `'https'` when the caller is itself loaded over HTTPS
24097
24248
  * to avoid mixed-content blocks in the browser. The public
24098
24249
  * tunnel always emits `https://` regardless. */
24099
24250
  scheme: _enum(["http", "https"]).optional()
24100
- }), 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" });
24251
+ }), 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, {
24252
+ kind: "mutation",
24253
+ auth: "admin"
24254
+ }), method(object({
24255
+ certPem: string().min(1),
24256
+ keyPem: string().min(1),
24257
+ caPem: string().optional()
24258
+ }), TlsStatusSchema, {
24259
+ kind: "mutation",
24260
+ auth: "admin"
24261
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
24262
+ kind: "mutation",
24263
+ auth: "admin"
24264
+ });
24101
24265
  var LockControlStatusSchema = object({
24102
24266
  /** Lifecycle state of the lock. `jammed` means the motor reported
24103
24267
  * failure to reach the target — operator intervention required. */
@@ -32616,6 +32780,12 @@ Object.freeze({
32616
32780
  addonId: null,
32617
32781
  access: "create"
32618
32782
  },
32783
+ "localNetwork.downloadCa": {
32784
+ capName: "local-network",
32785
+ capScope: "system",
32786
+ addonId: null,
32787
+ access: "view"
32788
+ },
32619
32789
  "localNetwork.getAllowedAddresses": {
32620
32790
  capName: "local-network",
32621
32791
  capScope: "system",
@@ -32640,18 +32810,42 @@ Object.freeze({
32640
32810
  addonId: null,
32641
32811
  access: "view"
32642
32812
  },
32813
+ "localNetwork.getTlsStatus": {
32814
+ capName: "local-network",
32815
+ capScope: "system",
32816
+ addonId: null,
32817
+ access: "view"
32818
+ },
32819
+ "localNetwork.getViewerEndpoints": {
32820
+ capName: "local-network",
32821
+ capScope: "system",
32822
+ addonId: null,
32823
+ access: "view"
32824
+ },
32643
32825
  "localNetwork.list": {
32644
32826
  capName: "local-network",
32645
32827
  capScope: "system",
32646
32828
  addonId: null,
32647
32829
  access: "view"
32648
32830
  },
32831
+ "localNetwork.regenerateCertificate": {
32832
+ capName: "local-network",
32833
+ capScope: "system",
32834
+ addonId: null,
32835
+ access: "create"
32836
+ },
32649
32837
  "localNetwork.resetAllowlistToBestMatch": {
32650
32838
  capName: "local-network",
32651
32839
  capScope: "system",
32652
32840
  addonId: null,
32653
32841
  access: "delete"
32654
32842
  },
32843
+ "localNetwork.revertToGeneratedCertificate": {
32844
+ capName: "local-network",
32845
+ capScope: "system",
32846
+ addonId: null,
32847
+ access: "create"
32848
+ },
32655
32849
  "localNetwork.setAllowedAddresses": {
32656
32850
  capName: "local-network",
32657
32851
  capScope: "system",
@@ -32664,6 +32858,18 @@ Object.freeze({
32664
32858
  addonId: null,
32665
32859
  access: "create"
32666
32860
  },
32861
+ "localNetwork.setViewerEndpoints": {
32862
+ capName: "local-network",
32863
+ capScope: "system",
32864
+ addonId: null,
32865
+ access: "create"
32866
+ },
32867
+ "localNetwork.uploadCertificate": {
32868
+ capName: "local-network",
32869
+ capScope: "system",
32870
+ addonId: null,
32871
+ access: "create"
32872
+ },
32667
32873
  "lockControl.lock": {
32668
32874
  capName: "lock-control",
32669
32875
  capScope: "device",
@@ -35544,6 +35750,12 @@ Object.freeze({
35544
35750
  addonId: null,
35545
35751
  access: "create"
35546
35752
  },
35753
+ "terminalSession.updateInstance": {
35754
+ capName: "terminal-session",
35755
+ capScope: "system",
35756
+ addonId: null,
35757
+ access: "create"
35758
+ },
35547
35759
  "terminalSession.writeInput": {
35548
35760
  capName: "terminal-session",
35549
35761
  capScope: "system",
@@ -38031,6 +38243,35 @@ Object.freeze(Object.fromEntries([{
38031
38243
  }]
38032
38244
  }].map((s) => [s.stepId, s.defaultModelId])));
38033
38245
  string().min(1);
38246
+ var CLUSTER_STEP_SETTING_FIELDS = [{
38247
+ stepId: "face-embedding",
38248
+ key: "minLandmarkFaceSize",
38249
+ label: "Min face size for recognition (detection px)",
38250
+ 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.",
38251
+ type: "slider",
38252
+ min: 0,
38253
+ max: 64,
38254
+ step: 2,
38255
+ default: 24
38256
+ }];
38257
+ function clusterStepSettingKey(stepId, fieldKey) {
38258
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
38259
+ }
38260
+ var ClusterSettingNumberSchema = number().finite();
38261
+ function readClusterStepSettings(config) {
38262
+ const out = {};
38263
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
38264
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
38265
+ const value = parsed.success ? parsed.data : field.default;
38266
+ const existing = out[field.stepId] ?? {};
38267
+ out[field.stepId] = {
38268
+ ...existing,
38269
+ [field.key]: value
38270
+ };
38271
+ }
38272
+ return out;
38273
+ }
38274
+ readClusterStepSettings({});
38034
38275
  object({
38035
38276
  /**
38036
38277
  * Fraction of the box's own size added on EACH side before cutting.
package/dist/addon.mjs CHANGED
@@ -5,7 +5,7 @@ import { networkInterfaces } from "node:os";
5
5
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
6
6
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
7
7
  //#endregion
8
- //#region ../types/dist/event-category-XfKNtfCc.mjs
8
+ //#region ../types/dist/event-category-CIa_iT6b.mjs
9
9
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
10
10
  EventCategory["SystemBoot"] = "system.boot";
11
11
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -21,6 +21,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
21
21
  */
22
22
  EventCategory["SystemRestartCompleted"] = "system.restart-completed";
23
23
  /**
24
+ * The hub reissued its own TLS certificate at boot (`ensureTlsCert`).
25
+ * Emitted only when the material on disk actually changed, so an
26
+ * operator who trusted the old certificate by hand is told rather than
27
+ * discovering it as a browser error. Payload `TlsCertChangedPayload`.
28
+ *
29
+ * Rule: docs/decisions/adr-0227-*.md
30
+ */
31
+ EventCategory["SystemTlsCertChanged"] = "system.tls-cert-changed";
32
+ /**
24
33
  * A newer addon or server-root package version was found by the
25
34
  * authoritative registry check. Emitted once when any observed
26
35
  * `latestVersion` changes (or a package/node first appears behind);
@@ -8005,6 +8014,15 @@ var LabelDefinitionSchema = object({
8005
8014
  description: string().optional(),
8006
8015
  icon: string().optional()
8007
8016
  });
8017
+ var ClassMapDefinitionSchema = object({
8018
+ mapping: record(string(), _enum([
8019
+ "person",
8020
+ "vehicle",
8021
+ "animal",
8022
+ "package"
8023
+ ])),
8024
+ preserveOriginal: boolean()
8025
+ });
8008
8026
  var MODEL_FORMATS = [
8009
8027
  "onnx",
8010
8028
  "coreml",
@@ -8183,7 +8201,13 @@ var ModelCatalogEntrySchema = object({
8183
8201
  * `id` stays the source of truth for resolution/download/persistence; grouping
8184
8202
  * is a presentation overlay resolved back to an `id`.
8185
8203
  */
8186
- group: ModelVariantGroupSchema.optional()
8204
+ group: ModelVariantGroupSchema.optional(),
8205
+ /**
8206
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8207
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8208
+ * labels already ARE the CamStack macros (Scrypted identity map).
8209
+ */
8210
+ classMap: ClassMapDefinitionSchema.optional()
8187
8211
  });
8188
8212
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8189
8213
  format: literal("openvino"),
@@ -8212,7 +8236,8 @@ var ModelConvertMetadataSchema = object({
8212
8236
  "ocr",
8213
8237
  "segmentation"
8214
8238
  ]),
8215
- faceAlignment: boolean().optional()
8239
+ faceAlignment: boolean().optional(),
8240
+ classMap: ClassMapDefinitionSchema.optional()
8216
8241
  });
8217
8242
  var ConvertResultSchema = object({
8218
8243
  entry: ModelCatalogEntrySchema,
@@ -17505,6 +17530,33 @@ var NativeCropRefSchema = object({
17505
17530
  h: number()
17506
17531
  })
17507
17532
  });
17533
+ object({
17534
+ crop: object({
17535
+ left: number(),
17536
+ top: number(),
17537
+ width: number().positive(),
17538
+ height: number().positive()
17539
+ }).optional(),
17540
+ content: object({
17541
+ width: number().int().positive(),
17542
+ height: number().int().positive()
17543
+ }),
17544
+ fit: _enum(["stretch", "contain"]),
17545
+ format: _enum([
17546
+ "rgb",
17547
+ "gray",
17548
+ "jpeg"
17549
+ ])
17550
+ });
17551
+ var FrameRefSchema = object({
17552
+ registryId: string().min(1),
17553
+ id: string().min(1),
17554
+ width: number().int().positive(),
17555
+ height: number().int().positive(),
17556
+ format: _enum(["rgb", "gray"]),
17557
+ timestamp: number(),
17558
+ capturedAt: number().optional()
17559
+ });
17508
17560
  var ModelFormatSchema$1 = _enum([
17509
17561
  "onnx",
17510
17562
  "coreml",
@@ -17749,6 +17801,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
17749
17801
  steps: array(PipelineStepInputSchema).min(1),
17750
17802
  frame: FrameInputSchema.optional(),
17751
17803
  /**
17804
+ * Process-local lazy frame. Valid only when caller and provider resolve
17805
+ * in the same execution-group process; split/cross-node callers use
17806
+ * `frame`/`image` inline compatibility instead.
17807
+ */
17808
+ frameRef: FrameRefSchema.optional(),
17809
+ /**
17752
17810
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
17753
17811
  * the decoded pixels live in. One more member of the one-of
17754
17812
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -18044,7 +18102,10 @@ var NativeCropResultSchema = object({
18044
18102
  * Which source served this crop, so a quality-sensitive consumer (the native
18045
18103
  * `keyFrame`) can reject a degraded fallback:
18046
18104
  * - `native` — cut from the decode worker's retained NATIVE surface (the
18047
- * quality path).
18105
+ * quality path). A subject-tile serve is also native-resolution and stays
18106
+ * `native` here: the public enum cannot name `tile` without a breaking cap
18107
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
18108
+ * internal crop result (`nativeHits` vs `tileHits`).
18048
18109
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
18049
18110
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
18050
18111
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -18535,12 +18596,41 @@ var RunnerLocalLoadSchema = object({
18535
18596
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
18536
18597
  * working unchanged when they switch to reading from the runner cap.
18537
18598
  */
18599
+ var FrameLazyCountersSchema = object({
18600
+ framesDecoded: number(),
18601
+ framesAdmitted: number(),
18602
+ framesDroppedPixelFree: number(),
18603
+ viewsMaterialized: number(),
18604
+ viewsSkipped: number(),
18605
+ workerToRunnerBytes: number(),
18606
+ runnerToPoolRawBytes: number(),
18607
+ runnerToPoolJpegBytes: number(),
18608
+ onDemandFullFrameRequests: number(),
18609
+ onDemandCropRequests: number(),
18610
+ nativeHits: number(),
18611
+ nativeMisses: number(),
18612
+ tileHits: number(),
18613
+ tileMisses: number(),
18614
+ fallbackHits: number(),
18615
+ fallbackMisses: number(),
18616
+ retainedWritesAvoided: number(),
18617
+ residentRefs: number(),
18618
+ residentBytes: number(),
18619
+ releases: number(),
18620
+ evictions: number(),
18621
+ staleMisses: number()
18622
+ });
18623
+ var FrameLazyMetricsSchema = object({
18624
+ node: FrameLazyCountersSchema,
18625
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
18626
+ });
18538
18627
  var RunnerLocalMetricsSchema = object({
18539
18628
  nodeId: string(),
18540
18629
  activeCameras: number(),
18541
18630
  throttledCameras: number(),
18542
18631
  avgInferenceTimeMs: number(),
18543
- queueDepth: number()
18632
+ queueDepth: number(),
18633
+ frameLazy: FrameLazyMetricsSchema.optional()
18544
18634
  });
18545
18635
  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({
18546
18636
  handle: FrameHandleSchema,
@@ -19944,6 +20034,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
19944
20034
  location: StorageLocationSchema,
19945
20035
  relativePath: string()
19946
20036
  }), _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" });
20037
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
20038
+ var ProfileSettingsSchemaBridge = unknown().nullable();
20039
+ var ProfileSettingsBagSchema = record(string(), unknown());
19947
20040
  /**
19948
20041
  * A live terminal session hosted by the provider addon. Output and input do
19949
20042
  * NOT flow through the capability — they use the addon data plane
@@ -19973,7 +20066,14 @@ var TerminalSessionInfoSchema = object({
19973
20066
  var TerminalProfileInfoSchema = object({
19974
20067
  profileId: string(),
19975
20068
  label: string(),
19976
- description: string().optional()
20069
+ description: string().optional(),
20070
+ /** Spawn defaults the instance form copies on create. */
20071
+ executable: string().optional(),
20072
+ args: array(string()).readonly().optional(),
20073
+ cwd: string().optional(),
20074
+ environment: array(string()).readonly().optional(),
20075
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
20076
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
19977
20077
  });
19978
20078
  /**
19979
20079
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -19986,7 +20086,12 @@ var TerminalInstanceInfoSchema = object({
19986
20086
  profileId: string(),
19987
20087
  profileLabel: string(),
19988
20088
  name: string(),
19989
- enabled: boolean()
20089
+ enabled: boolean(),
20090
+ executable: string(),
20091
+ args: array(string()).readonly(),
20092
+ cwd: string(),
20093
+ environment: array(string()).readonly(),
20094
+ profileSettings: ProfileSettingsBagSchema
19990
20095
  });
19991
20096
  var TerminalLegacyCameraSchema = object({
19992
20097
  stableId: string(),
@@ -20016,7 +20121,23 @@ var TerminalOutputBatchSchema = object({
20016
20121
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
20017
20122
  targetNodeId: string().min(1),
20018
20123
  profileId: string().min(1),
20019
- name: string().trim().min(1).max(160).optional()
20124
+ name: string().trim().min(1).max(160).optional(),
20125
+ executable: string().max(1024).optional(),
20126
+ args: array(string().max(2048)).max(64).optional(),
20127
+ cwd: string().max(1024).optional(),
20128
+ environment: array(string().max(4096)).max(64).optional(),
20129
+ profileSettings: ProfileSettingsBagSchema.optional()
20130
+ }), TerminalInstanceInfoSchema, {
20131
+ kind: "mutation",
20132
+ auth: "admin"
20133
+ }), method(object({
20134
+ instanceId: string().min(1),
20135
+ name: string().trim().min(1).max(160).optional(),
20136
+ executable: string().max(1024).optional(),
20137
+ args: array(string().max(2048)).max(64).optional(),
20138
+ cwd: string().max(1024).optional(),
20139
+ environment: array(string().max(4096)).max(64).optional(),
20140
+ profileSettings: ProfileSettingsBagSchema.optional()
20020
20141
  }), TerminalInstanceInfoSchema, {
20021
20142
  kind: "mutation",
20022
20143
  auth: "admin"
@@ -20038,7 +20159,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
20038
20159
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
20039
20160
  profileId: string(),
20040
20161
  cols: number().int().positive(),
20041
- rows: number().int().positive()
20162
+ rows: number().int().positive(),
20163
+ executable: string().max(1024).optional(),
20164
+ args: array(string().max(2048)).max(64).optional(),
20165
+ cwd: string().max(1024).optional(),
20166
+ environment: array(string().max(4096)).max(64).optional()
20042
20167
  }), TerminalSessionInfoSchema, {
20043
20168
  kind: "mutation",
20044
20169
  auth: "admin"
@@ -23913,10 +24038,10 @@ var lawnMowerControlCapability = {
23913
24038
  *
23914
24039
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
23915
24040
  * to receive an ordered list of candidate base URLs it should race
23916
- * on connect — LAN IPv4 first (lowest latency when on same network),
23917
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
23918
- * race them with short timeouts and stick with the winner for the
23919
- * session.
24041
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
24042
+ * when on the same network), then public hostname (if a tunnel is
24043
+ * up). The SDK can race them with short timeouts and stick with the
24044
+ * winner for the session.
23920
24045
  *
23921
24046
  * Why hub-only: agents are not directly addressable by the operator's
23922
24047
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -24071,6 +24196,17 @@ var NotificationEndpointSchema = object({
24071
24196
  /** What the ranking currently resolves to (null when nothing is reachable). */
24072
24197
  resolved: string().nullable()
24073
24198
  });
24199
+ /**
24200
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
24201
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
24202
+ * currently expands to, so the UI can show the effective set either way.
24203
+ */
24204
+ var ViewerEndpointsSchema = object({
24205
+ /** The operator's explicit race set, or empty for AUTO. */
24206
+ baseUrls: array(string()).readonly(),
24207
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
24208
+ resolved: array(string()).readonly()
24209
+ });
24074
24210
  var AllowedAddressesSchema = object({
24075
24211
  /**
24076
24212
  * Allowlist of interface addresses operators have explicitly opted
@@ -24079,6 +24215,20 @@ var AllowedAddressesSchema = object({
24079
24215
  * Network Addresses admin page and persisted by the addon.
24080
24216
  */
24081
24217
  addresses: array(string()).readonly() });
24218
+ var TlsStatusSchema = object({
24219
+ mode: _enum([
24220
+ "generated",
24221
+ "uploaded",
24222
+ "disabled"
24223
+ ]),
24224
+ leafFingerprintSha256: string().nullable(),
24225
+ caFingerprintSha256: string().nullable(),
24226
+ validTo: string().nullable(),
24227
+ sans: array(string()),
24228
+ caCertPem: string().nullable(),
24229
+ reissueError: string().nullable(),
24230
+ restartRequired: boolean()
24231
+ });
24082
24232
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
24083
24233
  /**
24084
24234
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -24088,17 +24238,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
24088
24238
  */
24089
24239
  port: number().int().min(1).max(65535).optional(),
24090
24240
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
24091
- * candidate. Default `true`. */
24241
+ * candidate. Default `false` — loopback is not a client route. */
24092
24242
  includeLoopback: boolean().optional(),
24093
- /** Skip IPv6 entries. Some legacy clients can't parse them.
24094
- * Default `false`. */
24243
+ /** Skip IPv6 entries. Default `false` the palette includes stable
24244
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
24245
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
24095
24246
  ipv4Only: boolean().optional(),
24096
24247
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
24097
24248
  * Pass `'https'` when the caller is itself loaded over HTTPS
24098
24249
  * to avoid mixed-content blocks in the browser. The public
24099
24250
  * tunnel always emits `https://` regardless. */
24100
24251
  scheme: _enum(["http", "https"]).optional()
24101
- }), 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" });
24252
+ }), 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, {
24253
+ kind: "mutation",
24254
+ auth: "admin"
24255
+ }), method(object({
24256
+ certPem: string().min(1),
24257
+ keyPem: string().min(1),
24258
+ caPem: string().optional()
24259
+ }), TlsStatusSchema, {
24260
+ kind: "mutation",
24261
+ auth: "admin"
24262
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
24263
+ kind: "mutation",
24264
+ auth: "admin"
24265
+ });
24102
24266
  var LockControlStatusSchema = object({
24103
24267
  /** Lifecycle state of the lock. `jammed` means the motor reported
24104
24268
  * failure to reach the target — operator intervention required. */
@@ -32617,6 +32781,12 @@ Object.freeze({
32617
32781
  addonId: null,
32618
32782
  access: "create"
32619
32783
  },
32784
+ "localNetwork.downloadCa": {
32785
+ capName: "local-network",
32786
+ capScope: "system",
32787
+ addonId: null,
32788
+ access: "view"
32789
+ },
32620
32790
  "localNetwork.getAllowedAddresses": {
32621
32791
  capName: "local-network",
32622
32792
  capScope: "system",
@@ -32641,18 +32811,42 @@ Object.freeze({
32641
32811
  addonId: null,
32642
32812
  access: "view"
32643
32813
  },
32814
+ "localNetwork.getTlsStatus": {
32815
+ capName: "local-network",
32816
+ capScope: "system",
32817
+ addonId: null,
32818
+ access: "view"
32819
+ },
32820
+ "localNetwork.getViewerEndpoints": {
32821
+ capName: "local-network",
32822
+ capScope: "system",
32823
+ addonId: null,
32824
+ access: "view"
32825
+ },
32644
32826
  "localNetwork.list": {
32645
32827
  capName: "local-network",
32646
32828
  capScope: "system",
32647
32829
  addonId: null,
32648
32830
  access: "view"
32649
32831
  },
32832
+ "localNetwork.regenerateCertificate": {
32833
+ capName: "local-network",
32834
+ capScope: "system",
32835
+ addonId: null,
32836
+ access: "create"
32837
+ },
32650
32838
  "localNetwork.resetAllowlistToBestMatch": {
32651
32839
  capName: "local-network",
32652
32840
  capScope: "system",
32653
32841
  addonId: null,
32654
32842
  access: "delete"
32655
32843
  },
32844
+ "localNetwork.revertToGeneratedCertificate": {
32845
+ capName: "local-network",
32846
+ capScope: "system",
32847
+ addonId: null,
32848
+ access: "create"
32849
+ },
32656
32850
  "localNetwork.setAllowedAddresses": {
32657
32851
  capName: "local-network",
32658
32852
  capScope: "system",
@@ -32665,6 +32859,18 @@ Object.freeze({
32665
32859
  addonId: null,
32666
32860
  access: "create"
32667
32861
  },
32862
+ "localNetwork.setViewerEndpoints": {
32863
+ capName: "local-network",
32864
+ capScope: "system",
32865
+ addonId: null,
32866
+ access: "create"
32867
+ },
32868
+ "localNetwork.uploadCertificate": {
32869
+ capName: "local-network",
32870
+ capScope: "system",
32871
+ addonId: null,
32872
+ access: "create"
32873
+ },
32668
32874
  "lockControl.lock": {
32669
32875
  capName: "lock-control",
32670
32876
  capScope: "device",
@@ -35545,6 +35751,12 @@ Object.freeze({
35545
35751
  addonId: null,
35546
35752
  access: "create"
35547
35753
  },
35754
+ "terminalSession.updateInstance": {
35755
+ capName: "terminal-session",
35756
+ capScope: "system",
35757
+ addonId: null,
35758
+ access: "create"
35759
+ },
35548
35760
  "terminalSession.writeInput": {
35549
35761
  capName: "terminal-session",
35550
35762
  capScope: "system",
@@ -38032,6 +38244,35 @@ Object.freeze(Object.fromEntries([{
38032
38244
  }]
38033
38245
  }].map((s) => [s.stepId, s.defaultModelId])));
38034
38246
  string().min(1);
38247
+ var CLUSTER_STEP_SETTING_FIELDS = [{
38248
+ stepId: "face-embedding",
38249
+ key: "minLandmarkFaceSize",
38250
+ label: "Min face size for recognition (detection px)",
38251
+ 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.",
38252
+ type: "slider",
38253
+ min: 0,
38254
+ max: 64,
38255
+ step: 2,
38256
+ default: 24
38257
+ }];
38258
+ function clusterStepSettingKey(stepId, fieldKey) {
38259
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
38260
+ }
38261
+ var ClusterSettingNumberSchema = number().finite();
38262
+ function readClusterStepSettings(config) {
38263
+ const out = {};
38264
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
38265
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
38266
+ const value = parsed.success ? parsed.data : field.default;
38267
+ const existing = out[field.stepId] ?? {};
38268
+ out[field.stepId] = {
38269
+ ...existing,
38270
+ [field.key]: value
38271
+ };
38272
+ }
38273
+ return out;
38274
+ }
38275
+ readClusterStepSettings({});
38035
38276
  object({
38036
38277
  /**
38037
38278
  * Fraction of the box's own size added on EACH side before cutting.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-amcrest",
3
- "version": "0.2.26",
3
+ "version": "0.2.28",
4
4
  "description": "Amcrest/Dahua camera device provider addon for CamStack — Dahua CGI over HTTP(S) with digest auth (snapshot, RTSP catalog, PTZ, image/day-night config)",
5
5
  "keywords": [
6
6
  "camstack",