camstack 1.2.28 → 1.2.30

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.
@@ -14522,7 +14522,7 @@ function date4(params) {
14522
14522
  // ../../node_modules/zod/v4/classic/external.js
14523
14523
  config(en_default());
14524
14524
 
14525
- // ../types/dist/sleep-BxO5xNe6.mjs
14525
+ // ../types/dist/sleep-zMxKWD0M.mjs
14526
14526
  var WELL_KNOWN_TABS = [
14527
14527
  {
14528
14528
  id: "overview",
@@ -15601,6 +15601,11 @@ var RecordingBandSchema = external_exports.object({
15601
15601
  preBufferSec: external_exports.number().min(0).optional(),
15602
15602
  postBufferSec: external_exports.number().min(0).optional()
15603
15603
  });
15604
+ var DEFAULT_EVENTS_BAND_BUFFER_SEC = {
15605
+ preBufferSec: 10,
15606
+ postBufferSec: 30
15607
+ };
15608
+ var VISIT_MERGE_GAP_MS = DEFAULT_EVENTS_BAND_BUFFER_SEC.postBufferSec * 1e3;
15604
15609
  var RecordingRetentionSchema = external_exports.object({
15605
15610
  maxAgeDays: external_exports.number().min(0).optional(),
15606
15611
  maxSizeGb: external_exports.number().min(0).optional()
@@ -15617,6 +15622,12 @@ var RecordingConfigSchema = external_exports.object({
15617
15622
  /** DERIVED summary of `bands`, stamped by the recorder on every save.
15618
15623
  * Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
15619
15624
  mode: RecordingStorageModeSchema.optional(),
15625
+ /**
15626
+ * Which assigned broker slots to record. Absent / empty = {@link
15627
+ * DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
15628
+ * camera's currently assigned slots — never `mid` unless the operator
15629
+ * picks it, and never a slot the broker has not assigned.
15630
+ */
15620
15631
  profiles: external_exports.array(CamProfileSchema).optional(),
15621
15632
  segmentSeconds: external_exports.number().int().positive().optional(),
15622
15633
  /**
@@ -15679,7 +15690,11 @@ var RelocateFootageInputSchema = external_exports.object({
15679
15690
  profiles: external_exports.array(external_exports.string()).optional(),
15680
15691
  /** Copy throttle in MB/s (default 40) — the drain is a background chore,
15681
15692
  * never allowed to starve live writers. */
15682
- throttleMbps: external_exports.number().min(1).max(1e3).optional()
15693
+ throttleMbps: external_exports.number().min(1).max(1e3).optional(),
15694
+ /** Move only segments whose startMs is >= this. Absent = the whole source
15695
+ * pile. Used when a full drain is too expensive and the operator only
15696
+ * wants the recent window on the new disk. */
15697
+ sinceMs: external_exports.number().int().optional()
15683
15698
  });
15684
15699
  var StorageMigrationLeaseInputSchema = external_exports.object({ leaseId: external_exports.string().min(1) });
15685
15700
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: external_exports.string().min(1) });
@@ -22666,6 +22681,7 @@ var NcSystemEventKindSchema = external_exports.enum([
22666
22681
  "node-offline",
22667
22682
  "node-inference-unavailable",
22668
22683
  "detection-blind",
22684
+ "addon-crash-loop",
22669
22685
  "addon-update-available",
22670
22686
  "server-update-available",
22671
22687
  "alarm-triggered",
@@ -22673,6 +22689,9 @@ var NcSystemEventKindSchema = external_exports.enum([
22673
22689
  "alarm-disarmed",
22674
22690
  "alarm-arming",
22675
22691
  "alarm-arm-refused",
22692
+ "addon-updated",
22693
+ "server-updated",
22694
+ "export-completed",
22676
22695
  "camera-online",
22677
22696
  "camera-offline",
22678
22697
  "camera-disabled",
@@ -24086,7 +24105,9 @@ var TrackFlagFields = {
24086
24105
  * `'staging'`. */
24087
24106
  markForTrain: external_exports.boolean().optional(),
24088
24107
  /** Operator marked this track for diagnostic attention. */
24089
- debug: external_exports.boolean().optional()
24108
+ debug: external_exports.boolean().optional(),
24109
+ /** Operator favourited this track. Pins it against pruning. */
24110
+ favourited: external_exports.boolean().optional()
24090
24111
  };
24091
24112
  var TrackRetrainFields = { retrainStatus: RetrainStatusSchema.optional() };
24092
24113
  var TrackFlagsPatchSchema = external_exports.object(TrackFlagFields);
@@ -24094,6 +24115,7 @@ var TrackFlagsSchema = external_exports.object({
24094
24115
  trackId: external_exports.string(),
24095
24116
  markForTrain: external_exports.boolean(),
24096
24117
  debug: external_exports.boolean(),
24118
+ favourited: external_exports.boolean(),
24097
24119
  /** The lifecycle state the boolean was derived from. Required here (unlike on
24098
24120
  * a track row) because this shape is only ever produced by the write body,
24099
24121
  * which always knows it — and a surface that has just written needs to render
@@ -24616,6 +24638,11 @@ var TrackCascadeCountsSchema = external_exports.object({
24616
24638
  /** Per-track CLIP search vectors removed (best-effort). */
24617
24639
  embeddings: external_exports.number().int()
24618
24640
  });
24641
+ var DiskReconcileCountsSchema = external_exports.object({
24642
+ mediaDropped: external_exports.number().int(),
24643
+ tracks: external_exports.number().int(),
24644
+ events: external_exports.number().int()
24645
+ });
24619
24646
  var EventStoreDeviceFootprintSchema = external_exports.object({
24620
24647
  deviceId: external_exports.number(),
24621
24648
  /** Persisted event rows (motion + object + audio) for the camera. */
@@ -24872,6 +24899,17 @@ var pipelineAnalyticsCapability = {
24872
24899
  auth: "admin"
24873
24900
  }),
24874
24901
  /**
24902
+ * Disk-wins reconcile for one camera. Drops media index rows whose blobs
24903
+ * are gone, then cascades tracks (including favourited and staging) that
24904
+ * have no remaining files. Enrolled identity/vehicle/scene media is never
24905
+ * probed. Trackless motion/audio events with no remaining file are dropped,
24906
+ * including snapshot-less rows.
24907
+ */
24908
+ reconcileFromDisk: method(external_exports.object({ deviceId: external_exports.number() }), DiskReconcileCountsSchema, {
24909
+ kind: "mutation",
24910
+ auth: "admin"
24911
+ }),
24912
+ /**
24875
24913
  * Delete whole tracks (object events) by id for the given device,
24876
24914
  * cascading their media in lockstep. Returns the number of tracks
24877
24915
  * actually deleted plus the ids that could not be removed.
@@ -24890,7 +24928,7 @@ var pipelineAnalyticsCapability = {
24890
24928
  auth: "admin"
24891
24929
  }),
24892
24930
  /**
24893
- * Set the per-track operator flags (`markForTrain`, `debug`) on ONE track.
24931
+ * Set the per-track operator flags (`markForTrain`, `debug`, `favourited`) on ONE track.
24894
24932
  * The patch is PARTIAL — an omitted key is left untouched — because the
24895
24933
  * three surfaces that write it (admin Events grid, viewer track detail,
24896
24934
  * viewer cluster detail) each own one toggle and must not clobber the other.
@@ -26878,6 +26916,24 @@ var CameraStatusDegradationSchema = external_exports.object({
26878
26916
  /** Wall-clock ms spent on the stage before it was abandoned. */
26879
26917
  elapsedMs: external_exports.number()
26880
26918
  });
26919
+ var DiskReconcileJobSchema = external_exports.object({
26920
+ state: external_exports.enum([
26921
+ "idle",
26922
+ "running",
26923
+ "done",
26924
+ "error"
26925
+ ]),
26926
+ total: external_exports.number().int().nonnegative(),
26927
+ completed: external_exports.number().int().nonnegative(),
26928
+ currentDeviceId: external_exports.number().int().nullable(),
26929
+ failed: external_exports.array(external_exports.number().int()).readonly(),
26930
+ mediaDropped: external_exports.number().int().nonnegative(),
26931
+ tracks: external_exports.number().int().nonnegative(),
26932
+ events: external_exports.number().int().nonnegative(),
26933
+ startedAtMs: external_exports.number().int().nullable(),
26934
+ finishedAtMs: external_exports.number().int().nullable(),
26935
+ error: external_exports.string().nullable()
26936
+ });
26881
26937
  var CameraStatusSchema = external_exports.object({
26882
26938
  deviceId: external_exports.number(),
26883
26939
  assignment: CameraAssignmentStatusSchema,
@@ -27402,6 +27458,18 @@ var pipelineOrchestratorCapability = {
27402
27458
  * rail without issuing N parallel browser round-trips.
27403
27459
  */
27404
27460
  getCameraStatuses: method(external_exports.object({ deviceIds: external_exports.array(external_exports.number()).optional() }), external_exports.array(CameraStatusSchema).readonly()),
27461
+ /**
27462
+ * Disk-wins fleet reconcile after a recordings wipe. Starts the walk in
27463
+ * the addon process and returns immediately with the job snapshot — the
27464
+ * work outlives the tRPC/UDS 60s timeout. Poll `getReconcileFromDiskStatus`.
27465
+ * Admin-only. Idempotent while a job is already running.
27466
+ */
27467
+ reconcileFromDisk: method(external_exports.void(), DiskReconcileJobSchema, {
27468
+ kind: "mutation",
27469
+ auth: "admin"
27470
+ }),
27471
+ /** Snapshot of the in-flight or last disk-wins fleet reconcile. */
27472
+ getReconcileFromDiskStatus: method(external_exports.void(), DiskReconcileJobSchema, { auth: "admin" }),
27405
27473
  /** List every template the operator has saved. */
27406
27474
  listTemplates: method(external_exports.void(), external_exports.array(PipelineTemplateSchema).readonly()),
27407
27475
  /** Create a new named preset from a given CameraPipelineConfig. */
@@ -28541,8 +28609,65 @@ var ClipSchema = external_exports.object({
28541
28609
  startMs: external_exports.number(),
28542
28610
  endMs: external_exports.number()
28543
28611
  }),
28544
- /** Thumbnail URL (lazy; e.g. analytics `getEventMedia`). Never inlined. */
28545
- thumbnail: external_exports.string().optional()
28612
+ /**
28613
+ * Distinct object classes attached to this visit (`person`, `car`, …),
28614
+ * dominant first, capped at {@link MAX_CLIP_LABELS}. The ribbon renders these
28615
+ * instead of the bare kind — "Object" tells the operator nothing a colour bar
28616
+ * did not. Absent (never empty) when the visit attached no classified object
28617
+ * event, so a motion/audio-only visit keeps its kind label.
28618
+ */
28619
+ labels: external_exports.array(external_exports.string()).max(3).optional(),
28620
+ /**
28621
+ * Lazy thumbnail URL, never inlined.
28622
+ *
28623
+ * Recording-derived clips (events-mode keep-window, and the prepared
28624
+ * continuous event+fragment visit) MUST use the snapshot of the **main
28625
+ * event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
28626
+ * of the event that owns `kind` (object > motion > audio). Do not extract
28627
+ * a keyframe from the recorded segments. Other providers (onboard, HKSV)
28628
+ * mint their own stills.
28629
+ *
28630
+ * **VOUCHED, never fabricated.** A provider emits this only for an event it
28631
+ * has CONFIRMED owns at least one media row (its own, or its owning track's).
28632
+ * Absent is meaningful — "this visit has no event still" — never "we did not
28633
+ * look". Stamping it from a URL template made 35% of one camera's clips point
28634
+ * at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
28635
+ * A read that FAILS drops the claim; it never invents it.
28636
+ */
28637
+ thumbnail: external_exports.string().optional(),
28638
+ /**
28639
+ * An instant INSIDE this visit's footage, hole-safe, where a recorded still
28640
+ * can be decoded. Present whenever the visit came from recorded availability;
28641
+ * absent on a per-event padded window (there is no footage to promise).
28642
+ *
28643
+ * This is not a thumbnail and not a second byte path: it is the argument to
28644
+ * the recorder's existing still route. The surface — never the provider —
28645
+ * decides whether to use it. It is the midpoint of the visit's LONGEST
28646
+ * contiguous range, not of the visit: a visit spans its holes by
28647
+ * construction, so a naive midpoint lands in dead air.
28648
+ */
28649
+ stillAtMs: external_exports.number().optional(),
28650
+ /**
28651
+ * Analytics event ids that overlap this visit — a BOUNDED sample, newest
28652
+ * first within kind (object → motion → audio), capped at
28653
+ * {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
28654
+ *
28655
+ * Bounded because it is not a payload the surface pages through: one visit on
28656
+ * device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
28657
+ * camera-day. Read {@link eventCount} for the true total.
28658
+ */
28659
+ eventIds: external_exports.array(external_exports.string()).optional(),
28660
+ /** How many analytics events actually overlap this visit. Differs from
28661
+ * `eventIds.length` exactly when the sample was capped — so a truncated
28662
+ * list is never mistaken for a quiet visit. */
28663
+ eventCount: external_exports.number().int().nonnegative().optional(),
28664
+ /** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
28665
+ * than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
28666
+ * bar keeps showing them via `recording.getAvailability`. */
28667
+ holes: external_exports.array(external_exports.object({
28668
+ startMs: external_exports.number(),
28669
+ endMs: external_exports.number()
28670
+ })).optional()
28546
28671
  });
28547
28672
  var ClipPlaybackSchema = external_exports.object({
28548
28673
  /** HLS master URL through the hub data-plane (Range + token in path). */
@@ -29090,7 +29215,14 @@ var SearchResultSchema = external_exports.object({
29090
29215
  });
29091
29216
  var AutoUpdateSettingsSchema = external_exports.object({
29092
29217
  channel: ChannelSchema,
29093
- intervalSeconds: external_exports.number()
29218
+ intervalSeconds: external_exports.number(),
29219
+ /**
29220
+ * Cadence of the "an update exists" POLLER, in seconds. Independent of
29221
+ * `channel`: the poller runs while auto-apply is `off`, because being told
29222
+ * about a publish and installing it are different decisions. Clamped
29223
+ * server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
29224
+ */
29225
+ updateCheckIntervalSeconds: external_exports.number()
29094
29226
  });
29095
29227
  var AddonAutoUpdateSchema = ChannelWithInheritSchema;
29096
29228
  var RestartAddonResultSchema = external_exports.unknown();
@@ -29341,7 +29473,9 @@ var addonsCapability = {
29341
29473
  getAutoUpdateSettings: method(external_exports.void(), AutoUpdateSettingsSchema, { auth: "admin" }),
29342
29474
  setAutoUpdateSettings: method(external_exports.object({
29343
29475
  channel: ChannelSchema,
29344
- intervalSeconds: external_exports.number().min(300).max(86400).optional()
29476
+ intervalSeconds: external_exports.number().min(300).max(86400).optional(),
29477
+ /** Availability-poll cadence; see AutoUpdateSettingsSchema. */
29478
+ updateCheckIntervalSeconds: external_exports.number().min(900).max(604800).optional()
29345
29479
  }), external_exports.unknown(), {
29346
29480
  kind: "mutation",
29347
29481
  auth: "admin"
@@ -34623,7 +34757,9 @@ var ExportOptionsSchema = external_exports.object({
34623
34757
  includeAudio: external_exports.boolean(),
34624
34758
  maxLifeMs: external_exports.number().int().positive(),
34625
34759
  deleteAfterDownload: external_exports.boolean(),
34626
- title: external_exports.string().max(200).optional()
34760
+ title: external_exports.string().max(200).optional(),
34761
+ /** Notification-output target ids to ping when this export becomes ready. */
34762
+ notifyTargetIds: external_exports.array(external_exports.string().min(1)).max(20).optional()
34627
34763
  }).superRefine((v, ctx) => {
34628
34764
  if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
34629
34765
  code: external_exports.ZodIssueCode.custom,
@@ -34678,14 +34814,23 @@ var recordingExportCapability = {
34678
34814
  scope: "system",
34679
34815
  mode: "singleton",
34680
34816
  methods: {
34681
- /** Queue a render of `[fromMs,toMs)` for `deviceId`/`profile`. Fails fast
34682
- * when no footage covers the range. Returns the queued record. */
34817
+ /** Queue a render of `[fromMs,toMs)` for `deviceId`/`profiles` (legacy
34818
+ * singular `profile` still accepted). Fails fast when no footage covers
34819
+ * the range. One job per profile; returns the first queued record. */
34683
34820
  createExport: method(external_exports.object({
34684
34821
  deviceId: external_exports.number(),
34685
- profile: external_exports.string(),
34822
+ /** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
34823
+ profile: external_exports.string().optional(),
34824
+ profiles: external_exports.array(external_exports.string()).min(1).optional(),
34686
34825
  fromMs: external_exports.number(),
34687
34826
  toMs: external_exports.number(),
34688
34827
  options: ExportOptionsSchema
34828
+ }).superRefine((v, ctx) => {
34829
+ if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
34830
+ code: external_exports.ZodIssueCode.custom,
34831
+ message: "pass profiles[] (min 1) or legacy profile",
34832
+ path: ["profiles"]
34833
+ });
34689
34834
  }), ExportRecordSchema, {
34690
34835
  kind: "mutation",
34691
34836
  auth: "protected"
@@ -39630,6 +39775,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
39630
39775
  addonId: null,
39631
39776
  access: "create"
39632
39777
  },
39778
+ "pipelineAnalytics.reconcileFromDisk": {
39779
+ capName: "pipeline-analytics",
39780
+ capScope: "device",
39781
+ addonId: null,
39782
+ access: "create"
39783
+ },
39633
39784
  "pipelineAnalytics.refreshStorageLocationsForMigration": {
39634
39785
  capName: "pipeline-analytics",
39635
39786
  capScope: "device",
@@ -40032,6 +40183,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
40032
40183
  addonId: null,
40033
40184
  access: "view"
40034
40185
  },
40186
+ "pipelineOrchestrator.getReconcileFromDiskStatus": {
40187
+ capName: "pipeline-orchestrator",
40188
+ capScope: "system",
40189
+ addonId: null,
40190
+ access: "view"
40191
+ },
40035
40192
  "pipelineOrchestrator.listAgentSettings": {
40036
40193
  capName: "pipeline-orchestrator",
40037
40194
  capScope: "system",
@@ -40056,6 +40213,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
40056
40213
  addonId: null,
40057
40214
  access: "create"
40058
40215
  },
40216
+ "pipelineOrchestrator.reconcileFromDisk": {
40217
+ capName: "pipeline-orchestrator",
40218
+ capScope: "system",
40219
+ addonId: null,
40220
+ access: "create"
40221
+ },
40059
40222
  "pipelineOrchestrator.removeAgentSettings": {
40060
40223
  capName: "pipeline-orchestrator",
40061
40224
  capScope: "system",
@@ -43047,6 +43210,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
43047
43210
  form: "single",
43048
43211
  optional: true
43049
43212
  }],
43213
+ "pipelineAnalytics.reconcileFromDisk": [{
43214
+ name: "deviceId",
43215
+ form: "single",
43216
+ optional: false
43217
+ }],
43050
43218
  "pipelineAnalytics.restageRetrainTrack": [{
43051
43219
  name: "deviceId",
43052
43220
  form: "single",
@@ -44227,6 +44395,18 @@ var UNIT_TABLE = {
44227
44395
  offset: 0
44228
44396
  }
44229
44397
  };
44398
+ var MB = 1024 * 1024;
44399
+ var DEFAULT_POOL_MEMORY_POLICY = {
44400
+ sampleIntervalMs: 6e4,
44401
+ baselineSettleSamples: 5,
44402
+ baselineSampleCount: 3,
44403
+ restartMultiple: 4,
44404
+ floorBytes: 1024 * MB,
44405
+ ceilingBytes: 3072 * MB,
44406
+ cooldownMs: 30 * 6e4,
44407
+ maxRestartsPerWindow: 6,
44408
+ restartWindowMs: 1440 * 6e4
44409
+ };
44230
44410
 
44231
44411
  // src/commands/discover.ts
44232
44412
  var DEFAULT_MULTICAST_GROUP = "239.0.0.0";
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runDiscover
4
- } from "./chunk-2THGXGK3.js";
4
+ } from "./chunk-UCPOTU23.js";
5
5
  import "./chunk-LMMQX4CK.js";
6
6
 
7
7
  // src/cli.ts
@@ -38,7 +38,7 @@ async function runServe(args) {
38
38
  ...typeof values.data === "string" ? { data: values.data } : {}
39
39
  };
40
40
  Object.assign(process.env, buildServeEnv(opts));
41
- await import("./launcher-XHGXNFNV.js");
41
+ await import("./launcher-3IBPG6ZS.js");
42
42
  }
43
43
 
44
44
  // src/commands/agent.ts
@@ -83,7 +83,7 @@ async function runAgent(args) {
83
83
  ...typeof values.port === "string" ? { port: values.port } : {}
84
84
  };
85
85
  Object.assign(process.env, buildAgentEnv(opts));
86
- await import("./launcher-XHGXNFNV.js");
86
+ await import("./launcher-3IBPG6ZS.js");
87
87
  }
88
88
 
89
89
  // src/commands/setup.ts
@@ -1130,7 +1130,7 @@ function isUnknown(_value) {
1130
1130
  return true;
1131
1131
  }
1132
1132
  async function resolveServerInteractive(presetNamespace) {
1133
- const { discoverNodes, resolveHubFromDiscovered, filterHubNodes, DEFAULT_HUB_HTTPS_PORT } = await import("./discover-5Y5DI4XF.js");
1133
+ const { discoverNodes, resolveHubFromDiscovered, filterHubNodes, DEFAULT_HUB_HTTPS_PORT } = await import("./discover-3RXB46IL.js");
1134
1134
  if (presetNamespace) {
1135
1135
  const spinner4 = clack.spinner();
1136
1136
  spinner4.start(`Discovering hub on LAN (namespace "${presetNamespace}")`);
@@ -5,7 +5,7 @@ import {
5
5
  filterHubNodes,
6
6
  resolveHubFromDiscovered,
7
7
  runDiscover
8
- } from "./chunk-2THGXGK3.js";
8
+ } from "./chunk-UCPOTU23.js";
9
9
  import "./chunk-LMMQX4CK.js";
10
10
  export {
11
11
  DEFAULT_HUB_HTTPS_PORT,