@camstack/addon-osd-manager 0.1.39 → 0.1.40

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 (18) hide show
  1. package/dist/{MotionZonesSettings-CIXOIaxJ.mjs → MotionZonesSettings-BTOqeXRd.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-BXVb51kq.mjs → PrivacyMaskSettings-BgmE0tQu.mjs} +4 -4
  3. package/dist/{SceneMonitorEditor-DYRLvvVA.mjs → SceneMonitorEditor-D4oig7v9.mjs} +3 -3
  4. package/dist/_stub.js +11 -11
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-BfqPLLgE.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-C4iHNnmw.mjs} +4 -4
  6. package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Blhr4EyN.mjs +26 -0
  7. package/dist/{hostInit-C5XnMBDh.mjs → hostInit-DzLQY4CO.mjs} +3 -3
  8. package/dist/index.js +309 -207
  9. package/dist/index.mjs +309 -207
  10. package/dist/{player-overlays-BuhAo_Es.mjs → player-overlays-DaucHym7.mjs} +1 -1
  11. package/dist/remoteEntry.js +1 -1
  12. package/dist/{responsive-BAE2Alvc.mjs → responsive-DC2YNF2r.mjs} +1 -1
  13. package/dist/{square-7AGHps0Y.mjs → square-DuMw_5RG.mjs} +1 -1
  14. package/dist/{trash-2-CeWvoLug.mjs → trash-2-C54qifas.mjs} +1 -1
  15. package/dist/{use-device-snapshot-DW8FBhRd.mjs → use-device-snapshot-BVTu9rWV.mjs} +1 -1
  16. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-hPck03Nc.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-B-XuFKdU.mjs} +1 -1
  17. package/package.json +1 -1
  18. package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CnoMYUFR.mjs +0 -26
package/dist/index.js CHANGED
@@ -14728,6 +14728,133 @@ var embeddingEncoderCapability = {
14728
14728
  }
14729
14729
  };
14730
14730
  /**
14731
+ * `failure-contribution` — the capability an addon reports its OWN losses
14732
+ * through, per camera, with the denominator attached. It stores nothing.
14733
+ *
14734
+ * ## The twin of `load-contribution`, and why it is a twin and not a field
14735
+ *
14736
+ * `load-contribution` answers *what did this camera COST*. This answers *what
14737
+ * did this camera LOSE*. The reporting discipline is identical and deliberately
14738
+ * copied: the contributor reports what it already knows, hub-main adds only
14739
+ * `addonId`, nothing needs global knowledge, and there is no central list for
14740
+ * somebody to forget to edit.
14741
+ *
14742
+ * They are not merged, because their invariants are opposites:
14743
+ *
14744
+ * - a `load-contribution` measurement is **absent, never zero** — a zero would
14745
+ * claim a camera cost nothing, which is a measurement nobody made;
14746
+ * - a `failure-contribution` zero is the **most valuable value on the
14747
+ * surface** — `attempts: 400, succeeded: 400` is the proof a fix landed,
14748
+ * and it is exactly what an absent entry cannot say.
14749
+ *
14750
+ * Putting a loss counter on a cost entry would also break the reconciliation
14751
+ * that gives `load-contribution` its point: contributions are subtracted from
14752
+ * `metrics.node-processes-snapshot` to find processes nobody claims. A failure
14753
+ * has no process.
14754
+ *
14755
+ * ## Why not a log line, since the counters already exist
14756
+ *
14757
+ * Several of these paths already counted themselves — `CaptureScheduler`'s
14758
+ * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
14759
+ * ends in a log line, and a log line is the thing the operator asked to stop
14760
+ * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
14761
+ * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
14762
+ * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
14763
+ * media blackout were both diagnosed. The counters stay; this is where they can
14764
+ * be READ.
14765
+ *
14766
+ * ## The rate is served with its denominator or not at all
14767
+ *
14768
+ * Every entry carries `attempts` and `succeeded`. A miss count alone is
14769
+ * unreadable: on 2026-08-28 the enrichment-crop miss count read as "35x worse
14770
+ * than yesterday" and was **flat across twelve hours** once divided by the
14771
+ * successes on the same path. A surface that publishes only the numerator
14772
+ * reproduces that mistake on every read.
14773
+ *
14774
+ * ## Shape
14775
+ *
14776
+ * Copied from `load-contribution.cap.ts` (`mode: 'collection'`,
14777
+ * `internal: true`, `mount: { kind: 'skip' }`): no tRPC route of its own and no
14778
+ * generated hooks, while `addons.listCapabilityProviders` still enumerates it
14779
+ * and the hub's `CapabilityRegistry` still holds an RPC proxy per provider — so
14780
+ * a forked runner's entries reach hub-main over transport that already exists.
14781
+ * No new UDS message, no second registry (D3). The operator reads the assembled
14782
+ * result through `system.getFailureContributions`.
14783
+ */
14784
+ var FailureReasonCountSchema = object({
14785
+ /**
14786
+ * Why the attempt did not land, in the contributor's own vocabulary —
14787
+ * `worker-lease-gone`, `queue-overflow`, `timeout`, `empty-read`. The same
14788
+ * strings that already appear in this repo's logs and, where one exists, the
14789
+ * same string the per-track `previewMissReason` records (D276): a second
14790
+ * vocabulary for the same loss would make the row and the counter
14791
+ * un-joinable.
14792
+ */
14793
+ reason: string(),
14794
+ count: number().int().nonnegative()
14795
+ });
14796
+ var FailureContributionSchema = object({
14797
+ /**
14798
+ * The failing path — `enrichment-crop`, `inference`, `plate-ocr`,
14799
+ * `person-over-vehicle`. Free text, for the reason `load-contribution` keeps
14800
+ * `unit` free: the families are owned by different addons and a shared enum
14801
+ * is a central list that rots invisibly.
14802
+ */
14803
+ family: string(),
14804
+ /**
14805
+ * The NUMERIC device id — the same value every log line carries as
14806
+ * `tags.deviceId`. Never nullable and never absent: a contributor that
14807
+ * cannot name the camera must not emit the entry, because a fleet total
14808
+ * cannot answer the only question anybody asks of this surface.
14809
+ */
14810
+ deviceId: number().int().positive(),
14811
+ /**
14812
+ * A second dimension inside the family: the model / step id for an inference
14813
+ * timeout, so "which camera AND which model" is one read. Absent when the
14814
+ * family has a single variant.
14815
+ */
14816
+ variant: string().optional(),
14817
+ /**
14818
+ * Epoch ms this counter started — the INCARNATION MARKER. A consumer
14819
+ * differencing two reads must drop the interval when it changes, because the
14820
+ * counter restarted from zero in a respawned runner. Same discipline as
14821
+ * `LoadContribution.startedAtMs`.
14822
+ */
14823
+ sinceMs: number(),
14824
+ /** Epoch ms it was read. `atMs - sinceMs` is the interval this covers. */
14825
+ atMs: number(),
14826
+ /**
14827
+ * THE DENOMINATOR — every attempt on this path for this camera in the
14828
+ * window. A failure count published without it is the mistake this schema
14829
+ * exists to make impossible.
14830
+ */
14831
+ attempts: number().int().nonnegative(),
14832
+ /** Attempts that landed. `attempts - succeeded` is the loss. */
14833
+ succeeded: number().int().nonnegative(),
14834
+ /** The loss, partitioned. Sums to `attempts - succeeded`. */
14835
+ reasons: array(FailureReasonCountSchema).readonly()
14836
+ });
14837
+ var failureContributionCapability = {
14838
+ name: "failure-contribution",
14839
+ scope: "system",
14840
+ mode: "collection",
14841
+ internal: true,
14842
+ methods: {
14843
+ /**
14844
+ * This addon's per-camera failure counters, read live from bounded in-RAM
14845
+ * state it already keeps. Inert: no persistence, no sampling, no timer.
14846
+ *
14847
+ * READING NEVER RESETS. The counters are CUMULATIVE since `sinceMs`, and a
14848
+ * consumer that wants a rate differences two reads. A draining read would
14849
+ * make two operators with the page open each destroy half of the other's
14850
+ * numbers, and `load-contribution` already settled the same question the
14851
+ * same way for `cpuSeconds`.
14852
+ */
14853
+ list: method(_void(), array(FailureContributionSchema).readonly()) },
14854
+ /** In-process only — enumerated through `addons.listCapabilityProviders`. */
14855
+ mount: { kind: "skip" }
14856
+ };
14857
+ /**
14731
14858
  * filesystem-browse — per-node capability for browsing the node's local
14732
14859
  * filesystem. Reads are unconfined (whole filesystem, from `/` down); WRITES
14733
14860
  * are sandboxed to operator-configured allowed roots (D115). Used by the
@@ -15338,6 +15465,82 @@ var llmCapability = {
15338
15465
  })
15339
15466
  }
15340
15467
  };
15468
+ var LoadContributionSchema = object({
15469
+ role: _enum([
15470
+ "decode",
15471
+ "transcode",
15472
+ "recording",
15473
+ "streaming",
15474
+ "detection"
15475
+ ]),
15476
+ /**
15477
+ * The NUMERIC device id — the same value every log line carries as
15478
+ * `tags.deviceId`. `null` means this cost genuinely belongs to no single
15479
+ * camera (a shared pool), NOT that the contributor forgot to look it up: a
15480
+ * contributor that cannot name its camera must not emit the entry at all,
15481
+ * because an unnamed per-camera entry is indistinguishable from a shared one
15482
+ * and would quietly turn one camera's cost into everybody's.
15483
+ */
15484
+ deviceId: number().int().positive().nullable(),
15485
+ attribution: _enum([
15486
+ "measured",
15487
+ "accounted",
15488
+ "unattributable"
15489
+ ]),
15490
+ /**
15491
+ * What ONE entry is, in the contributor's own words — `615/high`,
15492
+ * `617/native`, `cuda:0 shared pool`. Free text because the unit differs per
15493
+ * family and inventing a common one would lose the only information that
15494
+ * makes two entries for the same camera distinguishable.
15495
+ */
15496
+ unit: string(),
15497
+ /**
15498
+ * The OS process this cost lives in, when there is one. Present so a
15499
+ * consumer can (a) tell two generations of the same unit apart across a
15500
+ * restart, and (b) subtract claimed processes from the node's process
15501
+ * snapshot to see what NOBODY claimed. Absent for an entry that owns no
15502
+ * process of its own.
15503
+ */
15504
+ pid: number().int().positive().optional(),
15505
+ /**
15506
+ * When this generation started. The pid's incarnation marker: a consumer
15507
+ * differencing {@link LoadContributionSchema.shape.cpuSeconds} must drop the
15508
+ * window when this changes, because the counter restarted from zero in a new
15509
+ * process.
15510
+ */
15511
+ startedAtMs: number().optional(),
15512
+ /**
15513
+ * CUMULATIVE CPU seconds this unit has consumed since it started — user +
15514
+ * system, read from the child's own `/proc/<pid>/stat` at the moment the
15515
+ * contribution is asked for.
15516
+ *
15517
+ * Cumulative and not a rate on purpose: a rate needs a window, a window
15518
+ * needs a sampler, and a new per-node sampler is the defect half of
15519
+ * `docs/architecture/load-ledger.md` documents. A counter can be differenced
15520
+ * by whoever already keeps a history; a rate cannot be un-averaged.
15521
+ *
15522
+ * Absent — never zero — on a node with no `/proc`, on a read failure, and on
15523
+ * an entry with no process.
15524
+ */
15525
+ cpuSeconds: number().optional(),
15526
+ /** Resident bytes of this unit's process, same source and same rules. */
15527
+ rssBytes: number().optional()
15528
+ });
15529
+ var loadContributionCapability = {
15530
+ name: "load-contribution",
15531
+ scope: "system",
15532
+ mode: "collection",
15533
+ internal: true,
15534
+ methods: {
15535
+ /**
15536
+ * This addon's own cost entries, computed live from state it already
15537
+ * holds. Inert: no persistence, no sampling, no timer. It is answered on
15538
+ * whatever beat the caller already has.
15539
+ */
15540
+ list: method(_void(), array(LoadContributionSchema).readonly()) },
15541
+ /** In-process only — enumerated through `addons.listCapabilityProviders`. */
15542
+ mount: { kind: "skip" }
15543
+ };
15341
15544
  /**
15342
15545
  * `log-channels` — the capability an addon DECLARES its diagnostic channels
15343
15546
  * through. It stores nothing.
@@ -15446,209 +15649,6 @@ var logDestinationCapability = {
15446
15649
  mount: { kind: "skip" }
15447
15650
  };
15448
15651
  /**
15449
- * `failure-contribution` — the capability an addon reports its OWN losses
15450
- * through, per camera, with the denominator attached. It stores nothing.
15451
- *
15452
- * ## The twin of `load-contribution`, and why it is a twin and not a field
15453
- *
15454
- * `load-contribution` answers *what did this camera COST*. This answers *what
15455
- * did this camera LOSE*. The reporting discipline is identical and deliberately
15456
- * copied: the contributor reports what it already knows, hub-main adds only
15457
- * `addonId`, nothing needs global knowledge, and there is no central list for
15458
- * somebody to forget to edit.
15459
- *
15460
- * They are not merged, because their invariants are opposites:
15461
- *
15462
- * - a `load-contribution` measurement is **absent, never zero** — a zero would
15463
- * claim a camera cost nothing, which is a measurement nobody made;
15464
- * - a `failure-contribution` zero is the **most valuable value on the
15465
- * surface** — `attempts: 400, succeeded: 400` is the proof a fix landed,
15466
- * and it is exactly what an absent entry cannot say.
15467
- *
15468
- * Putting a loss counter on a cost entry would also break the reconciliation
15469
- * that gives `load-contribution` its point: contributions are subtracted from
15470
- * `metrics.node-processes-snapshot` to find processes nobody claims. A failure
15471
- * has no process.
15472
- *
15473
- * ## Why not a log line, since the counters already exist
15474
- *
15475
- * Several of these paths already counted themselves — `CaptureScheduler`'s
15476
- * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
15477
- * ends in a log line, and a log line is the thing the operator asked to stop
15478
- * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
15479
- * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
15480
- * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
15481
- * media blackout were both diagnosed. The counters stay; this is where they can
15482
- * be READ.
15483
- *
15484
- * ## The rate is served with its denominator or not at all
15485
- *
15486
- * Every entry carries `attempts` and `succeeded`. A miss count alone is
15487
- * unreadable: on 2026-08-28 the enrichment-crop miss count read as "35x worse
15488
- * than yesterday" and was **flat across twelve hours** once divided by the
15489
- * successes on the same path. A surface that publishes only the numerator
15490
- * reproduces that mistake on every read.
15491
- *
15492
- * ## Shape
15493
- *
15494
- * Copied from `load-contribution.cap.ts` (`mode: 'collection'`,
15495
- * `internal: true`, `mount: { kind: 'skip' }`): no tRPC route of its own and no
15496
- * generated hooks, while `addons.listCapabilityProviders` still enumerates it
15497
- * and the hub's `CapabilityRegistry` still holds an RPC proxy per provider — so
15498
- * a forked runner's entries reach hub-main over transport that already exists.
15499
- * No new UDS message, no second registry (D3). The operator reads the assembled
15500
- * result through `system.getFailureContributions`.
15501
- */
15502
- var FailureReasonCountSchema = object({
15503
- /**
15504
- * Why the attempt did not land, in the contributor's own vocabulary —
15505
- * `worker-lease-gone`, `queue-overflow`, `timeout`, `empty-read`. The same
15506
- * strings that already appear in this repo's logs and, where one exists, the
15507
- * same string the per-track `previewMissReason` records (D276): a second
15508
- * vocabulary for the same loss would make the row and the counter
15509
- * un-joinable.
15510
- */
15511
- reason: string(),
15512
- count: number().int().nonnegative()
15513
- });
15514
- var FailureContributionSchema = object({
15515
- /**
15516
- * The failing path — `enrichment-crop`, `inference`, `plate-ocr`,
15517
- * `person-over-vehicle`. Free text, for the reason `load-contribution` keeps
15518
- * `unit` free: the families are owned by different addons and a shared enum
15519
- * is a central list that rots invisibly.
15520
- */
15521
- family: string(),
15522
- /**
15523
- * The NUMERIC device id — the same value every log line carries as
15524
- * `tags.deviceId`. Never nullable and never absent: a contributor that
15525
- * cannot name the camera must not emit the entry, because a fleet total
15526
- * cannot answer the only question anybody asks of this surface.
15527
- */
15528
- deviceId: number().int().positive(),
15529
- /**
15530
- * A second dimension inside the family: the model / step id for an inference
15531
- * timeout, so "which camera AND which model" is one read. Absent when the
15532
- * family has a single variant.
15533
- */
15534
- variant: string().optional(),
15535
- /**
15536
- * Epoch ms this counter started — the INCARNATION MARKER. A consumer
15537
- * differencing two reads must drop the interval when it changes, because the
15538
- * counter restarted from zero in a respawned runner. Same discipline as
15539
- * `LoadContribution.startedAtMs`.
15540
- */
15541
- sinceMs: number(),
15542
- /** Epoch ms it was read. `atMs - sinceMs` is the interval this covers. */
15543
- atMs: number(),
15544
- /**
15545
- * THE DENOMINATOR — every attempt on this path for this camera in the
15546
- * window. A failure count published without it is the mistake this schema
15547
- * exists to make impossible.
15548
- */
15549
- attempts: number().int().nonnegative(),
15550
- /** Attempts that landed. `attempts - succeeded` is the loss. */
15551
- succeeded: number().int().nonnegative(),
15552
- /** The loss, partitioned. Sums to `attempts - succeeded`. */
15553
- reasons: array(FailureReasonCountSchema).readonly()
15554
- });
15555
- var failureContributionCapability = {
15556
- name: "failure-contribution",
15557
- scope: "system",
15558
- mode: "collection",
15559
- internal: true,
15560
- methods: {
15561
- /**
15562
- * This addon's per-camera failure counters, read live from bounded in-RAM
15563
- * state it already keeps. Inert: no persistence, no sampling, no timer.
15564
- *
15565
- * READING NEVER RESETS. The counters are CUMULATIVE since `sinceMs`, and a
15566
- * consumer that wants a rate differences two reads. A draining read would
15567
- * make two operators with the page open each destroy half of the other's
15568
- * numbers, and `load-contribution` already settled the same question the
15569
- * same way for `cpuSeconds`.
15570
- */
15571
- list: method(_void(), array(FailureContributionSchema).readonly()) },
15572
- /** In-process only — enumerated through `addons.listCapabilityProviders`. */
15573
- mount: { kind: "skip" }
15574
- };
15575
- var LoadContributionSchema = object({
15576
- role: _enum([
15577
- "decode",
15578
- "transcode",
15579
- "recording",
15580
- "streaming",
15581
- "detection"
15582
- ]),
15583
- /**
15584
- * The NUMERIC device id — the same value every log line carries as
15585
- * `tags.deviceId`. `null` means this cost genuinely belongs to no single
15586
- * camera (a shared pool), NOT that the contributor forgot to look it up: a
15587
- * contributor that cannot name its camera must not emit the entry at all,
15588
- * because an unnamed per-camera entry is indistinguishable from a shared one
15589
- * and would quietly turn one camera's cost into everybody's.
15590
- */
15591
- deviceId: number().int().positive().nullable(),
15592
- attribution: _enum([
15593
- "measured",
15594
- "accounted",
15595
- "unattributable"
15596
- ]),
15597
- /**
15598
- * What ONE entry is, in the contributor's own words — `615/high`,
15599
- * `617/native`, `cuda:0 shared pool`. Free text because the unit differs per
15600
- * family and inventing a common one would lose the only information that
15601
- * makes two entries for the same camera distinguishable.
15602
- */
15603
- unit: string(),
15604
- /**
15605
- * The OS process this cost lives in, when there is one. Present so a
15606
- * consumer can (a) tell two generations of the same unit apart across a
15607
- * restart, and (b) subtract claimed processes from the node's process
15608
- * snapshot to see what NOBODY claimed. Absent for an entry that owns no
15609
- * process of its own.
15610
- */
15611
- pid: number().int().positive().optional(),
15612
- /**
15613
- * When this generation started. The pid's incarnation marker: a consumer
15614
- * differencing {@link LoadContributionSchema.shape.cpuSeconds} must drop the
15615
- * window when this changes, because the counter restarted from zero in a new
15616
- * process.
15617
- */
15618
- startedAtMs: number().optional(),
15619
- /**
15620
- * CUMULATIVE CPU seconds this unit has consumed since it started — user +
15621
- * system, read from the child's own `/proc/<pid>/stat` at the moment the
15622
- * contribution is asked for.
15623
- *
15624
- * Cumulative and not a rate on purpose: a rate needs a window, a window
15625
- * needs a sampler, and a new per-node sampler is the defect half of
15626
- * `docs/architecture/load-ledger.md` documents. A counter can be differenced
15627
- * by whoever already keeps a history; a rate cannot be un-averaged.
15628
- *
15629
- * Absent — never zero — on a node with no `/proc`, on a read failure, and on
15630
- * an entry with no process.
15631
- */
15632
- cpuSeconds: number().optional(),
15633
- /** Resident bytes of this unit's process, same source and same rules. */
15634
- rssBytes: number().optional()
15635
- });
15636
- var loadContributionCapability = {
15637
- name: "load-contribution",
15638
- scope: "system",
15639
- mode: "collection",
15640
- internal: true,
15641
- methods: {
15642
- /**
15643
- * This addon's own cost entries, computed live from state it already
15644
- * holds. Inert: no persistence, no sampling, no timer. It is answered on
15645
- * whatever beat the caller already has.
15646
- */
15647
- list: method(_void(), array(LoadContributionSchema).readonly()) },
15648
- /** In-process only — enumerated through `addons.listCapabilityProviders`. */
15649
- mount: { kind: "skip" }
15650
- };
15651
- /**
15652
15652
  * `login-method` — collection cap through which auth addons contribute
15653
15653
  * their pre-auth login surfaces to the login page. This is the SINGLE,
15654
15654
  * generic mechanism that supersedes the dead `auth.listProviders` reader:
@@ -21308,12 +21308,53 @@ var MediaFileKindEnum = _enum([
21308
21308
  "keyFrameSmall",
21309
21309
  "thumbnailSmall"
21310
21310
  ]);
21311
+ /**
21312
+ * One media row ON THE WIRE: what it is, how big it is, and WHERE ITS BYTES
21313
+ * ARE — never the bytes themselves.
21314
+ *
21315
+ * ## Why `url` and not `base64`
21316
+ *
21317
+ * Measured on the live hub 2026-08-30: `getTrackMedia {trackId, deviceId}`
21318
+ * with no `kinds` returned 6 rows / **3 597 219 B**, of which `keyFrame` alone
21319
+ * was **2 824 077 B** — one full-resolution frame, base64, so +33 % on the
21320
+ * wire. Forty events is ~144 MB. Every byte of it was read off disk,
21321
+ * base64-encoded, held whole in a unary tRPC envelope, and materialised in
21322
+ * hub-main's heap on the way past — for an `<img>` that would have cached it.
21323
+ *
21324
+ * `url` points at the `event-media` data plane
21325
+ * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
21326
+ * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
21327
+ * render a `?variant=thumb`, and streams. The hub gate in front of it requires
21328
+ * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
21329
+ * no less protected than they were inside a `view`-level cap response — see
21330
+ * `data-plane-access.ts` for the rule and the one gap it does not close
21331
+ * (per-device scoping).
21332
+ *
21333
+ * The URL is built from the row's **stored** key, which is not always its
21334
+ * published `kind`: a track's face/plate crop is stored as `crop` under
21335
+ * `('face'|'plate', '<prefix>-<trackId>')` and published as
21336
+ * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
21337
+ *
21338
+ * ## `base64` is TRANSITIONAL and is going away
21339
+ *
21340
+ * It is still populated for one reason: the deployed viewer's track-detail
21341
+ * HERO tile reads it (`use-track-media-entry.ts` → `parseMediaFiles`, which
21342
+ * REQUIRES the field), and a row without it parses as a FAILED read — the red
21343
+ * triangle — not as absence. Removing the field before that viewer ships is an
21344
+ * outage, not a cleanup. Once the viewer takes its hero bytes from `url`,
21345
+ * delete this line and the `withBytes` pass-through in
21346
+ * `analytics-query-facade.ts`; nothing else reads it.
21347
+ */
21311
21348
  var MediaFileSchema = object({
21312
21349
  key: string(),
21313
21350
  kind: MediaFileKindEnum,
21314
- base64: string(),
21315
21351
  sizeBytes: number(),
21316
21352
  timestamp: number()
21353
+ }).extend({
21354
+ /** `/addon/<addonId>/event-media/<encoded stored key>`. Always present. */
21355
+ url: string(),
21356
+ /** @deprecated Transitional — see the schema docblock. Use {@link url}. */
21357
+ base64: string()
21317
21358
  });
21318
21359
  /**
21319
21360
  * One media row WITHOUT its bytes.
@@ -21325,7 +21366,9 @@ var MediaFileSchema = object({
21325
21366
  * blocks the whole view.
21326
21367
  *
21327
21368
  * `sizeBytes` is carried because it is what lets a client decide between the
21328
- * stored blob and a `?variant=thumb` rendering without fetching either.
21369
+ * stored blob and a `?variant=thumb` rendering without fetching either, and
21370
+ * `url` because a client that had to build the plane path itself is a second
21371
+ * copy of a route — the embed, the viewer and the admin UI each grew one.
21329
21372
  */
21330
21373
  var MediaFileInfoSchema = MediaFileSchema.omit({ base64: true });
21331
21374
  /**
@@ -22457,6 +22500,26 @@ var pipelineAnalyticsCapability = {
22457
22500
  deviceId: number()
22458
22501
  }), array(MediaFileInfoSchema).readonly()),
22459
22502
  /**
22503
+ * What media an EVENT has, without any of it — the twin `getEventMedia`
22504
+ * never had.
22505
+ *
22506
+ * `listTrackMedia` above got this treatment because a track's media is
22507
+ * 5-8 MB. An event's is worse per row, not better: an old-style event owns
22508
+ * a `crop` AND a native-resolution `fullFrameBoxed`, and the track DETAIL
22509
+ * modal — the one surface that legitimately shows the big kinds — unions
22510
+ * both listings to build its filmstrip. It then renders every tile from
22511
+ * the `event-media` plane by key and throws the bytes away. Measured on
22512
+ * the live hub: one event's `fullFrameBoxed` is 2 824 077 B, base64'd to
22513
+ * ~3.8 MB, allocated whole in hub-main's heap, for a list of keys.
22514
+ *
22515
+ * Same `deviceId` authorization subject as `getEventMedia`, and the same
22516
+ * rows — this is a projection of that method, never a different question.
22517
+ */
22518
+ listEventMedia: method(object({
22519
+ eventId: string(),
22520
+ deviceId: number()
22521
+ }), array(MediaFileInfoSchema).readonly()),
22522
+ /**
22460
22523
  * Search object events by text query using CLIP cosine similarity.
22461
22524
  * Encodes `text` via the `embedding-encoder` cap, queries the
22462
22525
  * `ObjectEmbeddingStore` with optional prefilters, ranks all matching
@@ -28803,10 +28866,24 @@ var FaceClusterSchema = object({
28803
28866
  size: number().int(),
28804
28867
  cohesion: number()
28805
28868
  });
28869
+ /**
28870
+ * One gallery media row: what the crop is, how big it is, and WHERE its bytes
28871
+ * are — never the bytes.
28872
+ *
28873
+ * `base64` was deleted here rather than deprecated. `MediaFile.base64` (the
28874
+ * track/event contract) is still populated because a deployed viewer requires
28875
+ * the field to parse a row at all; this method has no such reader. Its ONE
28876
+ * caller is the admin UI's detail modal, which was building
28877
+ * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
28878
+ * dialog already rendering its key FRAME from the `event-media` plane.
28879
+ *
28880
+ * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
28881
+ * media key directly, so this needed no new plane and no new access decision.
28882
+ */
28806
28883
  var MediaFileLiteSchema$1 = object({
28807
28884
  key: string(),
28808
28885
  kind: string(),
28809
- base64: string(),
28886
+ url: string(),
28810
28887
  sizeBytes: number(),
28811
28888
  timestamp: number()
28812
28889
  });
@@ -32196,10 +32273,24 @@ var PlateInfoSchema = object({
32196
32273
  */
32197
32274
  cropUrl: string().optional()
32198
32275
  });
32276
+ /**
32277
+ * One gallery media row: what the crop is, how big it is, and WHERE its bytes
32278
+ * are — never the bytes.
32279
+ *
32280
+ * `base64` was deleted here rather than deprecated. `MediaFile.base64` (the
32281
+ * track/event contract) is still populated because a deployed viewer requires
32282
+ * the field to parse a row at all; this method has no such reader. Its ONE
32283
+ * caller is the admin UI's detail modal, which was building
32284
+ * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
32285
+ * dialog already rendering its key FRAME from the `event-media` plane.
32286
+ *
32287
+ * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
32288
+ * media key directly, so this needed no new plane and no new access decision.
32289
+ */
32199
32290
  var MediaFileLiteSchema = object({
32200
32291
  key: string(),
32201
32292
  kind: string(),
32202
- base64: string(),
32293
+ url: string(),
32203
32294
  sizeBytes: number(),
32204
32295
  timestamp: number()
32205
32296
  });
@@ -39689,6 +39780,12 @@ Object.freeze({
39689
39780
  addonId: null,
39690
39781
  access: "view"
39691
39782
  },
39783
+ "pipelineAnalytics.listEventMedia": {
39784
+ capName: "pipeline-analytics",
39785
+ capScope: "device",
39786
+ addonId: null,
39787
+ access: "view"
39788
+ },
39692
39789
  "pipelineAnalytics.listGroups": {
39693
39790
  capName: "pipeline-analytics",
39694
39791
  capScope: "device",
@@ -43312,6 +43409,11 @@ Object.freeze({
43312
43409
  form: "array",
43313
43410
  optional: false
43314
43411
  }],
43412
+ "pipelineAnalytics.listEventMedia": [{
43413
+ name: "deviceId",
43414
+ form: "single",
43415
+ optional: false
43416
+ }],
43315
43417
  "pipelineAnalytics.listGroups": [{
43316
43418
  name: "deviceIds",
43317
43419
  form: "array",