@camstack/types 1.2.63 → 1.2.64
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/capabilities/index.d.ts +3 -3
- package/dist/capabilities/notification-rules.cap.d.ts +791 -164
- package/dist/capabilities/osd-manager.cap.d.ts +108 -12
- package/dist/capabilities/pipeline-analytics.cap.d.ts +1 -0
- package/dist/capabilities/recording.cap.d.ts +189 -0
- package/dist/capabilities/storage-migration.cap.d.ts +1 -0
- package/dist/generated/addon-api.d.ts +35 -0
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/system-proxy.d.ts +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +639 -14
- package/dist/index.mjs +619 -15
- package/dist/interfaces/addon.d.ts +32 -0
- package/dist/interfaces/event-bus.d.ts +29 -3
- package/dist/interfaces/relocate.d.ts +15 -0
- package/dist/interfaces/stream-broker.d.ts +14 -0
- package/dist/notification/timelapse-rule.d.ts +83 -0
- package/package.json +1 -1
|
@@ -1141,6 +1141,27 @@ export interface AddonExecution {
|
|
|
1141
1141
|
* former hard-coded addon-name list in `process-service.ts`.
|
|
1142
1142
|
*/
|
|
1143
1143
|
readonly heapProfile?: AddonHeapProfile;
|
|
1144
|
+
/**
|
|
1145
|
+
* `@camstack/system` builtin ONLY: run this builtin in its OWN forked runner
|
|
1146
|
+
* instead of in-process on the hub root.
|
|
1147
|
+
*
|
|
1148
|
+
* The hub drops `@camstack/system` builtins from the runner plan because the
|
|
1149
|
+
* infrastructure ones (storage, settings, logging, device-manager) must be
|
|
1150
|
+
* resident in the process every runner depends on. That is a reason to keep
|
|
1151
|
+
* THOSE in-process — not a property of the package. A builtin that owns
|
|
1152
|
+
* capture, transcode and byte caches (`snapshot`) charges all of it to the
|
|
1153
|
+
* event loop that also answers the tRPC API.
|
|
1154
|
+
*
|
|
1155
|
+
* Declaring `isolate: true` opts a single builtin out of that blanket rule:
|
|
1156
|
+
* it is planned, spawned, restarted and route-mounted exactly like any other
|
|
1157
|
+
* forked addon, and `resolveRunnerId` still owns its runner id (D2 — one
|
|
1158
|
+
* addon, one process). Nothing else changes: the manifest entry keeps its
|
|
1159
|
+
* `entry` path inside the `@camstack/system` closure, and the runner loads it
|
|
1160
|
+
* from there.
|
|
1161
|
+
*
|
|
1162
|
+
* Ignored for non-system packages — they already fork.
|
|
1163
|
+
*/
|
|
1164
|
+
readonly isolate?: boolean;
|
|
1144
1165
|
}
|
|
1145
1166
|
export type AddonHeapProfile = 'light' | 'heavy';
|
|
1146
1167
|
export declare const DEFAULT_ADDON_PLACEMENT: AddonPlacement;
|
|
@@ -1184,6 +1205,17 @@ export declare function resolveAddonGroup(decl: Pick<AddonDeclaration, 'executio
|
|
|
1184
1205
|
export declare function resolveRunnerId(decl: Pick<AddonDeclaration, 'execution'>, addonId: string): string;
|
|
1185
1206
|
/** Convenience accessor — the resolved placement (defaults to `hub-only`). */
|
|
1186
1207
|
export declare function resolveAddonPlacement(decl: Pick<AddonDeclaration, 'execution'>): AddonPlacement;
|
|
1208
|
+
/**
|
|
1209
|
+
* True when a `@camstack/system` builtin opted out of the in-process rule and
|
|
1210
|
+
* must be planned as a forked runner (`execution.isolate`).
|
|
1211
|
+
*
|
|
1212
|
+
* ONE predicate, because the hub asks this question in three places that must
|
|
1213
|
+
* agree: the runner plan (`buildAddonGroupPlan`), the "does this boot
|
|
1214
|
+
* in-process" filter, and `isForkedAddonEntry` (which decides route mounts,
|
|
1215
|
+
* data-plane mounts, restart and uninstall). They diverged once before for
|
|
1216
|
+
* `auth-oidc` and the addon's routes were mounted against an async UDS proxy.
|
|
1217
|
+
*/
|
|
1218
|
+
export declare function isIsolatedBuiltin(decl: Pick<AddonDeclaration, 'execution'>): boolean;
|
|
1187
1219
|
/**
|
|
1188
1220
|
* One entry of a `RunnerPlan` — an addon assigned to a runner, paired
|
|
1189
1221
|
* with the on-disk directory the runner subprocess loads it from.
|
|
@@ -526,6 +526,29 @@ export interface TopologyNode {
|
|
|
526
526
|
readonly version: string;
|
|
527
527
|
} | null;
|
|
528
528
|
}
|
|
529
|
+
/**
|
|
530
|
+
* Why a broker's stream health flipped — the discriminator that separates an
|
|
531
|
+
* OUTAGE from an intentional state change.
|
|
532
|
+
*
|
|
533
|
+
* A `stream.offline` on its own says nothing about whether anything is wrong:
|
|
534
|
+
* the same event is fired when a camera stops sending video AND when the last
|
|
535
|
+
* consumer left and the broker suspended its dial on purpose. A consumer that
|
|
536
|
+
* reads only the category cannot tell them apart, and the Notification Center
|
|
537
|
+
* shipped exactly that bug — an operator notified that a stream went down
|
|
538
|
+
* every time nobody was watching it.
|
|
539
|
+
*
|
|
540
|
+
* - `stale-timeout` — no video packet for the broker's stale window
|
|
541
|
+
* (120 s). The ONLY outage. This is what a notification may act on.
|
|
542
|
+
* - `recovered` — packets resumed after a `stale-timeout`. The
|
|
543
|
+
* matching end of an outage.
|
|
544
|
+
* - `first-packet` — a broker's first ever packet. A stream coming up, not
|
|
545
|
+
* a recovery: there was no outage before it.
|
|
546
|
+
* - `suspended` — the lazy-dial idle suspend (no demand). Deliberate.
|
|
547
|
+
* - `resumed` — demand returned and the dial came back. The matching
|
|
548
|
+
* end of a `suspended`, and equally deliberate.
|
|
549
|
+
* - `broker-destroyed` — the stream was unpublished/released. Deliberate.
|
|
550
|
+
*/
|
|
551
|
+
export type StreamHealthReason = 'stale-timeout' | 'recovered' | 'first-packet' | 'suspended' | 'resumed' | 'broker-destroyed';
|
|
529
552
|
/** All known event categories with their typed payloads */
|
|
530
553
|
export interface EventCatalog {
|
|
531
554
|
'system.boot': {
|
|
@@ -1011,7 +1034,7 @@ export interface EventCatalog {
|
|
|
1011
1034
|
readonly engine: PipelineEngineChoice;
|
|
1012
1035
|
readonly modelsLoaded: readonly string[];
|
|
1013
1036
|
readonly inUseByCameras: readonly number[];
|
|
1014
|
-
readonly kind: 'runtime' | 'warm-override';
|
|
1037
|
+
readonly kind: 'runtime' | 'warm-override' | 'device-pool';
|
|
1015
1038
|
readonly poolPid: number | null;
|
|
1016
1039
|
readonly idleMs: number | null;
|
|
1017
1040
|
readonly idleTtlMs: number | null;
|
|
@@ -1159,6 +1182,9 @@ export interface EventCatalog {
|
|
|
1159
1182
|
* (every broker is keyed by its cam stream now); `profile` is the
|
|
1160
1183
|
* profile slot currently bound to it (or `null` if the broker is
|
|
1161
1184
|
* only kept alive by a manual activation).
|
|
1185
|
+
*
|
|
1186
|
+
* `reason` is the discriminator a consumer MUST read before treating a flip
|
|
1187
|
+
* as an outage — see {@link StreamHealthReason}.
|
|
1162
1188
|
*/
|
|
1163
1189
|
'stream.online': {
|
|
1164
1190
|
readonly deviceId: number;
|
|
@@ -1167,7 +1193,7 @@ export interface EventCatalog {
|
|
|
1167
1193
|
readonly brokerId: string;
|
|
1168
1194
|
readonly sourceType: string;
|
|
1169
1195
|
readonly lastPacketAt: number;
|
|
1170
|
-
readonly reason?:
|
|
1196
|
+
readonly reason?: StreamHealthReason;
|
|
1171
1197
|
};
|
|
1172
1198
|
'stream.offline': {
|
|
1173
1199
|
readonly deviceId: number;
|
|
@@ -1176,7 +1202,7 @@ export interface EventCatalog {
|
|
|
1176
1202
|
readonly brokerId: string;
|
|
1177
1203
|
readonly sourceType: string;
|
|
1178
1204
|
readonly lastPacketAt: number;
|
|
1179
|
-
readonly reason?:
|
|
1205
|
+
readonly reason?: StreamHealthReason;
|
|
1180
1206
|
};
|
|
1181
1207
|
'retention.cleanup': {
|
|
1182
1208
|
readonly deletedEvents?: number;
|
|
@@ -8,7 +8,15 @@ import { z } from 'zod';
|
|
|
8
8
|
* Each completed/failed run also lands one durable ops-log row on its owning
|
|
9
9
|
* addon surface.
|
|
10
10
|
*/
|
|
11
|
+
/**
|
|
12
|
+
* `queued` exists because the recorder mover is SINGLE-FLIGHT and an operator
|
|
13
|
+
* rebalance enqueues one job per (camera, profile). Refusing the second job —
|
|
14
|
+
* what the engine did before — turned a fifteen-camera rebalance into fifteen
|
|
15
|
+
* manual retries. Queued jobs run FIFO; a queued job that is cancelled never
|
|
16
|
+
* runs at all.
|
|
17
|
+
*/
|
|
11
18
|
export declare const RelocateJobStateSchema: z.ZodEnum<{
|
|
19
|
+
queued: "queued";
|
|
12
20
|
done: "done";
|
|
13
21
|
failed: "failed";
|
|
14
22
|
running: "running";
|
|
@@ -18,6 +26,7 @@ export type RelocateJobState = z.infer<typeof RelocateJobStateSchema>;
|
|
|
18
26
|
export declare const RelocateJobSchema: z.ZodObject<{
|
|
19
27
|
jobId: z.ZodString;
|
|
20
28
|
state: z.ZodEnum<{
|
|
29
|
+
queued: "queued";
|
|
21
30
|
done: "done";
|
|
22
31
|
failed: "failed";
|
|
23
32
|
running: "running";
|
|
@@ -52,6 +61,8 @@ export declare const RelocateFootageInputSchema: z.ZodObject<{
|
|
|
52
61
|
recordings: "recordings";
|
|
53
62
|
recordingsLow: "recordingsLow";
|
|
54
63
|
}>>;
|
|
64
|
+
deviceId: z.ZodOptional<z.ZodNumber>;
|
|
65
|
+
profiles: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
55
66
|
throttleMbps: z.ZodOptional<z.ZodNumber>;
|
|
56
67
|
}, z.core.$strip>;
|
|
57
68
|
export type RelocateFootageInput = z.infer<typeof RelocateFootageInputSchema>;
|
|
@@ -72,6 +83,8 @@ export declare const StorageMigrationFootageMoveInputSchema: z.ZodObject<{
|
|
|
72
83
|
recordings: "recordings";
|
|
73
84
|
recordingsLow: "recordingsLow";
|
|
74
85
|
}>>;
|
|
86
|
+
deviceId: z.ZodOptional<z.ZodNumber>;
|
|
87
|
+
profiles: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
75
88
|
throttleMbps: z.ZodOptional<z.ZodNumber>;
|
|
76
89
|
leaseId: z.ZodString;
|
|
77
90
|
}, z.core.$strip>;
|
|
@@ -147,6 +160,7 @@ export declare const StorageMigrationMoveSchema: z.ZodObject<{
|
|
|
147
160
|
toLocationId: z.ZodString;
|
|
148
161
|
moverJobId: z.ZodNullable<z.ZodString>;
|
|
149
162
|
state: z.ZodNullable<z.ZodEnum<{
|
|
163
|
+
queued: "queued";
|
|
150
164
|
done: "done";
|
|
151
165
|
failed: "failed";
|
|
152
166
|
running: "running";
|
|
@@ -185,6 +199,7 @@ export declare const StorageMigrationJobSchema: z.ZodObject<{
|
|
|
185
199
|
toLocationId: z.ZodString;
|
|
186
200
|
moverJobId: z.ZodNullable<z.ZodString>;
|
|
187
201
|
state: z.ZodNullable<z.ZodEnum<{
|
|
202
|
+
queued: "queued";
|
|
188
203
|
done: "done";
|
|
189
204
|
failed: "failed";
|
|
190
205
|
running: "running";
|
|
@@ -265,6 +265,20 @@ export interface IStreamBroker {
|
|
|
265
265
|
* `H265Repacketizer` once the camera's first RTP+SDP arrive.
|
|
266
266
|
*/
|
|
267
267
|
onSdpParameterSets(callback: (ps: ReadonlyArray<Buffer>) => void): Unsubscribe;
|
|
268
|
+
/**
|
|
269
|
+
* Subscribe to SOURCE RESTARTS — every dial after the first, carrying a
|
|
270
|
+
* short reason (`dial#2`, …). The WebRTC server subscribes for EVERY
|
|
271
|
+
* session shape (RTP passthrough, AnnexB, transcode): each holds parameter
|
|
272
|
+
* sets and a bootstrap latch belonging to the connection that ended, so a
|
|
273
|
+
* re-dial must re-sync them rather than leave the session decoding against
|
|
274
|
+
* a dead mapping.
|
|
275
|
+
*
|
|
276
|
+
* Fires on the DIAL, not on the first packet: a restart that never delivers
|
|
277
|
+
* must still be observable rather than silently indistinguishable from a
|
|
278
|
+
* healthy stream. The returned function detaches ONE subscriber (a broker
|
|
279
|
+
* fans out to many live sessions).
|
|
280
|
+
*/
|
|
281
|
+
onSourceRestart(observer: (reason: string) => void): Unsubscribe;
|
|
268
282
|
/**
|
|
269
283
|
* Source type as configured at `start()` — see `StreamSourceType`
|
|
270
284
|
* for the supported values. `null` before `start()` resolves.
|
|
@@ -28,6 +28,35 @@ export declare const TimelapseTemplateSchema: z.ZodObject<{
|
|
|
28
28
|
body: z.ZodOptional<z.ZodString>;
|
|
29
29
|
}, z.core.$strip>;
|
|
30
30
|
export type TimelapseTemplate = z.infer<typeof TimelapseTemplateSchema>;
|
|
31
|
+
/**
|
|
32
|
+
* Floor on any dense cadence, seconds — the recording's own frame interval.
|
|
33
|
+
*
|
|
34
|
+
* Declared here because it bounds BOTH the rule field and the renderer's
|
|
35
|
+
* derivation, and two copies of a floor are two floors that can drift.
|
|
36
|
+
*/
|
|
37
|
+
export declare const TIMELAPSE_DENSE_FLOOR_SEC = 0.1;
|
|
38
|
+
/**
|
|
39
|
+
* Whether the notification's preview is a STILL or a short animation.
|
|
40
|
+
*
|
|
41
|
+
* The operator's ask, verbatim: *"inviato come gif o video (come per le altre
|
|
42
|
+
* rule)"* — his Scrypted advanced-notifier has a `gifRule`, and a ten-hour
|
|
43
|
+
* night reads better as three seconds of motion than as one frame of it. Both
|
|
44
|
+
* modes get the SAME treatment (blurred frame, large centred title); `'gif'`
|
|
45
|
+
* simply applies it to a dozen frames sampled across the render and assembles
|
|
46
|
+
* them.
|
|
47
|
+
*
|
|
48
|
+
* `'image'` is the default and stays the default: a GIF costs a dozen ffmpeg
|
|
49
|
+
* seeks and a palette pass, and no rule that never asked for one should start
|
|
50
|
+
* paying that on the deploy that shipped it.
|
|
51
|
+
*
|
|
52
|
+
* A GIF that cannot be assembled DEGRADES to the still — never to nothing.
|
|
53
|
+
*/
|
|
54
|
+
declare const PreviewModeField: z.ZodEnum<{
|
|
55
|
+
image: "image";
|
|
56
|
+
gif: "gif";
|
|
57
|
+
}>;
|
|
58
|
+
/** Still or animation — the rule field, as a type the renderer can take. */
|
|
59
|
+
export type TimelapsePreviewMode = z.infer<typeof PreviewModeField>;
|
|
31
60
|
/**
|
|
32
61
|
* Client-supplied timelapse-rule fields. The server stamps id / createdBy /
|
|
33
62
|
* createdAt / updatedAt / ownerUserId / lastGeneratedAt — none of them appear
|
|
@@ -48,6 +77,8 @@ export declare const TimelapseRuleInputSchema: z.ZodObject<{
|
|
|
48
77
|
}, z.core.$strip>;
|
|
49
78
|
cadenceSec: z.ZodDefault<z.ZodNumber>;
|
|
50
79
|
framerate: z.ZodDefault<z.ZodNumber>;
|
|
80
|
+
denseCadenceSec: z.ZodOptional<z.ZodNumber>;
|
|
81
|
+
minDwellSec: z.ZodOptional<z.ZodNumber>;
|
|
51
82
|
targets: z.ZodArray<z.ZodObject<{
|
|
52
83
|
targetId: z.ZodString;
|
|
53
84
|
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -56,9 +87,23 @@ export declare const TimelapseRuleInputSchema: z.ZodObject<{
|
|
|
56
87
|
title: z.ZodOptional<z.ZodString>;
|
|
57
88
|
body: z.ZodOptional<z.ZodString>;
|
|
58
89
|
}, z.core.$strip>>;
|
|
90
|
+
previewText: z.ZodOptional<z.ZodString>;
|
|
91
|
+
previewMode: z.ZodDefault<z.ZodEnum<{
|
|
92
|
+
image: "image";
|
|
93
|
+
gif: "gif";
|
|
94
|
+
}>>;
|
|
95
|
+
reportClasses: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
59
96
|
priority: z.ZodDefault<z.ZodNumber>;
|
|
60
97
|
}, z.core.$strip>;
|
|
61
98
|
export type TimelapseRuleInput = z.infer<typeof TimelapseRuleInputSchema>;
|
|
99
|
+
/**
|
|
100
|
+
* The caption a rule that never set one gets.
|
|
101
|
+
*
|
|
102
|
+
* Rendered through the ordinary `{{var}}` pass at delivery, so a rule created
|
|
103
|
+
* before this field existed still reads "Timelapse Videocamera ingresso" and
|
|
104
|
+
* not the literal braces.
|
|
105
|
+
*/
|
|
106
|
+
export declare const DEFAULT_TIMELAPSE_PREVIEW_TEXT = "Timelapse {{camera}}";
|
|
62
107
|
/**
|
|
63
108
|
* Partial patch for an update — any subset of the INPUT fields, with NO
|
|
64
109
|
* defaults (an absent key means "leave unchanged", never "reset to default").
|
|
@@ -90,6 +135,8 @@ export declare const TimelapseRulePatchSchema: z.ZodObject<{
|
|
|
90
135
|
}, z.core.$strip>>;
|
|
91
136
|
cadenceSec: z.ZodOptional<z.ZodNumber>;
|
|
92
137
|
framerate: z.ZodOptional<z.ZodNumber>;
|
|
138
|
+
denseCadenceSec: z.ZodOptional<z.ZodNumber>;
|
|
139
|
+
minDwellSec: z.ZodOptional<z.ZodNumber>;
|
|
93
140
|
targets: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
94
141
|
targetId: z.ZodString;
|
|
95
142
|
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -98,6 +145,12 @@ export declare const TimelapseRulePatchSchema: z.ZodObject<{
|
|
|
98
145
|
title: z.ZodOptional<z.ZodString>;
|
|
99
146
|
body: z.ZodOptional<z.ZodString>;
|
|
100
147
|
}, z.core.$strip>>>;
|
|
148
|
+
previewText: z.ZodOptional<z.ZodString>;
|
|
149
|
+
previewMode: z.ZodOptional<z.ZodEnum<{
|
|
150
|
+
image: "image";
|
|
151
|
+
gif: "gif";
|
|
152
|
+
}>>;
|
|
153
|
+
reportClasses: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
101
154
|
priority: z.ZodOptional<z.ZodNumber>;
|
|
102
155
|
}, z.core.$strip>;
|
|
103
156
|
export type TimelapseRulePatch = z.infer<typeof TimelapseRulePatchSchema>;
|
|
@@ -117,6 +170,8 @@ export declare const TimelapseRuleSchema: z.ZodObject<{
|
|
|
117
170
|
}, z.core.$strip>;
|
|
118
171
|
cadenceSec: z.ZodDefault<z.ZodNumber>;
|
|
119
172
|
framerate: z.ZodDefault<z.ZodNumber>;
|
|
173
|
+
denseCadenceSec: z.ZodOptional<z.ZodNumber>;
|
|
174
|
+
minDwellSec: z.ZodOptional<z.ZodNumber>;
|
|
120
175
|
targets: z.ZodArray<z.ZodObject<{
|
|
121
176
|
targetId: z.ZodString;
|
|
122
177
|
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -125,6 +180,12 @@ export declare const TimelapseRuleSchema: z.ZodObject<{
|
|
|
125
180
|
title: z.ZodOptional<z.ZodString>;
|
|
126
181
|
body: z.ZodOptional<z.ZodString>;
|
|
127
182
|
}, z.core.$strip>>;
|
|
183
|
+
previewText: z.ZodOptional<z.ZodString>;
|
|
184
|
+
previewMode: z.ZodDefault<z.ZodEnum<{
|
|
185
|
+
image: "image";
|
|
186
|
+
gif: "gif";
|
|
187
|
+
}>>;
|
|
188
|
+
reportClasses: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
128
189
|
priority: z.ZodDefault<z.ZodNumber>;
|
|
129
190
|
id: z.ZodString;
|
|
130
191
|
ownerUserId: z.ZodOptional<z.ZodString>;
|
|
@@ -135,6 +196,27 @@ export declare const TimelapseRuleSchema: z.ZodObject<{
|
|
|
135
196
|
updatedAt: z.ZodNumber;
|
|
136
197
|
}, z.core.$strip>;
|
|
137
198
|
export type TimelapseRule = z.infer<typeof TimelapseRuleSchema>;
|
|
199
|
+
/** The cadence pair a rule must satisfy — the guard's whole input. */
|
|
200
|
+
export interface TimelapseCadencePair {
|
|
201
|
+
readonly cadenceSec: number;
|
|
202
|
+
readonly denseCadenceSec?: number;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Refuse a dense cadence that is not denser than the base.
|
|
206
|
+
*
|
|
207
|
+
* Called at UPSERT, on the MERGED rule — a patch that lowers `cadenceSec`
|
|
208
|
+
* alone can invalidate a `denseCadenceSec` set months earlier, so checking the
|
|
209
|
+
* patch in isolation would let the bad pair through.
|
|
210
|
+
*
|
|
211
|
+
* Refusing here and not at render time is the whole point: `ExportTimelapseSchema`
|
|
212
|
+
* also rejects the pair, but it does so when the window has already closed and
|
|
213
|
+
* the footage is being cut — the operator learns at 06:05 that last night was
|
|
214
|
+
* never going to render, and a closed window does not come back. This turns
|
|
215
|
+
* that into a failed edit he can see and correct.
|
|
216
|
+
*
|
|
217
|
+
* @throws Error naming both numbers, so the message is actionable in a toast.
|
|
218
|
+
*/
|
|
219
|
+
export declare function assertTimelapseCadences(pair: TimelapseCadencePair): void;
|
|
138
220
|
/**
|
|
139
221
|
* The last successful generation for ONE camera of a rule, epoch-ms.
|
|
140
222
|
*
|
|
@@ -148,3 +230,4 @@ export type TimelapseRule = z.infer<typeof TimelapseRuleSchema>;
|
|
|
148
230
|
* entire rule set once.
|
|
149
231
|
*/
|
|
150
232
|
export declare function readTimelapseGeneratedAt(rule: Pick<TimelapseRule, 'lastGeneratedAt' | 'generatedByDevice'>, deviceId: number): number;
|
|
233
|
+
export {};
|