@camstack/addon-ai 0.4.120 → 0.4.122

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.
package/dist/addon.js CHANGED
@@ -5520,7 +5520,7 @@ function number(params) {
5520
5520
  return /* @__PURE__ */ _coercedNumber(ZodNumber, params);
5521
5521
  }
5522
5522
  //#endregion
5523
- //#region ../types/dist/sleep-Dk-BdnBf.mjs
5523
+ //#region ../types/dist/sleep-GU_us3DG.mjs
5524
5524
  /**
5525
5525
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5526
5526
  * window to float samples (D455).
@@ -12024,6 +12024,24 @@ var GetStreamWithCodecInputSchema = object({
12024
12024
  });
12025
12025
  var RtpSourceSchema = object({
12026
12026
  url: string(),
12027
+ /**
12028
+ * The SENTRY counterpart of {@link url} — the dial that asks for the stream
12029
+ * WITHOUT asking for the camera — or `null` when this camera offers none.
12030
+ *
12031
+ * The broker answers it because the broker is the only thing that can:
12032
+ * eligibility is `sentryEligible` (a BATTERY camera that DECLARES the signals
12033
+ * cap), asked of facts only the broker holds. A consumer re-deriving it from
12034
+ * cap bindings would be a second authority, and two authorities on the same
12035
+ * question is how a composite camera ends up dialling a sleeping camera
12036
+ * because its own copy of the rule was a release behind.
12037
+ *
12038
+ * `null` is a real answer and is never a live url in disguise: a consumer
12039
+ * that silently fell back to {@link url} would perform exactly the wake this
12040
+ * field exists to prevent. Only a PASSTHROUGH acquisition can offer one — a
12041
+ * transcode hands back an egress the broker is feeding itself, which has no
12042
+ * sentry counterpart.
12043
+ */
12044
+ sentryUrl: string().nullable().optional(),
12027
12045
  videoCodec: _enum(["H264", "H265"]),
12028
12046
  audioCodec: string(),
12029
12047
  resolution: object({
@@ -27460,6 +27478,118 @@ DeviceType.Light, DeviceType.Siren, DeviceType.Switch, method(object({
27460
27478
  lastChangedAt: number$1()
27461
27479
  });
27462
27480
  /**
27481
+ * camera-grid-layout — the geometry of a COMPOSITE camera, on the camera's own
27482
+ * page.
27483
+ *
27484
+ * ## Why this is a capability and not an addon settings schema
27485
+ *
27486
+ * It was one, and it did not render. The addon declared the editor as a
27487
+ * `type: 'widget'` field inside its own `deviceSettingsSchema()`; the hub
27488
+ * returned that section correctly and `ConfigFormField` renders `type:'widget'`
27489
+ * perfectly well — and nothing ever asked camera-grid for it. The Cluster →
27490
+ * Pipeline → Device Overrides page interrogates a HAND-WRITTEN list of four
27491
+ * addons (`PIPELINE_CLUSTER_DEVICE_ADDONS`), whose own comment says an addon
27492
+ * not on it "falls off silently".
27493
+ *
27494
+ * Adding a fifth name to that list would have been the wrong fix twice over:
27495
+ * that page is per-camera DETECTION tuning, and a grid's geometry belongs
27496
+ * beside PTZ and motion zones on the camera itself. The device page is
27497
+ * BINDING-driven (D12), so the way in is a capability bound to the device —
27498
+ * and this cap carries its section the way `recording` does, by RETURNING it
27499
+ * from `getDeviceSettingsContribution`.
27500
+ *
27501
+ * Seven other widgets are still declared the other way, through a
27502
+ * `deviceConfig.ui` block the framework derives a section from. That route
27503
+ * gives the addon no say in where its own panel lands and no way to decline
27504
+ * for a device the panel does not suit, which is why this one does not use it.
27505
+ *
27506
+ * ## Why one addon may implement it
27507
+ *
27508
+ * It is a device-scoped NATIVE cap, registered by the grid camera device
27509
+ * itself. Nothing else declares a composite camera, so nothing else has a
27510
+ * layout — and the device-scoped route means the widget asks THE camera, not
27511
+ * "the camera-grid addon", which is what let the old custom-action pair be
27512
+ * reached only by a caller that already knew the addon id.
27513
+ *
27514
+ * ## The tab
27515
+ *
27516
+ * `streaming`, not a top-tab of its own. A grid's geometry IS what its stream
27517
+ * is, so the Streaming tab is where it belongs; a `grid` top-tab would need an
27518
+ * entry in `WELL_KNOWN_TAB_MAP` or the device page renders the raw id as the
27519
+ * label (measured on the robot camera, 2026-09-06 — a tab called "navigation"
27520
+ * next to "PTZ").
27521
+ */
27522
+ /** A rectangle in normalized [0,1] coordinates of whatever contains it. */
27523
+ var GridNormalizedRectSchema = object({
27524
+ x: number$1().min(0).max(1),
27525
+ y: number$1().min(0).max(1),
27526
+ width: number$1().gt(0).max(1),
27527
+ height: number$1().gt(0).max(1)
27528
+ });
27529
+ /**
27530
+ * One source camera, the part of its picture taken, and where that part lands.
27531
+ *
27532
+ * Both rectangles are NORMALIZED (D519): a source camera can change resolution
27533
+ * — a profile switch, a firmware update, a substream that comes back different
27534
+ * — and a stored PIXEL rectangle would quietly start cutting the wrong region,
27535
+ * which is the class of bug nobody files.
27536
+ */
27537
+ var GridLayoutCellSchema = object({
27538
+ deviceId: number$1().int().positive(),
27539
+ /** The part of the SOURCE taken, normalized against the source. */
27540
+ source: GridNormalizedRectSchema,
27541
+ /** Where it lands, normalized against the CANVAS. */
27542
+ cell: GridNormalizedRectSchema
27543
+ });
27544
+ /**
27545
+ * Which profiles this grid can actually compose, and why not.
27546
+ *
27547
+ * A grid's `high` composes its sources' `high` and its `low` their `low`, so a
27548
+ * profile is on offer only when EVERY source can serve it. The refusal NAMES
27549
+ * the sources, because "this grid has no low" is not a finding — "615 has no
27550
+ * low" is, and it is the one an operator can act on.
27551
+ */
27552
+ var GridProfileOfferSchema = object({
27553
+ profile: _enum([
27554
+ "high",
27555
+ "mid",
27556
+ "low"
27557
+ ]),
27558
+ offered: boolean(),
27559
+ /** Sources that cannot serve it. Empty when it is offered, or when there are no cells. */
27560
+ missingSources: array(number$1().int().positive())
27561
+ });
27562
+ var GridLayoutViewSchema = object({
27563
+ /** The persisted grid row this camera was declared from. */
27564
+ instanceId: string(),
27565
+ deviceId: number$1().int().nonnegative(),
27566
+ name: string(),
27567
+ /** The canvas the operator laid out — this grid's `high`. mid/low are scaled from it. */
27568
+ width: number$1().int(),
27569
+ height: number$1().int(),
27570
+ fps: number$1().int(),
27571
+ cells: array(GridLayoutCellSchema),
27572
+ /** What the catalog will publish, and what it refuses to. Read-only. */
27573
+ profiles: array(GridProfileOfferSchema)
27574
+ });
27575
+ var GridLayoutPatchSchema = object({
27576
+ deviceId: number$1().int().nonnegative(),
27577
+ name: string().min(1).max(160).optional(),
27578
+ width: number$1().int().min(160).max(7680).optional(),
27579
+ height: number$1().int().min(120).max(4320).optional(),
27580
+ fps: number$1().int().min(1).max(60).optional(),
27581
+ /**
27582
+ * The whole cell list at once. A per-cell patch would need an ordering the
27583
+ * editor does not have, and a half-applied layout is a picture nobody asked
27584
+ * for.
27585
+ */
27586
+ cells: array(GridLayoutCellSchema).max(16)
27587
+ });
27588
+ DeviceType.Camera, method(object({ deviceId: number$1().int().nonnegative() }), GridLayoutViewSchema.nullable(), { auth: "admin" }), method(GridLayoutPatchSchema, GridLayoutViewSchema, {
27589
+ kind: "mutation",
27590
+ auth: "admin"
27591
+ });
27592
+ /**
27463
27593
  * Motion-zones share the same MaskShape vocabulary as privacy-mask — the
27464
27594
  * on-camera motion-detection mask is a single `grid` region (a row-major
27465
27595
  * boolean cell lattice the camera's onboard VMD evaluates). Composing it as
@@ -32488,6 +32618,18 @@ Object.freeze({
32488
32618
  addonId: null,
32489
32619
  access: "view"
32490
32620
  },
32621
+ "cameraGridLayout.getLayout": {
32622
+ capName: "camera-grid-layout",
32623
+ capScope: "device",
32624
+ addonId: null,
32625
+ access: "view"
32626
+ },
32627
+ "cameraGridLayout.saveLayout": {
32628
+ capName: "camera-grid-layout",
32629
+ capScope: "device",
32630
+ addonId: null,
32631
+ access: "create"
32632
+ },
32491
32633
  "cameraStreams.getBrokerStreams": {
32492
32634
  capName: "camera-streams",
32493
32635
  capScope: "device",
@@ -38187,6 +38329,21 @@ Object.freeze({
38187
38329
  form: "single",
38188
38330
  optional: false
38189
38331
  }],
38332
+ "cameraGridLayout.getLayout": [{
38333
+ name: "deviceId",
38334
+ form: "single",
38335
+ optional: false
38336
+ }],
38337
+ "cameraGridLayout.saveLayout": [{
38338
+ name: "cells",
38339
+ form: "object-array",
38340
+ optional: false,
38341
+ itemField: "deviceId"
38342
+ }, {
38343
+ name: "deviceId",
38344
+ form: "single",
38345
+ optional: false
38346
+ }],
38190
38347
  "cameraStreams.getBrokerStreams": [{
38191
38348
  name: "deviceId",
38192
38349
  form: "single",
package/dist/addon.mjs CHANGED
@@ -5547,7 +5547,7 @@ function number(params) {
5547
5547
  return /* @__PURE__ */ _coercedNumber(ZodNumber, params);
5548
5548
  }
5549
5549
  //#endregion
5550
- //#region ../types/dist/sleep-Dk-BdnBf.mjs
5550
+ //#region ../types/dist/sleep-GU_us3DG.mjs
5551
5551
  /**
5552
5552
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5553
5553
  * window to float samples (D455).
@@ -12051,6 +12051,24 @@ var GetStreamWithCodecInputSchema = object({
12051
12051
  });
12052
12052
  var RtpSourceSchema = object({
12053
12053
  url: string(),
12054
+ /**
12055
+ * The SENTRY counterpart of {@link url} — the dial that asks for the stream
12056
+ * WITHOUT asking for the camera — or `null` when this camera offers none.
12057
+ *
12058
+ * The broker answers it because the broker is the only thing that can:
12059
+ * eligibility is `sentryEligible` (a BATTERY camera that DECLARES the signals
12060
+ * cap), asked of facts only the broker holds. A consumer re-deriving it from
12061
+ * cap bindings would be a second authority, and two authorities on the same
12062
+ * question is how a composite camera ends up dialling a sleeping camera
12063
+ * because its own copy of the rule was a release behind.
12064
+ *
12065
+ * `null` is a real answer and is never a live url in disguise: a consumer
12066
+ * that silently fell back to {@link url} would perform exactly the wake this
12067
+ * field exists to prevent. Only a PASSTHROUGH acquisition can offer one — a
12068
+ * transcode hands back an egress the broker is feeding itself, which has no
12069
+ * sentry counterpart.
12070
+ */
12071
+ sentryUrl: string().nullable().optional(),
12054
12072
  videoCodec: _enum(["H264", "H265"]),
12055
12073
  audioCodec: string(),
12056
12074
  resolution: object({
@@ -27487,6 +27505,118 @@ DeviceType.Light, DeviceType.Siren, DeviceType.Switch, method(object({
27487
27505
  lastChangedAt: number$1()
27488
27506
  });
27489
27507
  /**
27508
+ * camera-grid-layout — the geometry of a COMPOSITE camera, on the camera's own
27509
+ * page.
27510
+ *
27511
+ * ## Why this is a capability and not an addon settings schema
27512
+ *
27513
+ * It was one, and it did not render. The addon declared the editor as a
27514
+ * `type: 'widget'` field inside its own `deviceSettingsSchema()`; the hub
27515
+ * returned that section correctly and `ConfigFormField` renders `type:'widget'`
27516
+ * perfectly well — and nothing ever asked camera-grid for it. The Cluster →
27517
+ * Pipeline → Device Overrides page interrogates a HAND-WRITTEN list of four
27518
+ * addons (`PIPELINE_CLUSTER_DEVICE_ADDONS`), whose own comment says an addon
27519
+ * not on it "falls off silently".
27520
+ *
27521
+ * Adding a fifth name to that list would have been the wrong fix twice over:
27522
+ * that page is per-camera DETECTION tuning, and a grid's geometry belongs
27523
+ * beside PTZ and motion zones on the camera itself. The device page is
27524
+ * BINDING-driven (D12), so the way in is a capability bound to the device —
27525
+ * and this cap carries its section the way `recording` does, by RETURNING it
27526
+ * from `getDeviceSettingsContribution`.
27527
+ *
27528
+ * Seven other widgets are still declared the other way, through a
27529
+ * `deviceConfig.ui` block the framework derives a section from. That route
27530
+ * gives the addon no say in where its own panel lands and no way to decline
27531
+ * for a device the panel does not suit, which is why this one does not use it.
27532
+ *
27533
+ * ## Why one addon may implement it
27534
+ *
27535
+ * It is a device-scoped NATIVE cap, registered by the grid camera device
27536
+ * itself. Nothing else declares a composite camera, so nothing else has a
27537
+ * layout — and the device-scoped route means the widget asks THE camera, not
27538
+ * "the camera-grid addon", which is what let the old custom-action pair be
27539
+ * reached only by a caller that already knew the addon id.
27540
+ *
27541
+ * ## The tab
27542
+ *
27543
+ * `streaming`, not a top-tab of its own. A grid's geometry IS what its stream
27544
+ * is, so the Streaming tab is where it belongs; a `grid` top-tab would need an
27545
+ * entry in `WELL_KNOWN_TAB_MAP` or the device page renders the raw id as the
27546
+ * label (measured on the robot camera, 2026-09-06 — a tab called "navigation"
27547
+ * next to "PTZ").
27548
+ */
27549
+ /** A rectangle in normalized [0,1] coordinates of whatever contains it. */
27550
+ var GridNormalizedRectSchema = object({
27551
+ x: number$1().min(0).max(1),
27552
+ y: number$1().min(0).max(1),
27553
+ width: number$1().gt(0).max(1),
27554
+ height: number$1().gt(0).max(1)
27555
+ });
27556
+ /**
27557
+ * One source camera, the part of its picture taken, and where that part lands.
27558
+ *
27559
+ * Both rectangles are NORMALIZED (D519): a source camera can change resolution
27560
+ * — a profile switch, a firmware update, a substream that comes back different
27561
+ * — and a stored PIXEL rectangle would quietly start cutting the wrong region,
27562
+ * which is the class of bug nobody files.
27563
+ */
27564
+ var GridLayoutCellSchema = object({
27565
+ deviceId: number$1().int().positive(),
27566
+ /** The part of the SOURCE taken, normalized against the source. */
27567
+ source: GridNormalizedRectSchema,
27568
+ /** Where it lands, normalized against the CANVAS. */
27569
+ cell: GridNormalizedRectSchema
27570
+ });
27571
+ /**
27572
+ * Which profiles this grid can actually compose, and why not.
27573
+ *
27574
+ * A grid's `high` composes its sources' `high` and its `low` their `low`, so a
27575
+ * profile is on offer only when EVERY source can serve it. The refusal NAMES
27576
+ * the sources, because "this grid has no low" is not a finding — "615 has no
27577
+ * low" is, and it is the one an operator can act on.
27578
+ */
27579
+ var GridProfileOfferSchema = object({
27580
+ profile: _enum([
27581
+ "high",
27582
+ "mid",
27583
+ "low"
27584
+ ]),
27585
+ offered: boolean(),
27586
+ /** Sources that cannot serve it. Empty when it is offered, or when there are no cells. */
27587
+ missingSources: array(number$1().int().positive())
27588
+ });
27589
+ var GridLayoutViewSchema = object({
27590
+ /** The persisted grid row this camera was declared from. */
27591
+ instanceId: string(),
27592
+ deviceId: number$1().int().nonnegative(),
27593
+ name: string(),
27594
+ /** The canvas the operator laid out — this grid's `high`. mid/low are scaled from it. */
27595
+ width: number$1().int(),
27596
+ height: number$1().int(),
27597
+ fps: number$1().int(),
27598
+ cells: array(GridLayoutCellSchema),
27599
+ /** What the catalog will publish, and what it refuses to. Read-only. */
27600
+ profiles: array(GridProfileOfferSchema)
27601
+ });
27602
+ var GridLayoutPatchSchema = object({
27603
+ deviceId: number$1().int().nonnegative(),
27604
+ name: string().min(1).max(160).optional(),
27605
+ width: number$1().int().min(160).max(7680).optional(),
27606
+ height: number$1().int().min(120).max(4320).optional(),
27607
+ fps: number$1().int().min(1).max(60).optional(),
27608
+ /**
27609
+ * The whole cell list at once. A per-cell patch would need an ordering the
27610
+ * editor does not have, and a half-applied layout is a picture nobody asked
27611
+ * for.
27612
+ */
27613
+ cells: array(GridLayoutCellSchema).max(16)
27614
+ });
27615
+ DeviceType.Camera, method(object({ deviceId: number$1().int().nonnegative() }), GridLayoutViewSchema.nullable(), { auth: "admin" }), method(GridLayoutPatchSchema, GridLayoutViewSchema, {
27616
+ kind: "mutation",
27617
+ auth: "admin"
27618
+ });
27619
+ /**
27490
27620
  * Motion-zones share the same MaskShape vocabulary as privacy-mask — the
27491
27621
  * on-camera motion-detection mask is a single `grid` region (a row-major
27492
27622
  * boolean cell lattice the camera's onboard VMD evaluates). Composing it as
@@ -32515,6 +32645,18 @@ Object.freeze({
32515
32645
  addonId: null,
32516
32646
  access: "view"
32517
32647
  },
32648
+ "cameraGridLayout.getLayout": {
32649
+ capName: "camera-grid-layout",
32650
+ capScope: "device",
32651
+ addonId: null,
32652
+ access: "view"
32653
+ },
32654
+ "cameraGridLayout.saveLayout": {
32655
+ capName: "camera-grid-layout",
32656
+ capScope: "device",
32657
+ addonId: null,
32658
+ access: "create"
32659
+ },
32518
32660
  "cameraStreams.getBrokerStreams": {
32519
32661
  capName: "camera-streams",
32520
32662
  capScope: "device",
@@ -38214,6 +38356,21 @@ Object.freeze({
38214
38356
  form: "single",
38215
38357
  optional: false
38216
38358
  }],
38359
+ "cameraGridLayout.getLayout": [{
38360
+ name: "deviceId",
38361
+ form: "single",
38362
+ optional: false
38363
+ }],
38364
+ "cameraGridLayout.saveLayout": [{
38365
+ name: "cells",
38366
+ form: "object-array",
38367
+ optional: false,
38368
+ itemField: "deviceId"
38369
+ }, {
38370
+ name: "deviceId",
38371
+ form: "single",
38372
+ optional: false
38373
+ }],
38217
38374
  "cameraStreams.getBrokerStreams": [{
38218
38375
  name: "deviceId",
38219
38376
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-ai",
3
- "version": "0.4.120",
3
+ "version": "0.4.122",
4
4
  "description": "AI addon for CamStack — the `llm` collection provider (cloud, LAN, and camstack-managed local llama.cpp profiles) plus the per-node `llm-runtime` managed executor.",
5
5
  "keywords": [
6
6
  "camstack",