@camstack/addon-agent-ui 1.2.155 → 1.2.157

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 (2) hide show
  1. package/dist/addon.js +314 -203
  2. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -5371,7 +5371,7 @@ var ZodIssueCode = {
5371
5371
  var ZodFirstPartyTypeKind;
5372
5372
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5373
5373
  //#endregion
5374
- //#region ../types/dist/sleep-DlrgY4Qb.mjs
5374
+ //#region ../types/dist/sleep-C-v96WpE.mjs
5375
5375
  /**
5376
5376
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5377
5377
  * window to float samples (D455).
@@ -7418,7 +7418,59 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
7418
7418
  return DeviceRole;
7419
7419
  }({});
7420
7420
  //#endregion
7421
- //#region ../types/dist/composition-BK-GTFYb.mjs
7421
+ //#region ../types/dist/composition-Dq42XWp0.mjs
7422
+ /**
7423
+ * The ffmpeg build every CamStack node downloads, per platform and arch.
7424
+ *
7425
+ * ## Why a download, on every node, including the container
7426
+ *
7427
+ * The version of ffmpeg used to be CODE: baked into the image, changeable only
7428
+ * by rebuilding and rolling it. Now it is DATA — this file states the release,
7429
+ * and a node that boots with a different one downloads it. Changing ffmpeg is a
7430
+ * framework bump, not an image rebuild.
7431
+ *
7432
+ * It also settles the licence question by removing it. FFmpeg's licence is
7433
+ * chosen at compile time: the core is LGPL 2.1+, `--enable-gpl` makes it GPL
7434
+ * (for x264/x265), `--enable-version3` makes that v3, and `--enable-nonfree`
7435
+ * makes the result undistributable. These are GPL v3 builds, and we do not
7436
+ * distribute them: the user's own node fetches the archive from the project
7437
+ * that publishes it, so we convey no GPL work and inherit none of the
7438
+ * obligations that come with conveying one. That is the whole reason the image
7439
+ * no longer installs ffmpeg at all — not a technical preference.
7440
+ *
7441
+ * ## Why jellyfin-ffmpeg
7442
+ *
7443
+ * One vendor covering linux-amd64, linux-arm64, darwin-arm64 and darwin-x64
7444
+ * from one release, with the widest hardware surface of anything maintained:
7445
+ * VAAPI, QSV, NVENC/NVDEC, AMF, Vulkan, OpenCL and DRM on amd64, plus Rockchip
7446
+ * MPP on arm64. It reaches the vendor libraries through dlopen trampolines,
7447
+ * so the binary is not linked against a driver stack it may not find.
7448
+ *
7449
+ * **What it still needs from the host, measured 2026-09-19.** On the Unraid
7450
+ * HOST, which has no libva, `-vaapi_device` aborts the process outright —
7451
+ * `implib-gen: libva-drm.so.2: failed to load library ... Assertion '0 &&
7452
+ * "Assertion in generated code"' failed`. Inside our container, which installs
7453
+ * libva plus the iHD driver, the same command encodes: verified end to end with
7454
+ * `testsrc → format=nv12,hwupload → h264_vaapi`. So the image drops ffmpeg and
7455
+ * KEEPS the Intel media stack; the drivers are the part a binary cannot bring.
7456
+ *
7457
+ * ## Why each artifact carries a checksum and a claim
7458
+ *
7459
+ * The download is now the only source, so `sha256` is verified before anything
7460
+ * is extracted — jellyfin publishes no checksums on its release assets, so
7461
+ * these are ours, taken at the version bump. And `expectedHwaccels` is what the
7462
+ * build is supposed to be able to do: a binary that probes short of its own
7463
+ * claim is a source that changed under us, which is precisely what went
7464
+ * unnoticed for months when a static build turned out to carry no VAAPI at all
7465
+ * (D536).
7466
+ */
7467
+ /** The pinned jellyfin-ffmpeg release. Changing this is the version change. */
7468
+ var FFMPEG_RELEASE = "8.1.2-5";
7469
+ var BASE = `https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v${FFMPEG_RELEASE}`;
7470
+ function portable(target) {
7471
+ return `${BASE}/jellyfin-ffmpeg_${FFMPEG_RELEASE}_portable_${target}-gpl.tar.xz`;
7472
+ }
7473
+ portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
7422
7474
  /**
7423
7475
  * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
7424
7476
  * valutatore puro del suo FRONTE.
@@ -8222,7 +8274,6 @@ var CompositionSchema = object({
8222
8274
  });
8223
8275
  new Set([
8224
8276
  "track",
8225
- "summary",
8226
8277
  "face",
8227
8278
  "identity",
8228
8279
  "plate",
@@ -12978,6 +13029,70 @@ var CompositionBlockStateSchema = object({
12978
13029
  reason: string().optional(),
12979
13030
  fields: array(CompositionFieldStateSchema)
12980
13031
  });
13032
+ var NUMBER = {
13033
+ kind: "fixed",
13034
+ result: ["number"]
13035
+ };
13036
+ var BOOLEAN = {
13037
+ kind: "fixed",
13038
+ result: ["boolean"]
13039
+ };
13040
+ Object.freeze({
13041
+ min: NUMBER,
13042
+ max: NUMBER,
13043
+ abs: NUMBER,
13044
+ floor: NUMBER,
13045
+ ceil: NUMBER,
13046
+ sqrt: NUMBER,
13047
+ round: NUMBER,
13048
+ pow: NUMBER,
13049
+ clamp: NUMBER,
13050
+ avg: NUMBER,
13051
+ sum: NUMBER,
13052
+ age: NUMBER,
13053
+ convert: NUMBER,
13054
+ coalesce: { kind: "coalesce" },
13055
+ any: BOOLEAN,
13056
+ all: BOOLEAN,
13057
+ within: BOOLEAN,
13058
+ latest: {
13059
+ kind: "fixed",
13060
+ result: ["number", "null"]
13061
+ },
13062
+ number: {
13063
+ kind: "fixed",
13064
+ result: ["number", "null"]
13065
+ },
13066
+ rose: {
13067
+ kind: "fixed",
13068
+ result: ["number", "null"]
13069
+ },
13070
+ count: NUMBER
13071
+ });
13072
+ `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
13073
+ /**
13074
+ * What a composition may BUILD on a device type (G2, D676): the caps a new
13075
+ * device of that type, or an existing one that lacks them, can be given. The
13076
+ * blockers are the validator's own refusal codes, and a spec compares this
13077
+ * listing with `planComposition` for every capability — the editor can never
13078
+ * offer a feature the save would refuse for the same reason.
13079
+ */
13080
+ var ComposableFieldSchema = object({
13081
+ path: string(),
13082
+ kind: CompositionFieldKindSchema,
13083
+ nullable: boolean(),
13084
+ required: boolean(),
13085
+ enumValues: array(string()).optional()
13086
+ });
13087
+ var ComposableCapabilitiesSchema = object({ caps: array(object({
13088
+ cap: string(),
13089
+ fields: array(ComposableFieldSchema),
13090
+ /** Present for an item-array cap (`consumables.items`). */
13091
+ itemArray: object({
13092
+ path: string(),
13093
+ keyField: string()
13094
+ }).optional()
13095
+ })) });
12981
13096
  /** Where a block runs. The operator chooses — a block driving a device on an
12982
13097
  * agent is the reason placement is not fixed to the hub. */
12983
13098
  var CoreBlockPlacementSchema = union([literal("hub"), string().min(1)]);
@@ -13142,7 +13257,7 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
13142
13257
  }), method(object({ blockId: string() }), CompositionGetReadSchema, { auth: "admin" }), method(object({}), CompositionListReadSchema, { auth: "admin" }), method(object({
13143
13258
  addonId: string().min(1),
13144
13259
  stableId: string().min(1)
13145
- }), CompositionCustomizationReadSchema, { auth: "admin" }), method(object({
13260
+ }), CompositionCustomizationReadSchema, { auth: "admin" }), method(object({ deviceType: _enum(DeviceType) }), ComposableCapabilitiesSchema, { auth: "admin" }), method(object({
13146
13261
  composition: CompositionSchema,
13147
13262
  blockId: string().optional()
13148
13263
  }), CompositionValidationSchema, {
@@ -20024,6 +20139,102 @@ var OauthIntegrationDescriptorSchema = object({
20024
20139
  });
20025
20140
  method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
20026
20141
  /**
20142
+ * What started a camera's onboard motion (D672). `camera` is the camera's own
20143
+ * firmware; `sensor` is a device the operator ATTACHED to it. Absent on the
20144
+ * wire means the camera: only the camera-sensors bridge ever says `sensor`.
20145
+ */
20146
+ var MotionTriggeredBySchema = discriminatedUnion("kind", [object({ kind: literal("camera") }), object({
20147
+ kind: literal("sensor"),
20148
+ deviceId: number().int().positive()
20149
+ })]);
20150
+ Object.freeze({ kind: "camera" });
20151
+ var CameraSensorRefSchema = object({
20152
+ addonId: string().min(1),
20153
+ stableId: string().min(1)
20154
+ });
20155
+ function cameraSensorRefKey(ref) {
20156
+ return `${ref.addonId}/${ref.stableId}`;
20157
+ }
20158
+ /** CS-1 accepts exactly one mode; CS-2 (D673) widens it to confirm / replace. */
20159
+ var CameraSensorsModeSchema = _enum(["in-addition"]);
20160
+ var CameraSensorsConfigSchema = object({
20161
+ mode: CameraSensorsModeSchema,
20162
+ sensors: array(CameraSensorRefSchema).min(1).max(8)
20163
+ }).superRefine((value, ctx) => {
20164
+ const seen = /* @__PURE__ */ new Set();
20165
+ value.sensors.forEach((ref, index) => {
20166
+ const key = cameraSensorRefKey(ref);
20167
+ if (seen.has(key)) ctx.addIssue({
20168
+ code: "custom",
20169
+ path: ["sensors", index],
20170
+ message: `duplicate camera sensor ${key}`
20171
+ });
20172
+ seen.add(key);
20173
+ });
20174
+ });
20175
+ var RefFields = {
20176
+ addonId: string(),
20177
+ stableId: string()
20178
+ };
20179
+ var CameraSensorAttachmentSchema = discriminatedUnion("state", [
20180
+ object({
20181
+ ...RefFields,
20182
+ state: literal("ready"),
20183
+ deviceId: number(),
20184
+ name: string(),
20185
+ caps: array(string()).readonly()
20186
+ }),
20187
+ object({
20188
+ ...RefFields,
20189
+ state: literal("offline"),
20190
+ deviceId: number(),
20191
+ name: string(),
20192
+ reason: _enum(["offline", "disabled"])
20193
+ }),
20194
+ object({
20195
+ ...RefFields,
20196
+ state: literal("missing"),
20197
+ reason: string()
20198
+ }),
20199
+ object({
20200
+ ...RefFields,
20201
+ state: literal("unsupported"),
20202
+ deviceId: number(),
20203
+ name: string(),
20204
+ reason: string()
20205
+ }),
20206
+ object({
20207
+ ...RefFields,
20208
+ state: literal("unreadable"),
20209
+ reason: string()
20210
+ })
20211
+ ]);
20212
+ /**
20213
+ * Why a configured camera forwards NOTHING from its sensors, however ready they
20214
+ * are (D672): it does not list `onboard` in `motionSources` (attaching a sensor
20215
+ * does not add it), or it has no active detection config at all (the bridge
20216
+ * reads the active config). Absent = forwarded, or an older node that does not
20217
+ * say.
20218
+ */
20219
+ var CameraSensorsNotForwardedSchema = _enum(["onboard-not-listed", "no-active-detection"]);
20220
+ var CameraSensorsStatusSchema = discriminatedUnion("state", [
20221
+ object({ state: literal("not-configured") }),
20222
+ object({
20223
+ state: literal("unreadable"),
20224
+ reason: string()
20225
+ }),
20226
+ object({
20227
+ state: literal("configured"),
20228
+ mode: CameraSensorsModeSchema,
20229
+ sensors: array(CameraSensorAttachmentSchema).readonly(),
20230
+ notForwarded: CameraSensorsNotForwardedSchema.optional()
20231
+ })
20232
+ ]);
20233
+ Object.freeze({
20234
+ state: "unreadable",
20235
+ reason: "camera sensor mappings not loaded"
20236
+ });
20237
+ /**
20027
20238
  * A model choice has a SCOPE, and a scope change has a workflow.
20028
20239
  *
20029
20240
  * ## The two scopes
@@ -20772,6 +20983,12 @@ var MotionEventSchema = object({
20772
20983
  /** Populated by B5 (recording playback URL for this event). */
20773
20984
  mediaUrl: string().optional(),
20774
20985
  /**
20986
+ * The FIRST trigger of an `onboard` episode, written once at open (D672).
20987
+ * Absent = not recorded (a row written before D672, or an analyzer /
20988
+ * device-activity episode) — never read as "the camera" (D393).
20989
+ */
20990
+ triggeredBy: MotionTriggeredBySchema.optional(),
20991
+ /**
20775
20992
  * One row per motion EPISODE, not one per push (D475). `null` while the
20776
20993
  * episode is still open — a further rising edge extends it in place rather
20777
20994
  * than inserting a new row. Set once, at close, to `lastOnAt - startedAt`
@@ -20930,6 +21147,21 @@ var ObjectEventSchema = object({
20930
21147
  keyFrameMediaId: number().int().optional(),
20931
21148
  /** Populated by B5 (recording playback URL for this event). */
20932
21149
  mediaUrl: string().optional(),
21150
+ /**
21151
+ * When the change this row records HAPPENED, epoch ms, when that is earlier
21152
+ * than {@link timestamp} and the producer could date it (D684).
21153
+ *
21154
+ * Set on a package PICK-UP row only: its `timestamp` is when the departure
21155
+ * was CONCLUDED — five minutes of unseen observed time after the parcel went
21156
+ * — and a notification's footage has to be cut around the pick-up, not the
21157
+ * empty doormat after it (D682). Stored so every path that rebuilds a
21158
+ * notification subject from the row (the boot reconcile, a dry-run) anchors
21159
+ * on the same instant the live hook did.
21160
+ *
21161
+ * `timestamp` keeps its meaning. Absent on every other row and on every
21162
+ * pick-up written before D684 — UNKNOWN, never 0 and never `timestamp`.
21163
+ */
21164
+ observedAt: number().optional(),
20933
21165
  /** The parent track's key-event importance [0,1], propagated to every object
20934
21166
  * event of the track (so an event row can be sorted by importance without a
20935
21167
  * track join). Absent on legacy rows / before the track was scored. */
@@ -21265,131 +21497,6 @@ var RecentTracksQueryInput = object({
21265
21497
  /** See {@link ExcludeSourcesDoc}. */
21266
21498
  excludeSources: array(TrackSourceSchema).optional()
21267
21499
  });
21268
- /**
21269
- * A Summary (D359): the per-camera session envelope that references tracks.
21270
- * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
21271
- * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
21272
- * first frame with a member visible has been materialised (D482 — it was the
21273
- * row's composite key, which embedded the owner and could name another table's
21274
- * row once event ids became per-table rowids).
21275
- */
21276
- var SummarySchema = object({
21277
- id: string(),
21278
- deviceId: number(),
21279
- openedAt: number(),
21280
- lastActiveAt: number(),
21281
- sealedAt: number().nullable(),
21282
- memberCount: number().int().nonnegative(),
21283
- groupShotMediaId: number().int().nullable()
21284
- });
21285
- var SummariesQueryInput = object({
21286
- deviceIds: array(number()).min(1),
21287
- since: number().optional(),
21288
- until: number().optional(),
21289
- limit: number().int().min(1).max(200).default(50),
21290
- /** The same member-wise filter {@link listRecentSummaries} takes — one
21291
- * meaning of "dog" on every summary surface. */
21292
- classes: array(string().min(1).max(60)).max(40).optional()
21293
- });
21294
- var SummaryDetailSchema = object({
21295
- summary: SummarySchema,
21296
- /** Members in absorption order, oldest join first. */
21297
- members: array(object({
21298
- trackId: string(),
21299
- /**
21300
- * The track's class at the time it JOINED, or `null` when it was unresolved
21301
- * and the track no longer exists to fill it.
21302
- *
21303
- * The join, not the latest opinion: class voting can re-decide a track's
21304
- * class afterwards, and a group's composition is the history of who came
21305
- * together. A consumer counts these to badge a thumbnail — "two people and a
21306
- * dog" — rather than being handed a second, denormalised tally that would
21307
- * drift from the members it claims to describe.
21308
- */
21309
- className: string().nullable(),
21310
- /** `primary` is the one member that carries the group's identity. */
21311
- role: _enum([
21312
- "primary",
21313
- "sibling",
21314
- "child"
21315
- ]).nullable(),
21316
- joinedAt: number(),
21317
- /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
21318
- * at read time. Absent when nothing resolved, never an empty string. */
21319
- label: string().optional(),
21320
- /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
21321
- * recognised nobody, and absent for a member whose track has been pruned —
21322
- * a thumbnail that names the wrong person is worse than one naming nobody. */
21323
- subLabel: string().optional()
21324
- })).readonly()
21325
- });
21326
- /**
21327
- * What a LIST of groups carries: the envelope plus what it is MADE OF.
21328
- *
21329
- * Coarse on purpose — the operator's rule is "only the rough data on the list,
21330
- * we extend when we notice we need more". Enough to draw the card ("two people
21331
- * and a dog") and no more. The MEMBERS, their names and the per-member links
21332
- * are on {@link getSummary}: shipping every member of every group so a client
21333
- * can count them turns a page of two hundred groups into a thousand records to
21334
- * draw two hundred badges.
21335
- */
21336
- var SummaryListItemSchema = object({
21337
- summary: SummarySchema,
21338
- /** Class counts, most numerous first; `className: null` is a member whose
21339
- * class never resolved, counted so the counts add up to `memberCount`. */
21340
- classes: array(object({
21341
- className: string().nullable(),
21342
- count: number().int().positive()
21343
- })).readonly()
21344
- });
21345
- var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
21346
- /**
21347
- * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
21348
- *
21349
- * The reel on the cameras page and the Events gallery both ride a cursor, so a
21350
- * summaries feed that could only answer "the last N for these cameras"
21351
- * (`listSummaries`) would make groups available in the timeline and nowhere
21352
- * else. Two feeds with different ordering contracts would be two ideas of what
21353
- * "newest" means, on one screen.
21354
- */
21355
- var RecentSummariesQueryInput = object({
21356
- /** Devices to merge. An empty array yields an empty page. */
21357
- deviceIds: array(number()),
21358
- /** Window lower bound on `lastActiveAt` (inclusive). */
21359
- since: number().optional(),
21360
- /** Window upper bound on `lastActiveAt` (inclusive). */
21361
- until: number().optional(),
21362
- limit: number().int().min(1).max(500).default(100),
21363
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
21364
- cursor: string().optional(),
21365
- /**
21366
- * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
21367
- * deliberately the same name: the events filter tree emits one list of ticked
21368
- * taxonomy nodes, and a summaries feed that called it something else would
21369
- * make the operator's one filter component produce two shapes.
21370
- *
21371
- * A question about the MEMBERS: a group matches when ANY of them does. "Show
21372
- * me the arrivals with a dog" has to return the group of four people who had
21373
- * one with them; a filter on the group's own fields could only ever answer
21374
- * about its primary.
21375
- *
21376
- * Matched against all THREE tiers of a member — the class (`animal`), the
21377
- * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
21378
- * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
21379
- * an event kind in the tree and a tier-1 label on an `animal` track; a filter
21380
- * that read only the class would answer "animal" and never "dog".
21381
- *
21382
- * ABSENT or EMPTY means no filter — absent is not a selection of none.
21383
- */
21384
- classes: array(string().min(1).max(60)).max(40).optional()
21385
- });
21386
- var RecentSummariesPageSchema = object({
21387
- /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
21388
- * {@link listSummaries} — one list shape, so a card renders the same
21389
- * whichever feed drew it. */
21390
- summaries: array(SummaryListItemSchema).readonly(),
21391
- nextCursor: string().nullable()
21392
- });
21393
21500
  var RecentTracksPageSchema = object({
21394
21501
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
21395
21502
  tracks: array(TrackSchema).readonly(),
@@ -21901,7 +22008,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21901
22008
  classes: array(string()).optional(),
21902
22009
  /** See {@link ExcludeSourcesDoc}. */
21903
22010
  excludeSources: array(TrackSourceSchema).optional()
21904
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
22011
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
21905
22012
  kind: "mutation",
21906
22013
  auth: "admin"
21907
22014
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -23081,6 +23188,23 @@ var MotionSourceEnum = _enum([
23081
23188
  */
23082
23189
  var MotionSourcesSchema = array(MotionSourceEnum);
23083
23190
  /**
23191
+ * The operator-facing name of each MOTION source (D672: `onboard` = the
23192
+ * camera's sensors — its own motion plus any sensor attached to it). The wire
23193
+ * value stays `onboard`; only the label changed. `detectionSources: 'onboard'`
23194
+ * ("Camera onboard AI") and the notification source filter keep their labels.
23195
+ */
23196
+ var MOTION_SOURCE_LABELS = {
23197
+ analyzer: "Frame-diff Analyzer (motion addon)",
23198
+ onboard: "Camera sensors",
23199
+ "device-activity": "Device activity (the device says it is working)"
23200
+ };
23201
+ /** The order the motion-source picker offers them in (not the enum's order). */
23202
+ var MOTION_SOURCE_PICKER_ORDER = [
23203
+ "analyzer",
23204
+ "onboard",
23205
+ "device-activity"
23206
+ ];
23207
+ /**
23084
23208
  * Which root detector a camera runs — EXACTLY ONE.
23085
23209
  *
23086
23210
  * Deliberately the SAME vocabulary post-analysis already tags every detection
@@ -23427,7 +23551,14 @@ var RunnerCameraConfigSchema = object({
23427
23551
  */
23428
23552
  inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
23429
23553
  });
23430
- motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, preRollInferenceFramesField.min, preRollInferenceFramesField.max, preRollInferenceFramesField.step, preRollInferenceFramesField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
23554
+ MOTION_SOURCE_PICKER_ORDER.map((value) => value === "onboard" ? {
23555
+ value,
23556
+ label: MOTION_SOURCE_LABELS[value],
23557
+ description: "The camera's own motion, plus any sensor attached to it"
23558
+ } : {
23559
+ value,
23560
+ label: MOTION_SOURCE_LABELS[value]
23561
+ }), motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, preRollInferenceFramesField.min, preRollInferenceFramesField.max, preRollInferenceFramesField.step, preRollInferenceFramesField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
23431
23562
  /**
23432
23563
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
23433
23564
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -24001,6 +24132,11 @@ var CameraStatusSchema = object({
24001
24132
  */
24002
24133
  switchedOff: array(CameraSwitchIdSchema).readonly(),
24003
24134
  /**
24135
+ * The camera's attached sensors (D672). Optional only for a status composed
24136
+ * by an orchestrator older than D672; this orchestrator always sets it.
24137
+ */
24138
+ cameraSensors: CameraSensorsStatusSchema.optional(),
24139
+ /**
24004
24140
  * Stages of the bounded fan-out that were CUT SHORT — a timeout or a
24005
24141
  * rejection — and whose block is therefore `null` because we could not
24006
24142
  * READ it, not because there is nothing there.
@@ -24093,6 +24229,28 @@ var NodeInferenceDevicesSchema = object({
24093
24229
  reachable: boolean(),
24094
24230
  devices: array(NodeInferenceDeviceSchema).readonly()
24095
24231
  });
24232
+ /**
24233
+ * Pipeline Orchestrator capability — global load balancer + camera dispatcher.
24234
+ *
24235
+ * Lives on the hub. Tracks the camera-to-agent assignment map, listens to
24236
+ * device lifecycle events, queries each runner's capacity + hardware
24237
+ * profile, and dispatches `attachCamera` / `detachCamera` cap calls to the
24238
+ * appropriate runner instances. Persists assignments via the multi-level
24239
+ * settings store so they survive hub restarts.
24240
+ *
24241
+ * Strictly the control plane: never sees a frame, never calls inference,
24242
+ * never drains queues. The runner does that work locally on each agent.
24243
+ */
24244
+ /**
24245
+ * The input of `setCameraSensors` (G5, D676): the ONE typed write path of a
24246
+ * camera's attached sensors (D672). `config: null` removes the attachment —
24247
+ * the camera reads `not-configured` again, never `unreadable`.
24248
+ */
24249
+ var SetCameraSensorsInputSchema = object({
24250
+ deviceId: number().int().nonnegative(),
24251
+ /** `null` removes the attachment: the camera goes back to `not-configured`. */
24252
+ config: CameraSensorsConfigSchema.nullable()
24253
+ });
24096
24254
  method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
24097
24255
  kind: "mutation",
24098
24256
  auth: "admin"
@@ -24247,7 +24405,10 @@ method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
24247
24405
  }), CameraSwitchGroupSchema, {
24248
24406
  kind: "mutation",
24249
24407
  auth: "admin"
24250
- }), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), DiskReconcileJobSchema, {
24408
+ }), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(SetCameraSensorsInputSchema, object({ success: literal(true) }), {
24409
+ kind: "mutation",
24410
+ auth: "admin"
24411
+ }), method(_void(), DiskReconcileJobSchema, {
24251
24412
  kind: "mutation",
24252
24413
  auth: "admin"
24253
24414
  }), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
@@ -30072,7 +30233,9 @@ object({
30072
30233
  detected: boolean(),
30073
30234
  timestamp: number(),
30074
30235
  source: MotionSourceEnum,
30075
- regions: array(MotionRegionSchema).readonly().optional()
30236
+ regions: array(MotionRegionSchema).readonly().optional(),
30237
+ /** Who started it (D672). Absent = the camera itself. Only `onboard` carries `sensor`. */
30238
+ triggeredBy: MotionTriggeredBySchema.optional()
30076
30239
  });
30077
30240
  DeviceType.Camera, DeviceType.Sensor, method(object({ deviceId: number() }), boolean());
30078
30241
  object({
@@ -35146,47 +35309,6 @@ DeviceType.Camera, method(object({
35146
35309
  detection: array(ZoneRuleSchema).readonly(),
35147
35310
  package: array(ZoneRuleSchema).readonly()
35148
35311
  });
35149
- var NUMBER = {
35150
- kind: "fixed",
35151
- result: ["number"]
35152
- };
35153
- var BOOLEAN = {
35154
- kind: "fixed",
35155
- result: ["boolean"]
35156
- };
35157
- Object.freeze({
35158
- min: NUMBER,
35159
- max: NUMBER,
35160
- abs: NUMBER,
35161
- floor: NUMBER,
35162
- ceil: NUMBER,
35163
- sqrt: NUMBER,
35164
- round: NUMBER,
35165
- pow: NUMBER,
35166
- clamp: NUMBER,
35167
- avg: NUMBER,
35168
- sum: NUMBER,
35169
- age: NUMBER,
35170
- convert: NUMBER,
35171
- coalesce: { kind: "coalesce" },
35172
- any: BOOLEAN,
35173
- all: BOOLEAN,
35174
- within: BOOLEAN,
35175
- latest: {
35176
- kind: "fixed",
35177
- result: ["number", "null"]
35178
- },
35179
- number: {
35180
- kind: "fixed",
35181
- result: ["number", "null"]
35182
- },
35183
- rose: {
35184
- kind: "fixed",
35185
- result: ["number", "null"]
35186
- },
35187
- count: NUMBER
35188
- });
35189
- `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
35190
35312
  /**
35191
35313
  * Accessory device helpers — shared across drivers.
35192
35314
  *
@@ -37609,6 +37731,12 @@ Object.freeze({
37609
37731
  addonId: null,
37610
37732
  access: "view"
37611
37733
  },
37734
+ "coreBlocks.listComposableCaps": {
37735
+ capName: "core-blocks",
37736
+ capScope: "system",
37737
+ addonId: null,
37738
+ access: "view"
37739
+ },
37612
37740
  "coreBlocks.listCompositions": {
37613
37741
  capName: "core-blocks",
37614
37742
  capScope: "system",
@@ -40579,12 +40707,6 @@ Object.freeze({
40579
40707
  addonId: null,
40580
40708
  access: "view"
40581
40709
  },
40582
- "pipelineAnalytics.getSummary": {
40583
- capName: "pipeline-analytics",
40584
- capScope: "device",
40585
- addonId: null,
40586
- access: "view"
40587
- },
40588
40710
  "pipelineAnalytics.getTrack": {
40589
40711
  capName: "pipeline-analytics",
40590
40712
  capScope: "device",
@@ -40645,12 +40767,6 @@ Object.freeze({
40645
40767
  addonId: null,
40646
40768
  access: "view"
40647
40769
  },
40648
- "pipelineAnalytics.listRecentSummaries": {
40649
- capName: "pipeline-analytics",
40650
- capScope: "device",
40651
- addonId: null,
40652
- access: "view"
40653
- },
40654
40770
  "pipelineAnalytics.listRecentTracks": {
40655
40771
  capName: "pipeline-analytics",
40656
40772
  capScope: "device",
@@ -40681,12 +40797,6 @@ Object.freeze({
40681
40797
  addonId: null,
40682
40798
  access: "view"
40683
40799
  },
40684
- "pipelineAnalytics.listSummaries": {
40685
- capName: "pipeline-analytics",
40686
- capScope: "device",
40687
- addonId: null,
40688
- access: "view"
40689
- },
40690
40800
  "pipelineAnalytics.listTrackMedia": {
40691
40801
  capName: "pipeline-analytics",
40692
40802
  capScope: "device",
@@ -41311,6 +41421,12 @@ Object.freeze({
41311
41421
  addonId: null,
41312
41422
  access: "create"
41313
41423
  },
41424
+ "pipelineOrchestrator.setCameraSensors": {
41425
+ capName: "pipeline-orchestrator",
41426
+ capScope: "system",
41427
+ addonId: null,
41428
+ access: "create"
41429
+ },
41314
41430
  "pipelineOrchestrator.setCameraStepOverride": {
41315
41431
  capName: "pipeline-orchestrator",
41316
41432
  capScope: "system",
@@ -44856,11 +44972,6 @@ Object.freeze({
44856
44972
  form: "single",
44857
44973
  optional: true
44858
44974
  }],
44859
- "pipelineAnalytics.listRecentSummaries": [{
44860
- name: "deviceIds",
44861
- form: "array",
44862
- optional: false
44863
- }],
44864
44975
  "pipelineAnalytics.listRecentTracks": [{
44865
44976
  name: "deviceIds",
44866
44977
  form: "array",
@@ -44871,11 +44982,6 @@ Object.freeze({
44871
44982
  form: "array",
44872
44983
  optional: true
44873
44984
  }],
44874
- "pipelineAnalytics.listSummaries": [{
44875
- name: "deviceIds",
44876
- form: "array",
44877
- optional: false
44878
- }],
44879
44985
  "pipelineAnalytics.listTrackMedia": [{
44880
44986
  name: "deviceId",
44881
44987
  form: "single",
@@ -45031,6 +45137,11 @@ Object.freeze({
45031
45137
  form: "single",
45032
45138
  optional: false
45033
45139
  }],
45140
+ "pipelineOrchestrator.setCameraSensors": [{
45141
+ name: "deviceId",
45142
+ form: "single",
45143
+ optional: false
45144
+ }],
45034
45145
  "pipelineOrchestrator.setCameraStepOverride": [{
45035
45146
  name: "deviceId",
45036
45147
  form: "single",
@@ -46687,7 +46798,7 @@ var AgentUIAddon = class extends BaseAddon {
46687
46798
  capability: adminUiCapability,
46688
46799
  provider: {
46689
46800
  getStaticDir: async () => ({ staticDir: path.resolve(__dirname) }),
46690
- getVersion: async () => ({ version: "1.2.155" })
46801
+ getVersion: async () => ({ version: "1.2.157" })
46691
46802
  }
46692
46803
  }];
46693
46804
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-agent-ui",
3
- "version": "1.2.155",
3
+ "version": "1.2.157",
4
4
  "description": "Agent UI — lightweight status dashboard served by every agent node",
5
5
  "keywords": [
6
6
  "camstack",