camstack 1.2.28 → 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. */
@@ -28541,8 +28609,57 @@ 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
+ * 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()
28546
28663
  });
28547
28664
  var ClipPlaybackSchema = external_exports.object({
28548
28665
  /** HLS master URL through the hub data-plane (Range + token in path). */
@@ -29090,7 +29207,14 @@ var SearchResultSchema = external_exports.object({
29090
29207
  });
29091
29208
  var AutoUpdateSettingsSchema = external_exports.object({
29092
29209
  channel: ChannelSchema,
29093
- 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()
29094
29218
  });
29095
29219
  var AddonAutoUpdateSchema = ChannelWithInheritSchema;
29096
29220
  var RestartAddonResultSchema = external_exports.unknown();
@@ -29341,7 +29465,9 @@ var addonsCapability = {
29341
29465
  getAutoUpdateSettings: method(external_exports.void(), AutoUpdateSettingsSchema, { auth: "admin" }),
29342
29466
  setAutoUpdateSettings: method(external_exports.object({
29343
29467
  channel: ChannelSchema,
29344
- 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()
29345
29471
  }), external_exports.unknown(), {
29346
29472
  kind: "mutation",
29347
29473
  auth: "admin"
@@ -34623,7 +34749,9 @@ var ExportOptionsSchema = external_exports.object({
34623
34749
  includeAudio: external_exports.boolean(),
34624
34750
  maxLifeMs: external_exports.number().int().positive(),
34625
34751
  deleteAfterDownload: external_exports.boolean(),
34626
- 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()
34627
34755
  }).superRefine((v, ctx) => {
34628
34756
  if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
34629
34757
  code: external_exports.ZodIssueCode.custom,
@@ -34678,14 +34806,23 @@ var recordingExportCapability = {
34678
34806
  scope: "system",
34679
34807
  mode: "singleton",
34680
34808
  methods: {
34681
- /** Queue a render of `[fromMs,toMs)` for `deviceId`/`profile`. Fails fast
34682
- * 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. */
34683
34812
  createExport: method(external_exports.object({
34684
34813
  deviceId: external_exports.number(),
34685
- 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(),
34686
34817
  fromMs: external_exports.number(),
34687
34818
  toMs: external_exports.number(),
34688
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
+ });
34689
34826
  }), ExportRecordSchema, {
34690
34827
  kind: "mutation",
34691
34828
  auth: "protected"
@@ -39630,6 +39767,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
39630
39767
  addonId: null,
39631
39768
  access: "create"
39632
39769
  },
39770
+ "pipelineAnalytics.reconcileFromDisk": {
39771
+ capName: "pipeline-analytics",
39772
+ capScope: "device",
39773
+ addonId: null,
39774
+ access: "create"
39775
+ },
39633
39776
  "pipelineAnalytics.refreshStorageLocationsForMigration": {
39634
39777
  capName: "pipeline-analytics",
39635
39778
  capScope: "device",
@@ -40032,6 +40175,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
40032
40175
  addonId: null,
40033
40176
  access: "view"
40034
40177
  },
40178
+ "pipelineOrchestrator.getReconcileFromDiskStatus": {
40179
+ capName: "pipeline-orchestrator",
40180
+ capScope: "system",
40181
+ addonId: null,
40182
+ access: "view"
40183
+ },
40035
40184
  "pipelineOrchestrator.listAgentSettings": {
40036
40185
  capName: "pipeline-orchestrator",
40037
40186
  capScope: "system",
@@ -40056,6 +40205,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
40056
40205
  addonId: null,
40057
40206
  access: "create"
40058
40207
  },
40208
+ "pipelineOrchestrator.reconcileFromDisk": {
40209
+ capName: "pipeline-orchestrator",
40210
+ capScope: "system",
40211
+ addonId: null,
40212
+ access: "create"
40213
+ },
40059
40214
  "pipelineOrchestrator.removeAgentSettings": {
40060
40215
  capName: "pipeline-orchestrator",
40061
40216
  capScope: "system",
@@ -43047,6 +43202,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
43047
43202
  form: "single",
43048
43203
  optional: true
43049
43204
  }],
43205
+ "pipelineAnalytics.reconcileFromDisk": [{
43206
+ name: "deviceId",
43207
+ form: "single",
43208
+ optional: false
43209
+ }],
43050
43210
  "pipelineAnalytics.restageRetrainTrack": [{
43051
43211
  name: "deviceId",
43052
43212
  form: "single",
@@ -44227,6 +44387,18 @@ var UNIT_TABLE = {
44227
44387
  offset: 0
44228
44388
  }
44229
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
+ };
44230
44402
 
44231
44403
  // src/commands/discover.ts
44232
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-2THGXGK3.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-XHGXNFNV.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-XHGXNFNV.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-5Y5DI4XF.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-2THGXGK3.js";
8
+ } from "./chunk-LH2PIENV.js";
9
9
  import "./chunk-LMMQX4CK.js";
10
10
  export {
11
11
  DEFAULT_HUB_HTTPS_PORT,