@camstack/addon-provider-gree 0.2.99 → 0.2.100

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
@@ -5349,7 +5349,7 @@ var ZodIssueCode = {
5349
5349
  var ZodFirstPartyTypeKind;
5350
5350
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5351
5351
  //#endregion
5352
- //#region ../types/dist/sleep-CnLyvg3w.mjs
5352
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5353
5353
  /**
5354
5354
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5355
5355
  * window to float samples (D455).
@@ -7378,7 +7378,23 @@ var EncodeProfileSchema = object({
7378
7378
  "zerolatency",
7379
7379
  "film",
7380
7380
  "animation"
7381
- ]).optional()
7381
+ ]).optional(),
7382
+ /**
7383
+ * ONE slice per access unit.
7384
+ *
7385
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
7386
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
7387
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
7388
+ * frame-end per frame: the first pictures render and the video then freezes
7389
+ * for good while the audio, on its own plane, plays on. Measured on this hub
7390
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
7391
+ *
7392
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
7393
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
7394
+ * consumers meaning the same thing spelled differently would stop sharing one
7395
+ * child. Absent means "whatever the encoder does" — today's behaviour.
7396
+ */
7397
+ singleSlicePerFrame: boolean().optional()
7382
7398
  }),
7383
7399
  audio: union([literal("passthrough"), object({
7384
7400
  codec: _enum([
@@ -7428,7 +7444,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
7428
7444
  audio: "passthrough"
7429
7445
  };
7430
7446
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7431
- ({ ...BASE_LIVE_EGRESS_PROFILE });
7447
+ ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7432
7448
  /**
7433
7449
  * Deep wiring healthcheck — snapshot of active reachability probes across
7434
7450
  * every declared capability + widget of every installed plugin, on every
@@ -20133,13 +20149,109 @@ var SummariesQueryInput = object({
20133
20149
  deviceIds: array(number()).min(1),
20134
20150
  since: number().optional(),
20135
20151
  until: number().optional(),
20136
- limit: number().int().min(1).max(200).default(50)
20152
+ limit: number().int().min(1).max(200).default(50),
20153
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
20154
+ * meaning of "dog" on every summary surface. */
20155
+ classes: array(string().min(1).max(60)).max(40).optional()
20137
20156
  });
20138
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
20139
20157
  var SummaryDetailSchema = object({
20140
20158
  summary: SummarySchema,
20141
- /** Member track ids, in absorption order. */
20142
- trackIds: array(string()).readonly()
20159
+ /** Members in absorption order, oldest join first. */
20160
+ members: array(object({
20161
+ trackId: string(),
20162
+ /**
20163
+ * The track's class at the time it JOINED, or `null` when it was unresolved
20164
+ * and the track no longer exists to fill it.
20165
+ *
20166
+ * The join, not the latest opinion: class voting can re-decide a track's
20167
+ * class afterwards, and a group's composition is the history of who came
20168
+ * together. A consumer counts these to badge a thumbnail — "two people and a
20169
+ * dog" — rather than being handed a second, denormalised tally that would
20170
+ * drift from the members it claims to describe.
20171
+ */
20172
+ className: string().nullable(),
20173
+ /** `primary` is the one member that carries the group's identity. */
20174
+ role: _enum([
20175
+ "primary",
20176
+ "sibling",
20177
+ "child"
20178
+ ]).nullable(),
20179
+ joinedAt: number(),
20180
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
20181
+ * at read time. Absent when nothing resolved, never an empty string. */
20182
+ label: string().optional(),
20183
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
20184
+ * recognised nobody, and absent for a member whose track has been pruned —
20185
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
20186
+ subLabel: string().optional()
20187
+ })).readonly()
20188
+ });
20189
+ /**
20190
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
20191
+ *
20192
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
20193
+ * we extend when we notice we need more". Enough to draw the card ("two people
20194
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
20195
+ * are on {@link getSummary}: shipping every member of every group so a client
20196
+ * can count them turns a page of two hundred groups into a thousand records to
20197
+ * draw two hundred badges.
20198
+ */
20199
+ var SummaryListItemSchema = object({
20200
+ summary: SummarySchema,
20201
+ /** Class counts, most numerous first; `className: null` is a member whose
20202
+ * class never resolved, counted so the counts add up to `memberCount`. */
20203
+ classes: array(object({
20204
+ className: string().nullable(),
20205
+ count: number().int().positive()
20206
+ })).readonly()
20207
+ });
20208
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
20209
+ /**
20210
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
20211
+ *
20212
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
20213
+ * summaries feed that could only answer "the last N for these cameras"
20214
+ * (`listSummaries`) would make groups available in the timeline and nowhere
20215
+ * else. Two feeds with different ordering contracts would be two ideas of what
20216
+ * "newest" means, on one screen.
20217
+ */
20218
+ var RecentSummariesQueryInput = object({
20219
+ /** Devices to merge. An empty array yields an empty page. */
20220
+ deviceIds: array(number()),
20221
+ /** Window lower bound on `lastActiveAt` (inclusive). */
20222
+ since: number().optional(),
20223
+ /** Window upper bound on `lastActiveAt` (inclusive). */
20224
+ until: number().optional(),
20225
+ limit: number().int().min(1).max(500).default(100),
20226
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
20227
+ cursor: string().optional(),
20228
+ /**
20229
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
20230
+ * deliberately the same name: the events filter tree emits one list of ticked
20231
+ * taxonomy nodes, and a summaries feed that called it something else would
20232
+ * make the operator's one filter component produce two shapes.
20233
+ *
20234
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
20235
+ * me the arrivals with a dog" has to return the group of four people who had
20236
+ * one with them; a filter on the group's own fields could only ever answer
20237
+ * about its primary.
20238
+ *
20239
+ * Matched against all THREE tiers of a member — the class (`animal`), the
20240
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
20241
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
20242
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
20243
+ * that read only the class would answer "animal" and never "dog".
20244
+ *
20245
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
20246
+ */
20247
+ classes: array(string().min(1).max(60)).max(40).optional()
20248
+ });
20249
+ var RecentSummariesPageSchema = object({
20250
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
20251
+ * {@link listSummaries} — one list shape, so a card renders the same
20252
+ * whichever feed drew it. */
20253
+ summaries: array(SummaryListItemSchema).readonly(),
20254
+ nextCursor: string().nullable()
20143
20255
  });
20144
20256
  var RecentTracksPageSchema = object({
20145
20257
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20553,7 +20665,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20553
20665
  classes: array(string()).optional(),
20554
20666
  /** See {@link ExcludeSourcesDoc}. */
20555
20667
  excludeSources: array(TrackSourceSchema).optional()
20556
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20668
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20557
20669
  kind: "mutation",
20558
20670
  auth: "admin"
20559
20671
  }), 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({
@@ -32306,7 +32418,29 @@ var LoggingSettingsPatchSchema = object({
32306
32418
  * disarmed the channels it did not mention would make the Levels page and
32307
32419
  * the Diagnostics page fight over the same value.
32308
32420
  */
32309
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
32421
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
32422
+ /**
32423
+ * How many days the analytics OPS LOG is kept — the audit trail of what
32424
+ * retention actually deleted.
32425
+ *
32426
+ * It lives HERE, on the logging document, and not on the analytics addon's
32427
+ * settings form, on the operator's instruction: every other log-ish window in
32428
+ * this system is configured through this one document, and a retention audit
32429
+ * configured somewhere else is a second place to look when the question is
32430
+ * "what happened to my data".
32431
+ *
32432
+ * That placement has a cost worth stating: this document ships in the
32433
+ * framework closure, so this knob needs a published `@camstack/server` while
32434
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
32435
+ *
32436
+ * `retention-model.ts` argued this one must NOT follow a device window,
32437
+ * because the audit trail is the history of the SWEEP and not of the camera.
32438
+ * That argument stands and is untouched — what changes is that 30 days stops
32439
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
32440
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
32441
+ * state in which the next 9.5 GiB leak cannot be explained.
32442
+ */
32443
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
32310
32444
  });
32311
32445
  /**
32312
32446
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -32379,6 +32513,14 @@ var LoggingSettingsStateSchema = object({
32379
32513
  channels: array(LogChannelDescriptorSchema).readonly(),
32380
32514
  /** The channels ARMED right now, each with its deadline. */
32381
32515
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
32516
+ /**
32517
+ * The ops-log window in days, as it is in force. `0` = kept forever.
32518
+ *
32519
+ * Reported rather than left to the caller's memory of what it wrote: this is
32520
+ * the value the analytics sweep will actually use, and an operator asking how
32521
+ * long their audit trail lives should not have to infer it from a default.
32522
+ */
32523
+ opsLogRetentionDays: number().int().min(0),
32382
32524
  persisted: boolean()
32383
32525
  });
32384
32526
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
@@ -38379,6 +38521,12 @@ Object.freeze({
38379
38521
  addonId: null,
38380
38522
  access: "view"
38381
38523
  },
38524
+ "pipelineAnalytics.listRecentSummaries": {
38525
+ capName: "pipeline-analytics",
38526
+ capScope: "device",
38527
+ addonId: null,
38528
+ access: "view"
38529
+ },
38382
38530
  "pipelineAnalytics.listRecentTracks": {
38383
38531
  capName: "pipeline-analytics",
38384
38532
  capScope: "device",
@@ -42206,6 +42354,11 @@ Object.freeze({
42206
42354
  form: "single",
42207
42355
  optional: true
42208
42356
  }],
42357
+ "pipelineAnalytics.listRecentSummaries": [{
42358
+ name: "deviceIds",
42359
+ form: "array",
42360
+ optional: false
42361
+ }],
42209
42362
  "pipelineAnalytics.listRecentTracks": [{
42210
42363
  name: "deviceIds",
42211
42364
  form: "array",
package/dist/addon.mjs CHANGED
@@ -5348,7 +5348,7 @@ var ZodIssueCode = {
5348
5348
  var ZodFirstPartyTypeKind;
5349
5349
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5350
5350
  //#endregion
5351
- //#region ../types/dist/sleep-CnLyvg3w.mjs
5351
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5352
5352
  /**
5353
5353
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5354
5354
  * window to float samples (D455).
@@ -7377,7 +7377,23 @@ var EncodeProfileSchema = object({
7377
7377
  "zerolatency",
7378
7378
  "film",
7379
7379
  "animation"
7380
- ]).optional()
7380
+ ]).optional(),
7381
+ /**
7382
+ * ONE slice per access unit.
7383
+ *
7384
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
7385
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
7386
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
7387
+ * frame-end per frame: the first pictures render and the video then freezes
7388
+ * for good while the audio, on its own plane, plays on. Measured on this hub
7389
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
7390
+ *
7391
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
7392
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
7393
+ * consumers meaning the same thing spelled differently would stop sharing one
7394
+ * child. Absent means "whatever the encoder does" — today's behaviour.
7395
+ */
7396
+ singleSlicePerFrame: boolean().optional()
7381
7397
  }),
7382
7398
  audio: union([literal("passthrough"), object({
7383
7399
  codec: _enum([
@@ -7427,7 +7443,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
7427
7443
  audio: "passthrough"
7428
7444
  };
7429
7445
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7430
- ({ ...BASE_LIVE_EGRESS_PROFILE });
7446
+ ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7431
7447
  /**
7432
7448
  * Deep wiring healthcheck — snapshot of active reachability probes across
7433
7449
  * every declared capability + widget of every installed plugin, on every
@@ -20132,13 +20148,109 @@ var SummariesQueryInput = object({
20132
20148
  deviceIds: array(number()).min(1),
20133
20149
  since: number().optional(),
20134
20150
  until: number().optional(),
20135
- limit: number().int().min(1).max(200).default(50)
20151
+ limit: number().int().min(1).max(200).default(50),
20152
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
20153
+ * meaning of "dog" on every summary surface. */
20154
+ classes: array(string().min(1).max(60)).max(40).optional()
20136
20155
  });
20137
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
20138
20156
  var SummaryDetailSchema = object({
20139
20157
  summary: SummarySchema,
20140
- /** Member track ids, in absorption order. */
20141
- trackIds: array(string()).readonly()
20158
+ /** Members in absorption order, oldest join first. */
20159
+ members: array(object({
20160
+ trackId: string(),
20161
+ /**
20162
+ * The track's class at the time it JOINED, or `null` when it was unresolved
20163
+ * and the track no longer exists to fill it.
20164
+ *
20165
+ * The join, not the latest opinion: class voting can re-decide a track's
20166
+ * class afterwards, and a group's composition is the history of who came
20167
+ * together. A consumer counts these to badge a thumbnail — "two people and a
20168
+ * dog" — rather than being handed a second, denormalised tally that would
20169
+ * drift from the members it claims to describe.
20170
+ */
20171
+ className: string().nullable(),
20172
+ /** `primary` is the one member that carries the group's identity. */
20173
+ role: _enum([
20174
+ "primary",
20175
+ "sibling",
20176
+ "child"
20177
+ ]).nullable(),
20178
+ joinedAt: number(),
20179
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
20180
+ * at read time. Absent when nothing resolved, never an empty string. */
20181
+ label: string().optional(),
20182
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
20183
+ * recognised nobody, and absent for a member whose track has been pruned —
20184
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
20185
+ subLabel: string().optional()
20186
+ })).readonly()
20187
+ });
20188
+ /**
20189
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
20190
+ *
20191
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
20192
+ * we extend when we notice we need more". Enough to draw the card ("two people
20193
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
20194
+ * are on {@link getSummary}: shipping every member of every group so a client
20195
+ * can count them turns a page of two hundred groups into a thousand records to
20196
+ * draw two hundred badges.
20197
+ */
20198
+ var SummaryListItemSchema = object({
20199
+ summary: SummarySchema,
20200
+ /** Class counts, most numerous first; `className: null` is a member whose
20201
+ * class never resolved, counted so the counts add up to `memberCount`. */
20202
+ classes: array(object({
20203
+ className: string().nullable(),
20204
+ count: number().int().positive()
20205
+ })).readonly()
20206
+ });
20207
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
20208
+ /**
20209
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
20210
+ *
20211
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
20212
+ * summaries feed that could only answer "the last N for these cameras"
20213
+ * (`listSummaries`) would make groups available in the timeline and nowhere
20214
+ * else. Two feeds with different ordering contracts would be two ideas of what
20215
+ * "newest" means, on one screen.
20216
+ */
20217
+ var RecentSummariesQueryInput = object({
20218
+ /** Devices to merge. An empty array yields an empty page. */
20219
+ deviceIds: array(number()),
20220
+ /** Window lower bound on `lastActiveAt` (inclusive). */
20221
+ since: number().optional(),
20222
+ /** Window upper bound on `lastActiveAt` (inclusive). */
20223
+ until: number().optional(),
20224
+ limit: number().int().min(1).max(500).default(100),
20225
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
20226
+ cursor: string().optional(),
20227
+ /**
20228
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
20229
+ * deliberately the same name: the events filter tree emits one list of ticked
20230
+ * taxonomy nodes, and a summaries feed that called it something else would
20231
+ * make the operator's one filter component produce two shapes.
20232
+ *
20233
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
20234
+ * me the arrivals with a dog" has to return the group of four people who had
20235
+ * one with them; a filter on the group's own fields could only ever answer
20236
+ * about its primary.
20237
+ *
20238
+ * Matched against all THREE tiers of a member — the class (`animal`), the
20239
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
20240
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
20241
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
20242
+ * that read only the class would answer "animal" and never "dog".
20243
+ *
20244
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
20245
+ */
20246
+ classes: array(string().min(1).max(60)).max(40).optional()
20247
+ });
20248
+ var RecentSummariesPageSchema = object({
20249
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
20250
+ * {@link listSummaries} — one list shape, so a card renders the same
20251
+ * whichever feed drew it. */
20252
+ summaries: array(SummaryListItemSchema).readonly(),
20253
+ nextCursor: string().nullable()
20142
20254
  });
20143
20255
  var RecentTracksPageSchema = object({
20144
20256
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20552,7 +20664,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20552
20664
  classes: array(string()).optional(),
20553
20665
  /** See {@link ExcludeSourcesDoc}. */
20554
20666
  excludeSources: array(TrackSourceSchema).optional()
20555
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20667
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20556
20668
  kind: "mutation",
20557
20669
  auth: "admin"
20558
20670
  }), 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({
@@ -32305,7 +32417,29 @@ var LoggingSettingsPatchSchema = object({
32305
32417
  * disarmed the channels it did not mention would make the Levels page and
32306
32418
  * the Diagnostics page fight over the same value.
32307
32419
  */
32308
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
32420
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
32421
+ /**
32422
+ * How many days the analytics OPS LOG is kept — the audit trail of what
32423
+ * retention actually deleted.
32424
+ *
32425
+ * It lives HERE, on the logging document, and not on the analytics addon's
32426
+ * settings form, on the operator's instruction: every other log-ish window in
32427
+ * this system is configured through this one document, and a retention audit
32428
+ * configured somewhere else is a second place to look when the question is
32429
+ * "what happened to my data".
32430
+ *
32431
+ * That placement has a cost worth stating: this document ships in the
32432
+ * framework closure, so this knob needs a published `@camstack/server` while
32433
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
32434
+ *
32435
+ * `retention-model.ts` argued this one must NOT follow a device window,
32436
+ * because the audit trail is the history of the SWEEP and not of the camera.
32437
+ * That argument stands and is untouched — what changes is that 30 days stops
32438
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
32439
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
32440
+ * state in which the next 9.5 GiB leak cannot be explained.
32441
+ */
32442
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
32309
32443
  });
32310
32444
  /**
32311
32445
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -32378,6 +32512,14 @@ var LoggingSettingsStateSchema = object({
32378
32512
  channels: array(LogChannelDescriptorSchema).readonly(),
32379
32513
  /** The channels ARMED right now, each with its deadline. */
32380
32514
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
32515
+ /**
32516
+ * The ops-log window in days, as it is in force. `0` = kept forever.
32517
+ *
32518
+ * Reported rather than left to the caller's memory of what it wrote: this is
32519
+ * the value the analytics sweep will actually use, and an operator asking how
32520
+ * long their audit trail lives should not have to infer it from a default.
32521
+ */
32522
+ opsLogRetentionDays: number().int().min(0),
32381
32523
  persisted: boolean()
32382
32524
  });
32383
32525
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
@@ -38378,6 +38520,12 @@ Object.freeze({
38378
38520
  addonId: null,
38379
38521
  access: "view"
38380
38522
  },
38523
+ "pipelineAnalytics.listRecentSummaries": {
38524
+ capName: "pipeline-analytics",
38525
+ capScope: "device",
38526
+ addonId: null,
38527
+ access: "view"
38528
+ },
38381
38529
  "pipelineAnalytics.listRecentTracks": {
38382
38530
  capName: "pipeline-analytics",
38383
38531
  capScope: "device",
@@ -42205,6 +42353,11 @@ Object.freeze({
42205
42353
  form: "single",
42206
42354
  optional: true
42207
42355
  }],
42356
+ "pipelineAnalytics.listRecentSummaries": [{
42357
+ name: "deviceIds",
42358
+ form: "array",
42359
+ optional: false
42360
+ }],
42208
42361
  "pipelineAnalytics.listRecentTracks": [{
42209
42362
  name: "deviceIds",
42210
42363
  form: "array",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-gree",
3
- "version": "0.2.99",
3
+ "version": "0.2.100",
4
4
  "description": "Gree air-conditioner device-provider addon for CamStack — wraps the @apocaliss92/nodegree local-UDP client (LAN discovery + AES control), exposing climate-control and fan-control",
5
5
  "keywords": [
6
6
  "camstack",