@camstack/addon-post-analysis 1.2.49 → 1.2.50

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.
@@ -6482,7 +6482,20 @@ var BrokerStatsSchema = object({
6482
6482
  sampleRate: number(),
6483
6483
  channels: number(),
6484
6484
  supported: boolean()
6485
- }).nullable().optional()
6485
+ }).nullable().optional(),
6486
+ /**
6487
+ * BROKER-SIDE AUDIO MUTE (D83). `true` = this broker is deliberately
6488
+ * distributing none of the device's audio, on live or recording.
6489
+ *
6490
+ * Present so a silent camera can be told apart from a broken one on the
6491
+ * stream panel itself, without cross-referencing the switch group: a
6492
+ * broker holding an `audio` track descriptor while `audioMuted` is true is
6493
+ * working exactly as asked. `audioMutedDropped` counts the audio units
6494
+ * thrown away since the current dial — it is how you confirm from stats
6495
+ * alone that the mute is on the packet path and not merely persisted.
6496
+ */
6497
+ audioMuted: boolean().optional(),
6498
+ audioMutedDropped: number().optional()
6486
6499
  });
6487
6500
  /**
6488
6501
  * Exporter-facing "profile restream" entry. Returned by
@@ -7313,6 +7326,19 @@ object({
7313
7326
  * | `notifications` | `notificationRules.setDeviceMuted` | `NotificationCenter.evaluateAndEnqueue` returns before any rule is evaluated |
7314
7327
  * | `privacy-mask` | `privacyMask.setMask({ enabled })` → the CAMERA | the camera blanks the masked regions itself; every stream and recording carries the black boxes |
7315
7328
  * | `device-audio` | `privacyMask.setAudioEnabled` → the CAMERA | the camera stops encoding an audio track at all; every consumer sees silent video |
7329
+ * | `broker-audio` | `streamBroker.setDeviceAudioMute` → `DeviceOverride.audioMuted` | `StreamBroker.setAudioMuted` drops the audio plane at the source: no `type:'audio'` packet leaves `fanOutEncoded`, no RTP reaches the restreamer, and the restreamer serves the video-only SDP |
7330
+ *
7331
+ * ## `device-audio` and `broker-audio` are two functions, not two knobs
7332
+ *
7333
+ * They look adjacent and they are not the same control ([D83](../../../../docs/decisions/adr-0083.md)):
7334
+ * `device-audio` writes the CAMERA, so it is hardware privacy — the microphone
7335
+ * genuinely stops, it survives CamStack entirely, and it costs a multi-second
7336
+ * encoder restart on every flip. `broker-audio` writes THIS server, so it is
7337
+ * instant, vendor-independent and reversible without touching the camera, and
7338
+ * a camera that ignores or lacks the ISAPI/Reolink control is still silenced.
7339
+ * D62 forbids a second switch that *disagrees* with the first; these two
7340
+ * cannot disagree, because neither reads the other's store — the camera holds
7341
+ * one, the broker holds the other, and each reports its own fact.
7316
7342
  *
7317
7343
  * ## The two switches whose authority is not on this server
7318
7344
  *
@@ -7374,6 +7400,7 @@ var CameraSwitchIdSchema = _enum([
7374
7400
  "object-detection",
7375
7401
  "privacy-mask",
7376
7402
  "device-audio",
7403
+ "broker-audio",
7377
7404
  "audio-analysis",
7378
7405
  "recording",
7379
7406
  "notifications"
@@ -7399,7 +7426,8 @@ var CameraSwitchAuthoritySchema = discriminatedUnion("kind", [
7399
7426
  object({
7400
7427
  kind: literal("camera-mask"),
7401
7428
  capName: string()
7402
- })
7429
+ }),
7430
+ object({ kind: literal("broker-audio-mute") })
7403
7431
  ]);
7404
7432
  /**
7405
7433
  * Why a switch is not offered for this camera. Rendered instead of the
@@ -9621,7 +9649,25 @@ method(object({
9621
9649
  }), _void(), {
9622
9650
  kind: "mutation",
9623
9651
  auth: "admin"
9624
- }), method(object({ brokerId: string() }), boolean()), object({
9652
+ }), method(object({ brokerId: string() }), boolean()), method(object({ deviceId: number().int() }), object({
9653
+ muted: boolean(),
9654
+ /**
9655
+ * How many live non-derived brokers currently hold the mute. Purely
9656
+ * diagnostic: `muted` is the policy and is authoritative on its own
9657
+ * (it applies to brokers that do not exist yet), while this says
9658
+ * whether anything is presently being silenced.
9659
+ */
9660
+ appliedBrokers: number().int().nonnegative()
9661
+ })), method(object({
9662
+ deviceId: number().int(),
9663
+ muted: boolean()
9664
+ }), object({
9665
+ muted: boolean(),
9666
+ appliedBrokers: number().int().nonnegative()
9667
+ }), {
9668
+ kind: "mutation",
9669
+ auth: "admin"
9670
+ }), object({
9625
9671
  deviceId: number().int().nonnegative(),
9626
9672
  camStreamId: string(),
9627
9673
  profile: CamProfileSchema
@@ -15577,6 +15623,30 @@ var TrackSourceSchema = _enum([
15577
15623
  "audio"
15578
15624
  ]);
15579
15625
  /**
15626
+ * Where a track sits in the RETRAIN lifecycle (D81).
15627
+ *
15628
+ * - `none` — never marked, or un-marked. Evictable.
15629
+ * - `staging` — the operator wants this track as training material and has not
15630
+ * finished with it. **This is the only state retention holds**: the track and
15631
+ * everything it owns (object events, crops, keyframes, CLIP vector) survive
15632
+ * the device's age window.
15633
+ * - `trained` — the retrain page has taken what it needed. The frames it chose
15634
+ * were COPIED into the retrain dataset at selection time, so the dataset no
15635
+ * longer depends on the track's media and the track becomes EVICTABLE again.
15636
+ * Terminal for the plain `markForTrain` toggle: returning it to `staging` is
15637
+ * a deliberate action of the retrain page, not a side effect of a checkbox.
15638
+ *
15639
+ * There is no `null`. The state is stored `TEXT NOT NULL DEFAULT 'none'` because
15640
+ * the store's filter language has only positive equality and `whereIn` — no
15641
+ * negation, no IS NULL — so a NULL would be unselectable by ANY predicate and
15642
+ * would make the entire pre-column history immortal in one deploy.
15643
+ */
15644
+ var RetrainStatusSchema = _enum([
15645
+ "none",
15646
+ "staging",
15647
+ "trained"
15648
+ ]);
15649
+ /**
15580
15650
  * Per-track OPERATOR flags — set by hand from the admin UI or the viewer, never
15581
15651
  * by the pipeline. Spread into `TrackSchema` and `KeyEventSchema` from one place
15582
15652
  * so the two surfaces cannot drift.
@@ -15586,18 +15656,31 @@ var TrackSourceSchema = _enum([
15586
15656
  * columns existed read as absent, and a consumer that needs a boolean should say
15587
15657
  * `flag === true`, not `flag !== false`.
15588
15658
  *
15589
- * What the flags DO is deliberately UNDEFINED at the time of writing: they are
15590
- * operator curation, and the behaviour they drive will be specified separately.
15591
- * In particular a `markForTrain` track is NOT pinned against retention — see
15592
- * `docs/decisions/adr-0059.md` for why that is a store-level change, not a flag.
15659
+ * `markForTrain` is the WIRE FACE of {@link RetrainStatusSchema}, not a column:
15660
+ * it is exactly `retrainStatus === 'staging'`, in both directions. Writing
15661
+ * `true` moves `none → staging`, writing `false` moves `staging → none`, and a
15662
+ * `trained` track reports `false` while refusing both writes. The boolean is
15663
+ * kept because three surfaces drive a toggle off it; anything that needs to tell
15664
+ * "never marked" from "already trained" must read `retrainStatus`.
15665
+ *
15666
+ * `debug` does NOT pin; it is attention, not durability.
15593
15667
  */
15594
15668
  var TrackFlagFields = {
15595
- /** Operator marked this track as training material. */
15669
+ /** Operator marked this track as training material — i.e. `retrainStatus` is
15670
+ * `'staging'`. */
15596
15671
  markForTrain: boolean().optional(),
15597
15672
  /** Operator marked this track for diagnostic attention. */
15598
15673
  debug: boolean().optional()
15599
15674
  };
15600
15675
  /**
15676
+ * The lifecycle field itself, on the READ surfaces only (`Track`, `KeyEvent`).
15677
+ * Deliberately NOT part of {@link TrackFlagFields}: that group also builds the
15678
+ * write patch, and the status is not something the toggle sets — it is what the
15679
+ * toggle's boolean is derived from. Absent on an in-RAM track never touched;
15680
+ * always present on a persisted row (the column default materialises `'none'`).
15681
+ */
15682
+ var TrackRetrainFields = { retrainStatus: RetrainStatusSchema.optional() };
15683
+ /**
15601
15684
  * The write half: a PARTIAL patch. An omitted key is left untouched, so setting
15602
15685
  * one flag can never clear the other — the toggles are independent and are
15603
15686
  * driven from three surfaces that do not know about each other.
@@ -15611,7 +15694,32 @@ var TrackFlagsPatchSchema = object(TrackFlagFields);
15611
15694
  var TrackFlagsSchema = object({
15612
15695
  trackId: string(),
15613
15696
  markForTrain: boolean(),
15614
- debug: boolean()
15697
+ debug: boolean(),
15698
+ /** The lifecycle state the boolean was derived from. Required here (unlike on
15699
+ * a track row) because this shape is only ever produced by the write body,
15700
+ * which always knows it — and a surface that has just written needs to render
15701
+ * `trained` without a re-fetch. */
15702
+ retrainStatus: RetrainStatusSchema
15703
+ });
15704
+ /** Per-camera slice of a training-export estimate. */
15705
+ var TrainingExportDeviceTotalsSchema = object({
15706
+ deviceId: number(),
15707
+ tracks: number().int(),
15708
+ files: number().int(),
15709
+ bytes: number().int()
15710
+ });
15711
+ /**
15712
+ * What a training export WOULD contain. Computed from media index rows only —
15713
+ * no blob is read to produce this.
15714
+ */
15715
+ var TrainingExportSummarySchema = object({
15716
+ generatedAt: number(),
15717
+ trackCount: number().int(),
15718
+ fileCount: number().int(),
15719
+ byteCount: number().int(),
15720
+ /** More marked tracks exist than a single pass carries. */
15721
+ truncated: boolean(),
15722
+ devices: array(TrainingExportDeviceTotalsSchema).readonly()
15615
15723
  });
15616
15724
  var TrackSchema = object({
15617
15725
  trackId: string(),
@@ -15656,7 +15764,8 @@ var TrackSchema = object({
15656
15764
  * Populated from the persisted envelope columns on historical reads;
15657
15765
  * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
15658
15766
  envelope: TrackEnvelopeSchema.optional(),
15659
- ...TrackFlagFields
15767
+ ...TrackFlagFields,
15768
+ ...TrackRetrainFields
15660
15769
  });
15661
15770
  var BaseEventFields = {
15662
15771
  id: string(),
@@ -15870,7 +15979,8 @@ var KeyEventSchema = object({
15870
15979
  bestEventId: string(),
15871
15980
  /** Track lifetime in ms (lastSeen - firstSeen). */
15872
15981
  windowMs: number().optional(),
15873
- ...TrackFlagFields
15982
+ ...TrackFlagFields,
15983
+ ...TrackRetrainFields
15874
15984
  });
15875
15985
  object({
15876
15986
  trackId: string(),
@@ -16232,11 +16342,29 @@ var pipelineAnalyticsCapability = {
16232
16342
  *
16233
16343
  * `auth: 'protected'` (the default), NOT `admin`: the viewer is an
16234
16344
  * authenticated non-admin surface and two of the three call sites are
16235
- * there. Revisit if a flag ever gains an effect that costs storage —
16236
- * `deleteTracks` next door is admin for exactly that reason.
16345
+ * there. The note that used to sit here said to revisit this the day a flag
16346
+ * gained an effect that costs storage, and D81 is that day — `markForTrain`
16347
+ * now pins. It STAYS protected, and the reason is that the alternative
16348
+ * makes the feature pointless: marking a track is something you do while
16349
+ * looking at it, on the surface you were already looking at it on, and that
16350
+ * surface is the viewer. What the storage cost gets instead is a BOUND — a
16351
+ * per-device pin budget enforced in the body, refusing a new pin past the
16352
+ * limit while always allowing un-marking. `deleteTracks` next door is still
16353
+ * admin, because destroying evidence and preserving it are not symmetric.
16237
16354
  *
16238
- * Returns the RESOLVED state of both flags (absent → `false`) so a caller
16239
- * can drive its toggle without a re-fetch. Rejects an unknown track.
16355
+ * `markForTrain` writes the retrain LIFECYCLE, not a boolean column: `true`
16356
+ * is `none → staging`, `false` is `staging → none`. A track already
16357
+ * `trained` refuses BOTH — its frames are copies inside the retrain dataset
16358
+ * and re-staging it from a generic toggle is how the same material gets
16359
+ * annotated twice under two ground truths. Returning a trained track to
16360
+ * staging is a deliberate action of the retrain page, which is also the only
16361
+ * thing that produces `trained` in the first place.
16362
+ *
16363
+ * Returns the RESOLVED state of both flags (absent → `false`) plus the
16364
+ * `retrainStatus` they were derived from, so a caller can drive its toggle
16365
+ * — and render a `trained` badge — without a re-fetch. Rejects an unknown
16366
+ * track, a new staging mark on a device already holding its full budget, and
16367
+ * any `markForTrain` write against a trained track.
16240
16368
  */
16241
16369
  setTrackFlags: method(object({
16242
16370
  /** Log/audit scope only — the trackId is globally unique on its own. */
@@ -16300,6 +16428,42 @@ var pipelineAnalyticsCapability = {
16300
16428
  kind: "query",
16301
16429
  auth: "admin"
16302
16430
  }),
16431
+ /**
16432
+ * The CHEAP QUESTION, asked before any media moves: how big is the dataset
16433
+ * the marked (`markForTrain`) tracks would produce?
16434
+ *
16435
+ * Answered from media INDEX rows only — key, kind, size, timestamp — so it
16436
+ * costs ~2 KB of reads per track and no blob reads at all. The measured harm
16437
+ * behind D56 was a bulk pass that read and base64'd every blob a track owned
16438
+ * before deciding anything, taking hub-main to 82 s busy out of 120; an
16439
+ * export is that same I/O shape, so it inherits the same discipline: know
16440
+ * the size, then decide.
16441
+ *
16442
+ * `truncated` reports that more marked tracks exist than one pass carries.
16443
+ * Empty `deviceIds` ⇒ every device that has marked tracks.
16444
+ */
16445
+ getTrainingExportSummary: method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
16446
+ kind: "query",
16447
+ auth: "admin"
16448
+ }),
16449
+ /**
16450
+ * Where to download the dataset archive.
16451
+ *
16452
+ * The BYTES do not come back through this cap — they come from the returned
16453
+ * data-plane URL, which streams a tar built entry by entry. A multi-gigabyte
16454
+ * archive base64'd through a unary RPC envelope would be held whole in
16455
+ * memory twice on a hub this repo has already OOM'd once (D9/D18 are the
16456
+ * same lesson about frames). `getDownloadUrl` on `recordingExport` is the
16457
+ * precedent, and this follows it deliberately.
16458
+ *
16459
+ * The archive contains a `manifest.json` FIRST, then the stored media
16460
+ * VERBATIM under `tracks/<deviceId>/<trackId>/…`. No crop is derived and no
16461
+ * model is run: a training set's pixels must be the pixels the pipeline saw.
16462
+ */
16463
+ getTrainingExportUrl: method(object({ deviceIds: array(number()).optional() }), object({ url: string() }), {
16464
+ kind: "query",
16465
+ auth: "admin"
16466
+ }),
16303
16467
  getEventMedia: method(object({
16304
16468
  eventId: string(),
16305
16469
  kind: MediaFileKindEnum.optional()
@@ -23132,6 +23296,173 @@ DeviceType.Camera, method(object({
23132
23296
  status: OsdStatusSchema
23133
23297
  });
23134
23298
  /**
23299
+ * `osd-manager` — the ORCHESTRATOR over the device-scope `osd` cap.
23300
+ *
23301
+ * The `osd` cap is the firmware contract: it probes a camera's overlay
23302
+ * SLOTS and writes literal text into one. It has no idea WHERE that text
23303
+ * comes from, and it must not — a driver that grew a "show the temperature
23304
+ * here" feature would grow it once per vendor.
23305
+ *
23306
+ * This cap owns the other half: a per-(camera, slot) BINDING that says
23307
+ * which value feeds the slot, how it is formatted, and under which
23308
+ * conditions it is shown at all. One addon renders every binding on every
23309
+ * camera, so a new source costs zero driver code.
23310
+ *
23311
+ * Three deliberate choices, each with a rejected alternative:
23312
+ *
23313
+ * 1. A source is `(capName, valuePath)` over the kernel's device
23314
+ * runtime-state mirror — NOT a closed enum of source kinds. Every
23315
+ * cap-keyed slice a device publishes is bindable the day the cap
23316
+ * ships. The rejected alternative (one enum member per source, with
23317
+ * a resolver branch each) is what makes "add the humidity too" a
23318
+ * code change.
23319
+ * 2. The display gate reuses `NcConditionsSchema` verbatim — the
23320
+ * notification centre's condition vocabulary — rather than a parallel
23321
+ * model. An operator who has learned one condition editor has learned
23322
+ * both.
23323
+ * 3. Because the renderer's facts are device STATE and not a detection
23324
+ * record, only a SUBSET of that vocabulary can be answered here.
23325
+ * `setSlotBinding` REJECTS the rest at write time (see
23326
+ * `getConditionSupport`). It does not accept-then-fail-closed: a
23327
+ * condition that can never be true renders a permanently blank
23328
+ * overlay, and a blank overlay looks exactly like a broken camera.
23329
+ */
23330
+ /** Where a slot's value comes from. */
23331
+ var OsdSourceSchema = discriminatedUnion("kind", [
23332
+ object({
23333
+ kind: literal("static"),
23334
+ text: string().max(64)
23335
+ }),
23336
+ object({
23337
+ kind: literal("clock"),
23338
+ /** Token pattern: `YYYY MM DD HH mm ss`. Everything else is literal. */
23339
+ pattern: string().min(1).max(32).default("HH:mm"),
23340
+ /** IANA zone. Omitted = the server's zone. */
23341
+ timezone: string().min(1).max(64).optional()
23342
+ }),
23343
+ object({
23344
+ kind: literal("device-state"),
23345
+ deviceId: number().int().optional(),
23346
+ capName: string().min(1).max(64),
23347
+ /** Dot path inside the slice, e.g. `detected`, `value`, `mode`. */
23348
+ valuePath: string().min(1).max(64)
23349
+ })
23350
+ ]);
23351
+ var OsdSlotBindingSchema = object({
23352
+ /** Off = the manager stops driving this slot. It does NOT clear it. */
23353
+ enabled: boolean().default(true),
23354
+ source: OsdSourceSchema,
23355
+ /** `${value}` and `${unit}` are substituted; every occurrence. */
23356
+ template: string().max(96).default("${value}"),
23357
+ /** Truncate with an ellipsis past this length. Absent = no limit. */
23358
+ maxCharacters: number().int().min(4).max(64).optional(),
23359
+ /**
23360
+ * Decimal places for a numeric value. `0` yields an integer — the
23361
+ * documented workaround for firmwares that reject `.` in overlay text.
23362
+ */
23363
+ maxDecimals: number().int().min(0).max(4).default(1),
23364
+ /** Appended via `${unit}`. The state mirror does not carry units. */
23365
+ unitLabel: string().max(8).optional(),
23366
+ /** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
23367
+ valueMap: record(string(), string()).optional(),
23368
+ /** Time windows in which the slot is shown. Absent = always. */
23369
+ schedule: NcScheduleSchema.optional(),
23370
+ /**
23371
+ * Display gate, in the notification centre's condition vocabulary.
23372
+ * Only the keys reported by `getConditionSupport` are accepted.
23373
+ */
23374
+ conditions: NcConditionsSchema.optional(),
23375
+ /** Rendered when the gate is closed or the value unreadable. Empty = hide. */
23376
+ fallbackText: string().max(64).default("")
23377
+ });
23378
+ /** One camera slot, as the operator sees it: firmware truth + our binding. */
23379
+ var OsdSlotViewSchema = object({
23380
+ slotId: string(),
23381
+ kind: OsdOverlayKindEnum,
23382
+ /** Firmware refuses text edits (a timestamp, the channel name). */
23383
+ readOnly: boolean(),
23384
+ cameraEnabled: boolean(),
23385
+ cameraText: string().optional(),
23386
+ binding: OsdSlotBindingSchema.nullable()
23387
+ });
23388
+ /**
23389
+ * What happened to one slot on one render pass. `unchanged` exists so the
23390
+ * operator can tell "we are driving this and the value is steady" from
23391
+ * "we never got there" — and so the loop can prove it is not rewriting
23392
+ * identical text to the camera every tick.
23393
+ */
23394
+ var OsdRenderOutcomeEnum = _enum([
23395
+ "written",
23396
+ "unchanged",
23397
+ "gated",
23398
+ "unreadable",
23399
+ "disabled",
23400
+ "unbound",
23401
+ "failed"
23402
+ ]);
23403
+ var OsdRenderResultSchema = object({
23404
+ slotId: string(),
23405
+ outcome: OsdRenderOutcomeEnum,
23406
+ /** The text the slot should carry. Empty = the slot is switched off. */
23407
+ text: string(),
23408
+ /** Why, whenever the outcome is not a plain write. Never silent. */
23409
+ reason: string().optional()
23410
+ });
23411
+ var OsdSourceValueTypeEnum = _enum([
23412
+ "number",
23413
+ "boolean",
23414
+ "string",
23415
+ "enum"
23416
+ ]);
23417
+ /**
23418
+ * One bindable value, derived from a cap's `runtimeState` schema — never
23419
+ * hand-listed. The editor renders from this, so a cap that ships a new
23420
+ * state field becomes bindable with no UI change.
23421
+ */
23422
+ var OsdSourceOptionSchema = object({
23423
+ deviceId: number().int(),
23424
+ deviceName: string(),
23425
+ capName: string(),
23426
+ valuePath: string(),
23427
+ label: string(),
23428
+ valueType: OsdSourceValueTypeEnum,
23429
+ /** Present for `enum`; the editor offers these as `valueMap` keys. */
23430
+ enumValues: array(string()).readonly().optional()
23431
+ });
23432
+ method(object({ deviceId: number().int() }), object({
23433
+ supported: boolean(),
23434
+ slots: array(OsdSlotViewSchema)
23435
+ }), { auth: "admin" }), method(object({ deviceId: number().int() }), object({ sources: array(OsdSourceOptionSchema) }), { auth: "admin" }), method(object({}), object({
23436
+ supported: array(string()),
23437
+ catalog: array(NcConditionDescriptorSchema)
23438
+ }), { auth: "admin" }), method(object({
23439
+ deviceId: number().int(),
23440
+ slotId: string().min(1),
23441
+ binding: OsdSlotBindingSchema
23442
+ }), object({
23443
+ slot: OsdSlotViewSchema,
23444
+ render: OsdRenderResultSchema
23445
+ }), {
23446
+ kind: "mutation",
23447
+ auth: "admin"
23448
+ }), method(object({
23449
+ deviceId: number().int(),
23450
+ slotId: string().min(1)
23451
+ }), object({ success: literal(true) }), {
23452
+ kind: "mutation",
23453
+ auth: "admin"
23454
+ }), method(object({
23455
+ deviceId: number().int(),
23456
+ slotId: string().min(1),
23457
+ binding: OsdSlotBindingSchema.optional()
23458
+ }), OsdRenderResultSchema, {
23459
+ kind: "mutation",
23460
+ auth: "admin"
23461
+ }), method(object({ deviceId: number().int() }), object({ results: array(OsdRenderResultSchema) }), {
23462
+ kind: "mutation",
23463
+ auth: "admin"
23464
+ });
23465
+ /**
23135
23466
  * Feeder connectivity / power status — mirrors the HA petkit device-status
23136
23467
  * enum: `normal` (online, mains), `offline` (not reaching PetKit cloud),
23137
23468
  * `on_batteries` (running on battery backup). `null` until first reported.
@@ -29632,6 +29963,48 @@ Object.freeze({
29632
29963
  addonId: null,
29633
29964
  access: "create"
29634
29965
  },
29966
+ "osdManager.clearSlotBinding": {
29967
+ capName: "osd-manager",
29968
+ capScope: "system",
29969
+ addonId: null,
29970
+ access: "delete"
29971
+ },
29972
+ "osdManager.getConditionSupport": {
29973
+ capName: "osd-manager",
29974
+ capScope: "system",
29975
+ addonId: null,
29976
+ access: "view"
29977
+ },
29978
+ "osdManager.getDeviceOsd": {
29979
+ capName: "osd-manager",
29980
+ capScope: "system",
29981
+ addonId: null,
29982
+ access: "view"
29983
+ },
29984
+ "osdManager.getSourceCatalog": {
29985
+ capName: "osd-manager",
29986
+ capScope: "system",
29987
+ addonId: null,
29988
+ access: "view"
29989
+ },
29990
+ "osdManager.previewSlot": {
29991
+ capName: "osd-manager",
29992
+ capScope: "system",
29993
+ addonId: null,
29994
+ access: "create"
29995
+ },
29996
+ "osdManager.renderDevice": {
29997
+ capName: "osd-manager",
29998
+ capScope: "system",
29999
+ addonId: null,
30000
+ access: "create"
30001
+ },
30002
+ "osdManager.setSlotBinding": {
30003
+ capName: "osd-manager",
30004
+ capScope: "system",
30005
+ addonId: null,
30006
+ access: "create"
30007
+ },
29635
30008
  "petFeeder.callPet": {
29636
30009
  capName: "pet-feeder",
29637
30010
  capScope: "device",
@@ -29794,6 +30167,18 @@ Object.freeze({
29794
30167
  addonId: null,
29795
30168
  access: "view"
29796
30169
  },
30170
+ "pipelineAnalytics.getTrainingExportSummary": {
30171
+ capName: "pipeline-analytics",
30172
+ capScope: "device",
30173
+ addonId: null,
30174
+ access: "view"
30175
+ },
30176
+ "pipelineAnalytics.getTrainingExportUrl": {
30177
+ capName: "pipeline-analytics",
30178
+ capScope: "device",
30179
+ addonId: null,
30180
+ access: "view"
30181
+ },
29797
30182
  "pipelineAnalytics.listEventKinds": {
29798
30183
  capName: "pipeline-analytics",
29799
30184
  capScope: "device",
@@ -31270,6 +31655,12 @@ Object.freeze({
31270
31655
  addonId: null,
31271
31656
  access: "view"
31272
31657
  },
31658
+ "streamBroker.getDeviceAudioMute": {
31659
+ capName: "stream-broker",
31660
+ capScope: "system",
31661
+ addonId: null,
31662
+ access: "view"
31663
+ },
31273
31664
  "streamBroker.getPreBufferInfo": {
31274
31665
  capName: "stream-broker",
31275
31666
  capScope: "system",
@@ -31390,6 +31781,12 @@ Object.freeze({
31390
31781
  addonId: null,
31391
31782
  access: "create"
31392
31783
  },
31784
+ "streamBroker.setDeviceAudioMute": {
31785
+ capName: "stream-broker",
31786
+ capScope: "system",
31787
+ addonId: null,
31788
+ access: "create"
31789
+ },
31393
31790
  "streamBroker.setPreBufferDuration": {
31394
31791
  capName: "stream-broker",
31395
31792
  capScope: "system",
@@ -32037,6 +32434,66 @@ Object.freeze({
32037
32434
  "smtp-provider": "email"
32038
32435
  });
32039
32436
  new Set(["devices", "classes"]);
32437
+ var WEEKDAY_TO_DAY = {
32438
+ Sun: 0,
32439
+ Mon: 1,
32440
+ Tue: 2,
32441
+ Wed: 3,
32442
+ Thu: 4,
32443
+ Fri: 5,
32444
+ Sat: 6
32445
+ };
32446
+ /** Resolve (weekday, minute-of-day) of `atMs` in the schedule's timezone.
32447
+ * An invalid/unknown IANA name falls back to the host timezone. */
32448
+ function localDayMinute(atMs, timezone) {
32449
+ const d = new Date(atMs);
32450
+ if (timezone !== void 0) try {
32451
+ const parts = new Intl.DateTimeFormat("en-US", {
32452
+ timeZone: timezone,
32453
+ weekday: "short",
32454
+ hour: "numeric",
32455
+ minute: "numeric",
32456
+ hourCycle: "h23"
32457
+ }).formatToParts(d);
32458
+ let weekday;
32459
+ let hour;
32460
+ let minute;
32461
+ for (const p of parts) if (p.type === "weekday") weekday = p.value;
32462
+ else if (p.type === "hour") hour = Number(p.value);
32463
+ else if (p.type === "minute") minute = Number(p.value);
32464
+ const day = weekday !== void 0 ? WEEKDAY_TO_DAY[weekday] : void 0;
32465
+ if (day !== void 0 && hour !== void 0 && minute !== void 0) return {
32466
+ day,
32467
+ minute: hour * 60 + minute
32468
+ };
32469
+ } catch {}
32470
+ return {
32471
+ day: d.getDay(),
32472
+ minute: d.getHours() * 60 + d.getMinutes()
32473
+ };
32474
+ }
32475
+ /**
32476
+ * Is the schedule active at `atMs`? No schedule = always active. Windows
32477
+ * are OR'd; a window with `startMinute > endMinute` crosses midnight (it
32478
+ * starts on a listed day and spills into the next). `invert` flips the
32479
+ * result (active OUTSIDE the windows).
32480
+ */
32481
+ function isScheduleActive(schedule, atMs) {
32482
+ if (schedule === void 0) return true;
32483
+ const { day, minute } = localDayMinute(atMs, schedule.timezone);
32484
+ const prevDay = (day + 6) % 7;
32485
+ let inside = false;
32486
+ for (const w of schedule.windows) if (w.startMinute <= w.endMinute) {
32487
+ if (w.days.includes(day) && minute >= w.startMinute && minute < w.endMinute) {
32488
+ inside = true;
32489
+ break;
32490
+ }
32491
+ } else if (w.days.includes(day) && minute >= w.startMinute || w.days.includes(prevDay) && minute < w.endMinute) {
32492
+ inside = true;
32493
+ break;
32494
+ }
32495
+ return schedule.invert === true ? !inside : inside;
32496
+ }
32040
32497
  /**
32041
32498
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
32042
32499
  *
@@ -32393,6 +32850,12 @@ Object.defineProperty(exports, "OpsLogEntrySchema", {
32393
32850
  return OpsLogEntrySchema;
32394
32851
  }
32395
32852
  });
32853
+ Object.defineProperty(exports, "RetrainStatusSchema", {
32854
+ enumerable: true,
32855
+ get: function() {
32856
+ return RetrainStatusSchema;
32857
+ }
32858
+ });
32396
32859
  Object.defineProperty(exports, "TimelapseRuleInputSchema", {
32397
32860
  enumerable: true,
32398
32861
  get: function() {
@@ -32525,6 +32988,12 @@ Object.defineProperty(exports, "isDeviceScopedCap", {
32525
32988
  return isDeviceScopedCap;
32526
32989
  }
32527
32990
  });
32991
+ Object.defineProperty(exports, "isScheduleActive", {
32992
+ enumerable: true,
32993
+ get: function() {
32994
+ return isScheduleActive;
32995
+ }
32996
+ });
32528
32997
  Object.defineProperty(exports, "kebabToCamel", {
32529
32998
  enumerable: true,
32530
32999
  get: function() {
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BaqsyWbd.js");
5
+ const require_dist = require("../dist-Bm5GpbGn.js");
6
6
  let node_fs = require("node:fs");
7
7
  let node_fs$1 = require_dist.__toESM(node_fs, 1);
8
8
  node_fs = require_dist.__toESM(node_fs);
@@ -1,4 +1,4 @@
1
- import { A as hfModelUrl, D as embeddingEncoderCapability, V as BaseAddon } from "../dist-DYOxxL_e.mjs";
1
+ import { O as embeddingEncoderCapability, U as BaseAddon, j as hfModelUrl } from "../dist-BhadCxSQ.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import * as fs from "node:fs";
4
4
  import "node:fs";
@@ -1,5 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path$1 from "node:path";
3
+ import "node:crypto";
3
4
  import "node:child_process";
4
5
  var STORAGE_LOCATION_TYPES = [
5
6
  "data",
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-BaqsyWbd.js");
1
+ const require_dist = require("./dist-Bm5GpbGn.js");
2
2
  let node_fs = require("node:fs");
3
3
  node_fs = require_dist.__toESM(node_fs, 1);
4
4
  let node_path = require("node:path");
@@ -1,6 +1,6 @@
1
1
  import { a as e, i as t, n, o as r, r as i, t as a } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare__react__loadShare__.js-C0AuF9av.mjs";
2
2
  import { t as o } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-B3Wx5J80.mjs";
3
- import { a as s, i as c, n as l, o as u, r as d, s as f, t as p } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CiIJt_s8.mjs";
3
+ import { a as s, i as c, n as l, o as u, r as d, s as f, t as p } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CJ3AVT6B.mjs";
4
4
  import { n as m, r as h, t as g } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-Bm-iyjmq.mjs";
5
5
  //#region ../../node_modules/lucide-react/dist/esm/shared/src/utils.js
6
6
  var _ = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), v = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), y = (e) => {