camstack 1.2.27 → 1.2.29

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. */
@@ -27844,7 +27912,14 @@ var snapshotCapability = {
27844
27912
  "sleeping",
27845
27913
  "unreachable",
27846
27914
  "waking"
27847
- ]).nullable()
27915
+ ]).nullable(),
27916
+ /** A battery camera (whatever its current state). An AWAKE battery
27917
+ * camera is deliberately NOT recaptured on the poll cadence — every
27918
+ * capture is a camera hit that would keep it out of sleep — so its
27919
+ * cached frame legitimately ages past the currency ceiling while
27920
+ * nothing is streaming. A surface must keep painting it (a fresh
27921
+ * frame is captured at each wake), not blank to "unavailable". */
27922
+ battery: external_exports.boolean()
27848
27923
  })))
27849
27924
  },
27850
27925
  status: {
@@ -28534,8 +28609,57 @@ var ClipSchema = external_exports.object({
28534
28609
  startMs: external_exports.number(),
28535
28610
  endMs: external_exports.number()
28536
28611
  }),
28537
- /** Thumbnail URL (lazy; e.g. analytics `getEventMedia`). Never inlined. */
28538
- thumbnail: external_exports.string().optional()
28612
+ /**
28613
+ * Lazy thumbnail URL, never inlined.
28614
+ *
28615
+ * Recording-derived clips (events-mode keep-window, and the prepared
28616
+ * continuous event+fragment visit) MUST use the snapshot of the **main
28617
+ * event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
28618
+ * of the event that owns `kind` (object > motion > audio). Do not extract
28619
+ * a keyframe from the recorded segments. Other providers (onboard, HKSV)
28620
+ * mint their own stills.
28621
+ *
28622
+ * **VOUCHED, never fabricated.** A provider emits this only for an event it
28623
+ * has CONFIRMED owns at least one media row (its own, or its owning track's).
28624
+ * Absent is meaningful — "this visit has no event still" — never "we did not
28625
+ * look". Stamping it from a URL template made 35% of one camera's clips point
28626
+ * at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
28627
+ * A read that FAILS drops the claim; it never invents it.
28628
+ */
28629
+ thumbnail: external_exports.string().optional(),
28630
+ /**
28631
+ * An instant INSIDE this visit's footage, hole-safe, where a recorded still
28632
+ * can be decoded. Present whenever the visit came from recorded availability;
28633
+ * absent on a per-event padded window (there is no footage to promise).
28634
+ *
28635
+ * This is not a thumbnail and not a second byte path: it is the argument to
28636
+ * the recorder's existing still route. The surface — never the provider —
28637
+ * decides whether to use it. It is the midpoint of the visit's LONGEST
28638
+ * contiguous range, not of the visit: a visit spans its holes by
28639
+ * construction, so a naive midpoint lands in dead air.
28640
+ */
28641
+ stillAtMs: external_exports.number().optional(),
28642
+ /**
28643
+ * Analytics event ids that overlap this visit — a BOUNDED sample, newest
28644
+ * first within kind (object → motion → audio), capped at
28645
+ * {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
28646
+ *
28647
+ * Bounded because it is not a payload the surface pages through: one visit on
28648
+ * device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
28649
+ * camera-day. Read {@link eventCount} for the true total.
28650
+ */
28651
+ eventIds: external_exports.array(external_exports.string()).optional(),
28652
+ /** How many analytics events actually overlap this visit. Differs from
28653
+ * `eventIds.length` exactly when the sample was capped — so a truncated
28654
+ * list is never mistaken for a quiet visit. */
28655
+ eventCount: external_exports.number().int().nonnegative().optional(),
28656
+ /** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
28657
+ * than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
28658
+ * bar keeps showing them via `recording.getAvailability`. */
28659
+ holes: external_exports.array(external_exports.object({
28660
+ startMs: external_exports.number(),
28661
+ endMs: external_exports.number()
28662
+ })).optional()
28539
28663
  });
28540
28664
  var ClipPlaybackSchema = external_exports.object({
28541
28665
  /** HLS master URL through the hub data-plane (Range + token in path). */
@@ -29083,7 +29207,14 @@ var SearchResultSchema = external_exports.object({
29083
29207
  });
29084
29208
  var AutoUpdateSettingsSchema = external_exports.object({
29085
29209
  channel: ChannelSchema,
29086
- intervalSeconds: external_exports.number()
29210
+ intervalSeconds: external_exports.number(),
29211
+ /**
29212
+ * Cadence of the "an update exists" POLLER, in seconds. Independent of
29213
+ * `channel`: the poller runs while auto-apply is `off`, because being told
29214
+ * about a publish and installing it are different decisions. Clamped
29215
+ * server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
29216
+ */
29217
+ updateCheckIntervalSeconds: external_exports.number()
29087
29218
  });
29088
29219
  var AddonAutoUpdateSchema = ChannelWithInheritSchema;
29089
29220
  var RestartAddonResultSchema = external_exports.unknown();
@@ -29334,7 +29465,9 @@ var addonsCapability = {
29334
29465
  getAutoUpdateSettings: method(external_exports.void(), AutoUpdateSettingsSchema, { auth: "admin" }),
29335
29466
  setAutoUpdateSettings: method(external_exports.object({
29336
29467
  channel: ChannelSchema,
29337
- intervalSeconds: external_exports.number().min(300).max(86400).optional()
29468
+ intervalSeconds: external_exports.number().min(300).max(86400).optional(),
29469
+ /** Availability-poll cadence; see AutoUpdateSettingsSchema. */
29470
+ updateCheckIntervalSeconds: external_exports.number().min(900).max(604800).optional()
29338
29471
  }), external_exports.unknown(), {
29339
29472
  kind: "mutation",
29340
29473
  auth: "admin"
@@ -34616,7 +34749,9 @@ var ExportOptionsSchema = external_exports.object({
34616
34749
  includeAudio: external_exports.boolean(),
34617
34750
  maxLifeMs: external_exports.number().int().positive(),
34618
34751
  deleteAfterDownload: external_exports.boolean(),
34619
- title: external_exports.string().max(200).optional()
34752
+ title: external_exports.string().max(200).optional(),
34753
+ /** Notification-output target ids to ping when this export becomes ready. */
34754
+ notifyTargetIds: external_exports.array(external_exports.string().min(1)).max(20).optional()
34620
34755
  }).superRefine((v, ctx) => {
34621
34756
  if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
34622
34757
  code: external_exports.ZodIssueCode.custom,
@@ -34671,14 +34806,23 @@ var recordingExportCapability = {
34671
34806
  scope: "system",
34672
34807
  mode: "singleton",
34673
34808
  methods: {
34674
- /** Queue a render of `[fromMs,toMs)` for `deviceId`/`profile`. Fails fast
34675
- * when no footage covers the range. Returns the queued record. */
34809
+ /** Queue a render of `[fromMs,toMs)` for `deviceId`/`profiles` (legacy
34810
+ * singular `profile` still accepted). Fails fast when no footage covers
34811
+ * the range. One job per profile; returns the first queued record. */
34676
34812
  createExport: method(external_exports.object({
34677
34813
  deviceId: external_exports.number(),
34678
- profile: external_exports.string(),
34814
+ /** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
34815
+ profile: external_exports.string().optional(),
34816
+ profiles: external_exports.array(external_exports.string()).min(1).optional(),
34679
34817
  fromMs: external_exports.number(),
34680
34818
  toMs: external_exports.number(),
34681
34819
  options: ExportOptionsSchema
34820
+ }).superRefine((v, ctx) => {
34821
+ if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
34822
+ code: external_exports.ZodIssueCode.custom,
34823
+ message: "pass profiles[] (min 1) or legacy profile",
34824
+ path: ["profiles"]
34825
+ });
34682
34826
  }), ExportRecordSchema, {
34683
34827
  kind: "mutation",
34684
34828
  auth: "protected"
@@ -39623,6 +39767,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
39623
39767
  addonId: null,
39624
39768
  access: "create"
39625
39769
  },
39770
+ "pipelineAnalytics.reconcileFromDisk": {
39771
+ capName: "pipeline-analytics",
39772
+ capScope: "device",
39773
+ addonId: null,
39774
+ access: "create"
39775
+ },
39626
39776
  "pipelineAnalytics.refreshStorageLocationsForMigration": {
39627
39777
  capName: "pipeline-analytics",
39628
39778
  capScope: "device",
@@ -40025,6 +40175,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
40025
40175
  addonId: null,
40026
40176
  access: "view"
40027
40177
  },
40178
+ "pipelineOrchestrator.getReconcileFromDiskStatus": {
40179
+ capName: "pipeline-orchestrator",
40180
+ capScope: "system",
40181
+ addonId: null,
40182
+ access: "view"
40183
+ },
40028
40184
  "pipelineOrchestrator.listAgentSettings": {
40029
40185
  capName: "pipeline-orchestrator",
40030
40186
  capScope: "system",
@@ -40049,6 +40205,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
40049
40205
  addonId: null,
40050
40206
  access: "create"
40051
40207
  },
40208
+ "pipelineOrchestrator.reconcileFromDisk": {
40209
+ capName: "pipeline-orchestrator",
40210
+ capScope: "system",
40211
+ addonId: null,
40212
+ access: "create"
40213
+ },
40052
40214
  "pipelineOrchestrator.removeAgentSettings": {
40053
40215
  capName: "pipeline-orchestrator",
40054
40216
  capScope: "system",
@@ -43040,6 +43202,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
43040
43202
  form: "single",
43041
43203
  optional: true
43042
43204
  }],
43205
+ "pipelineAnalytics.reconcileFromDisk": [{
43206
+ name: "deviceId",
43207
+ form: "single",
43208
+ optional: false
43209
+ }],
43043
43210
  "pipelineAnalytics.restageRetrainTrack": [{
43044
43211
  name: "deviceId",
43045
43212
  form: "single",
@@ -44220,6 +44387,18 @@ var UNIT_TABLE = {
44220
44387
  offset: 0
44221
44388
  }
44222
44389
  };
44390
+ var MB = 1024 * 1024;
44391
+ var DEFAULT_POOL_MEMORY_POLICY = {
44392
+ sampleIntervalMs: 6e4,
44393
+ baselineSettleSamples: 5,
44394
+ baselineSampleCount: 3,
44395
+ restartMultiple: 4,
44396
+ floorBytes: 1024 * MB,
44397
+ ceilingBytes: 3072 * MB,
44398
+ cooldownMs: 30 * 6e4,
44399
+ maxRestartsPerWindow: 6,
44400
+ restartWindowMs: 1440 * 6e4
44401
+ };
44223
44402
 
44224
44403
  // src/commands/discover.ts
44225
44404
  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-NQDB3IYC.js";
4
+ } from "./chunk-LH2PIENV.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-35MVUU7V.js");
41
+ await import("./launcher-MEE5XESN.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-35MVUU7V.js");
86
+ await import("./launcher-MEE5XESN.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-P6EGD2V7.js");
1133
+ const { discoverNodes, resolveHubFromDiscovered, filterHubNodes, DEFAULT_HUB_HTTPS_PORT } = await import("./discover-LPBV7MTE.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-NQDB3IYC.js";
8
+ } from "./chunk-LH2PIENV.js";
9
9
  import "./chunk-LMMQX4CK.js";
10
10
  export {
11
11
  DEFAULT_HUB_HTTPS_PORT,