camstack 1.2.20 → 1.2.22
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-
|
|
14525
|
+
// ../types/dist/sleep-BxjBLp3L.mjs
|
|
14526
14526
|
var WELL_KNOWN_TABS = [
|
|
14527
14527
|
{
|
|
14528
14528
|
id: "overview",
|
|
@@ -15626,6 +15626,7 @@ var RecordingConfigSchema = external_exports.object({
|
|
|
15626
15626
|
scrubThumbnails: ScrubThumbnailPresetSchema.optional()
|
|
15627
15627
|
}).strict();
|
|
15628
15628
|
var RelocateJobStateSchema = external_exports.enum([
|
|
15629
|
+
"queued",
|
|
15629
15630
|
"running",
|
|
15630
15631
|
"done",
|
|
15631
15632
|
"failed",
|
|
@@ -15650,20 +15651,96 @@ var RelocateJobSchema = external_exports.object({
|
|
|
15650
15651
|
finishedAt: external_exports.number().nullable(),
|
|
15651
15652
|
error: external_exports.string().nullable()
|
|
15652
15653
|
});
|
|
15654
|
+
var RelocateFootageClassSchema = external_exports.enum(["recordings", "recordingsLow"]);
|
|
15653
15655
|
var RelocateFootageInputSchema = external_exports.object({
|
|
15654
|
-
deviceId: external_exports.number().optional(),
|
|
15655
15656
|
fromLocationId: external_exports.string(),
|
|
15656
15657
|
toLocationId: external_exports.string(),
|
|
15657
15658
|
entities: external_exports.array(external_exports.enum(["segments"])).optional(),
|
|
15659
|
+
/** Limits relocation to the logical profile class. Omit only for the
|
|
15660
|
+
* pre-orchestration compatibility path. */
|
|
15661
|
+
footageClass: RelocateFootageClassSchema.optional(),
|
|
15662
|
+
/** Scope the move to ONE camera. Absent = every camera on the source, which
|
|
15663
|
+
* is what a whole-disk drain means. The rebalance path always sets it: its
|
|
15664
|
+
* unit is a (camera, profile) pile, not a disk. */
|
|
15665
|
+
deviceId: external_exports.number().int().optional(),
|
|
15666
|
+
/** Scope the move to specific segment profiles (`high` / `mid` / `low`).
|
|
15667
|
+
* Finer than `footageClass`, which cannot separate high from mid — and the
|
|
15668
|
+
* placement plan assigns those two independently, so a rebalance that could
|
|
15669
|
+
* only say "recordings" would move footage the plan never asked to move. */
|
|
15670
|
+
profiles: external_exports.array(external_exports.string()).optional(),
|
|
15658
15671
|
/** Copy throttle in MB/s (default 40) — the drain is a background chore,
|
|
15659
15672
|
* never allowed to starve live writers. */
|
|
15660
15673
|
throttleMbps: external_exports.number().min(1).max(1e3).optional()
|
|
15661
15674
|
});
|
|
15675
|
+
var StorageMigrationLeaseInputSchema = external_exports.object({ leaseId: external_exports.string().min(1) });
|
|
15676
|
+
var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: external_exports.string().min(1) });
|
|
15662
15677
|
var RelocateMediaInputSchema = external_exports.object({
|
|
15663
|
-
deviceId: external_exports.number().optional(),
|
|
15664
15678
|
toLocationId: external_exports.string(),
|
|
15665
15679
|
throttleMbps: external_exports.number().min(1).max(1e3).optional()
|
|
15666
15680
|
});
|
|
15681
|
+
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: external_exports.string().min(1) });
|
|
15682
|
+
var StorageMigrationClassSchema = external_exports.enum([
|
|
15683
|
+
"recordings",
|
|
15684
|
+
"recordingsLow",
|
|
15685
|
+
"eventMedia"
|
|
15686
|
+
]);
|
|
15687
|
+
var StorageMigrationDestinationsSchema = external_exports.object({
|
|
15688
|
+
recordings: external_exports.string().min(1).optional(),
|
|
15689
|
+
recordingsLow: external_exports.string().min(1).optional(),
|
|
15690
|
+
eventMedia: external_exports.string().min(1).optional()
|
|
15691
|
+
}).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
|
|
15692
|
+
var StorageMigrationInputSchema = external_exports.object({
|
|
15693
|
+
destinations: StorageMigrationDestinationsSchema,
|
|
15694
|
+
throttleMbps: external_exports.number().min(1).max(1e3).optional()
|
|
15695
|
+
});
|
|
15696
|
+
var StorageMigrationPhaseSchema = external_exports.enum([
|
|
15697
|
+
"planning",
|
|
15698
|
+
"pausing",
|
|
15699
|
+
"moving",
|
|
15700
|
+
"verifying",
|
|
15701
|
+
"repointing",
|
|
15702
|
+
"refreshing",
|
|
15703
|
+
"resuming",
|
|
15704
|
+
"done",
|
|
15705
|
+
"failed",
|
|
15706
|
+
"cancelled"
|
|
15707
|
+
]);
|
|
15708
|
+
var StorageMigrationParticipantSchema = external_exports.enum([
|
|
15709
|
+
"pipeline",
|
|
15710
|
+
"recorder",
|
|
15711
|
+
"analytics"
|
|
15712
|
+
]);
|
|
15713
|
+
var StorageMigrationMoveSchema = external_exports.object({
|
|
15714
|
+
storageClass: StorageMigrationClassSchema,
|
|
15715
|
+
fromLocationId: external_exports.string(),
|
|
15716
|
+
toLocationId: external_exports.string(),
|
|
15717
|
+
moverJobId: external_exports.string().nullable(),
|
|
15718
|
+
state: RelocateJobStateSchema.nullable(),
|
|
15719
|
+
error: external_exports.string().nullable()
|
|
15720
|
+
});
|
|
15721
|
+
var StorageMigrationJobSchema = external_exports.object({
|
|
15722
|
+
jobId: external_exports.string(),
|
|
15723
|
+
phase: StorageMigrationPhaseSchema,
|
|
15724
|
+
destinations: StorageMigrationDestinationsSchema,
|
|
15725
|
+
throttleMbps: external_exports.number(),
|
|
15726
|
+
moves: external_exports.array(StorageMigrationMoveSchema),
|
|
15727
|
+
pauseLeaseId: external_exports.string().nullable(),
|
|
15728
|
+
pausedParticipants: external_exports.array(StorageMigrationParticipantSchema),
|
|
15729
|
+
repointed: external_exports.boolean(),
|
|
15730
|
+
cancelRequested: external_exports.boolean(),
|
|
15731
|
+
startedAt: external_exports.number(),
|
|
15732
|
+
updatedAt: external_exports.number(),
|
|
15733
|
+
finishedAt: external_exports.number().nullable(),
|
|
15734
|
+
error: external_exports.string().nullable()
|
|
15735
|
+
});
|
|
15736
|
+
var StorageMigrationPlanSchema = external_exports.object({
|
|
15737
|
+
destinations: StorageMigrationDestinationsSchema,
|
|
15738
|
+
moves: external_exports.array(external_exports.object({
|
|
15739
|
+
storageClass: StorageMigrationClassSchema,
|
|
15740
|
+
fromLocationId: external_exports.string(),
|
|
15741
|
+
toLocationId: external_exports.string()
|
|
15742
|
+
}))
|
|
15743
|
+
});
|
|
15667
15744
|
var StorageLocationTypeSchema = external_exports.string().regex(/^[a-z][a-zA-Z0-9-]*$/);
|
|
15668
15745
|
var StorageLocationSchema = external_exports.object({
|
|
15669
15746
|
id: external_exports.string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
|
|
@@ -15681,6 +15758,21 @@ var StorageLocationSchema = external_exports.object({
|
|
|
15681
15758
|
nodeId: external_exports.string().optional(),
|
|
15682
15759
|
isDefault: external_exports.boolean().default(false),
|
|
15683
15760
|
isSystem: external_exports.boolean().default(false),
|
|
15761
|
+
/**
|
|
15762
|
+
* Operator opt-in: whether consumers that BALANCE across several locations
|
|
15763
|
+
* of a type may write here. Recordings reads it today; event media and
|
|
15764
|
+
* backups are the next consumers, which is why the flag lives on the
|
|
15765
|
+
* location rather than in any one addon's store — nothing has to be
|
|
15766
|
+
* extended to add the next consumer.
|
|
15767
|
+
*
|
|
15768
|
+
* OPTIONAL, and ABSENT MEANS ACTIVE. Every location persisted before the
|
|
15769
|
+
* flag existed reads back with no flag and keeps working exactly as before;
|
|
15770
|
+
* that is the whole compat story, and it is why no migration ships with it.
|
|
15771
|
+
* A newly CREATED sibling is stamped `false` by the orchestrator (creating a
|
|
15772
|
+
* disk must not silently start writing to it); the default of a type is
|
|
15773
|
+
* always stamped `true`.
|
|
15774
|
+
*/
|
|
15775
|
+
enabled: external_exports.boolean().optional(),
|
|
15684
15776
|
/** COMPUTED at read time by the orchestrator (statfs of the backing volume
|
|
15685
15777
|
* for node-local locations it can reach) — never persisted, absent when the
|
|
15686
15778
|
* volume is remote/unreachable. The single capacity truth every UI reads. */
|
|
@@ -20575,9 +20667,9 @@ var filesystemBrowseCapability = {
|
|
|
20575
20667
|
mode: "singleton",
|
|
20576
20668
|
internal: true,
|
|
20577
20669
|
methods: {
|
|
20578
|
-
/** The
|
|
20670
|
+
/** The roots `createDir` is sandboxed to on this node. Browsing is not confined to them. */
|
|
20579
20671
|
listAllowedRoots: method(external_exports.void(), external_exports.array(external_exports.string()).readonly(), { auth: "admin" }),
|
|
20580
|
-
/** Immediate subdirectories of `path`
|
|
20672
|
+
/** Immediate subdirectories of any absolute `path` + free/total bytes. Read-only. */
|
|
20581
20673
|
browse: method(external_exports.object({ path: external_exports.string() }), BrowseResultSchema, { auth: "admin" }),
|
|
20582
20674
|
/** Create a subdirectory (within an allowed root). Returns its absolute path. */
|
|
20583
20675
|
createDir: method(external_exports.object({ path: external_exports.string() }), external_exports.object({ path: external_exports.string() }), {
|
|
@@ -22292,18 +22384,46 @@ var NcDeliverySchema = external_exports.enum([
|
|
|
22292
22384
|
"system-event"
|
|
22293
22385
|
]);
|
|
22294
22386
|
var NcSystemEventKindSchema = external_exports.enum([
|
|
22295
|
-
"
|
|
22296
|
-
"
|
|
22387
|
+
"device-online",
|
|
22388
|
+
"device-offline",
|
|
22389
|
+
"device-disabled",
|
|
22390
|
+
"device-enabled",
|
|
22297
22391
|
"stream-online",
|
|
22298
22392
|
"stream-offline",
|
|
22299
22393
|
"node-online",
|
|
22300
22394
|
"node-offline",
|
|
22301
22395
|
"addon-update-available",
|
|
22302
|
-
"server-update-available"
|
|
22396
|
+
"server-update-available",
|
|
22397
|
+
"alarm-triggered",
|
|
22398
|
+
"alarm-armed",
|
|
22399
|
+
"alarm-disarmed",
|
|
22400
|
+
"camera-online",
|
|
22401
|
+
"camera-offline",
|
|
22402
|
+
"camera-disabled",
|
|
22403
|
+
"camera-enabled"
|
|
22404
|
+
]);
|
|
22405
|
+
var NC_LEGACY_SYSTEM_EVENT_KINDS = /* @__PURE__ */ new Set([
|
|
22406
|
+
"camera-online",
|
|
22407
|
+
"camera-offline",
|
|
22408
|
+
"camera-disabled",
|
|
22409
|
+
"camera-enabled"
|
|
22303
22410
|
]);
|
|
22411
|
+
var NC_AUTHORABLE_SYSTEM_EVENT_KINDS = NcSystemEventKindSchema.options.filter((kind) => !NC_LEGACY_SYSTEM_EVENT_KINDS.has(kind));
|
|
22304
22412
|
var NcSystemEventConditionSchema = external_exports.object({
|
|
22305
22413
|
kinds: external_exports.array(NcSystemEventKindSchema).min(1),
|
|
22306
22414
|
deviceIds: external_exports.array(external_exports.number().int()).min(1).optional(),
|
|
22415
|
+
/**
|
|
22416
|
+
* Narrow a `device-*` kind to these device TYPES (`DeviceType` values —
|
|
22417
|
+
* `camera`, `switch`, `sensor`, `container`, …). Absent = every type, which
|
|
22418
|
+
* is what a liveness rule means when nobody said otherwise.
|
|
22419
|
+
*
|
|
22420
|
+
* This is where "only my cameras" is expressed, and it lives on the rule for
|
|
22421
|
+
* one reason: the intake cannot know which devices this household cares
|
|
22422
|
+
* about, and a producer-side filter is one no operator can change. Fails
|
|
22423
|
+
* CLOSED — a subject whose device type is unknown (an id the device mirror
|
|
22424
|
+
* does not carry) matches no `deviceTypes` list.
|
|
22425
|
+
*/
|
|
22426
|
+
deviceTypes: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
22307
22427
|
nodeIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
22308
22428
|
packageNames: external_exports.array(external_exports.string().min(1)).min(1).optional()
|
|
22309
22429
|
});
|
|
@@ -22339,6 +22459,16 @@ var NcOccupancyConditionSchema = external_exports.object({
|
|
|
22339
22459
|
count: external_exports.number().int().min(0).default(1),
|
|
22340
22460
|
sustainSeconds: external_exports.number().int().min(0).max(3600).default(15)
|
|
22341
22461
|
});
|
|
22462
|
+
var NcAudioConditionSchema = external_exports.object({
|
|
22463
|
+
/** Audio macro labels; absent = any sound (level-only rule). */
|
|
22464
|
+
labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
22465
|
+
/** Level floor in dBFS (negative-going, `0` = full scale); absent = any level. */
|
|
22466
|
+
dbThreshold: external_exports.number().min(-96).max(0).optional(),
|
|
22467
|
+
/** Percentage of the window's samples that must be hits (1–100). */
|
|
22468
|
+
hitPercent: external_exports.number().int().min(1).max(100).default(60),
|
|
22469
|
+
/** Length of the sampling window in seconds. */
|
|
22470
|
+
samplingSeconds: external_exports.number().int().min(1).max(300).default(10)
|
|
22471
|
+
});
|
|
22342
22472
|
var NcCrossingSchema = external_exports.enum([
|
|
22343
22473
|
"enter",
|
|
22344
22474
|
"exit",
|
|
@@ -22537,7 +22667,33 @@ var NcConditionsSchema = external_exports.object({
|
|
|
22537
22667
|
* threshold and holds for `sustainSeconds`. Fail-closed on missing
|
|
22538
22668
|
* substrate (no snapshot / missing zone). See {@link NcOccupancyCondition}.
|
|
22539
22669
|
*/
|
|
22540
|
-
occupancy: NcOccupancyConditionSchema.optional()
|
|
22670
|
+
occupancy: NcOccupancyConditionSchema.optional(),
|
|
22671
|
+
/**
|
|
22672
|
+
* IMMEDIATE only. Sustained-sound matcher — `hitPercent` of the samples in a
|
|
22673
|
+
* `samplingSeconds` window clear the optional `dbThreshold` and carry one of
|
|
22674
|
+
* the optional `labels`. Fail-closed on missing substrate (no audio samples,
|
|
22675
|
+
* a window that is not full yet, neither filter given). See
|
|
22676
|
+
* {@link NcAudioCondition}.
|
|
22677
|
+
*
|
|
22678
|
+
* Presence of this key is what makes a rule an AUDIO rule: the engine fires
|
|
22679
|
+
* it ONLY on a confirmed audio window, and a rule carrying it never fires on
|
|
22680
|
+
* a detection, a track or a device event (the same fail-closed pairing
|
|
22681
|
+
* `occupancy` has with the `device-event` trigger). That is how audio labels
|
|
22682
|
+
* leave `classes`: an audio rule names its sounds HERE, and the legacy path
|
|
22683
|
+
* (an `immediate` rule naming an `audio-*` class, one notification per
|
|
22684
|
+
* classified sample) stays exactly as it was for rules that already use it.
|
|
22685
|
+
*
|
|
22686
|
+
* NOT in {@link NC_CONDITION_CATALOG} yet, and that is the sequencing rule
|
|
22687
|
+
* rather than an oversight: the viewer mirrors the descriptor enums BY HAND
|
|
22688
|
+
* (`camstack/src/data/notification-center.ts`, guarded by
|
|
22689
|
+
* `scripts/check-viewer-condition-mirror.ts`) and its rule editor STRIPS the
|
|
22690
|
+
* condition fields it does not know when a rule is saved from the phone.
|
|
22691
|
+
* Publishing an editor for a condition the app cannot round-trip is how an
|
|
22692
|
+
* operator loses a rule's conditions by opening it — so the descriptor, the
|
|
22693
|
+
* admin widget and the viewer mirror land together (P2 + P3), and only then
|
|
22694
|
+
* does an audio rule become authorable.
|
|
22695
|
+
*/
|
|
22696
|
+
audio: NcAudioConditionSchema.optional()
|
|
22541
22697
|
});
|
|
22542
22698
|
var NcRuleTargetSchema = external_exports.object({
|
|
22543
22699
|
/** `notification-output` Target id. */
|
|
@@ -22614,6 +22770,44 @@ var NcThrottleSchema = external_exports.object({
|
|
|
22614
22770
|
*/
|
|
22615
22771
|
granularity: NcThrottleGranularitySchema.optional()
|
|
22616
22772
|
});
|
|
22773
|
+
var NC_CONFIRM_MIN_TIMEOUT_MS = 1e3;
|
|
22774
|
+
var NC_CONFIRM_MAX_TIMEOUT_MS = 2e4;
|
|
22775
|
+
var NC_CONFIRM_DEFAULT_TIMEOUT_MS = 8e3;
|
|
22776
|
+
var NcConfirmExpectSchema = external_exports.object({
|
|
22777
|
+
op: external_exports.enum([
|
|
22778
|
+
">=",
|
|
22779
|
+
">",
|
|
22780
|
+
"<=",
|
|
22781
|
+
"<",
|
|
22782
|
+
"=="
|
|
22783
|
+
]),
|
|
22784
|
+
count: external_exports.number().int().min(0).max(1e3)
|
|
22785
|
+
});
|
|
22786
|
+
var NcConfirmSchema = external_exports.object({
|
|
22787
|
+
/** Off unless asked for. An absent `confirm` and `enabled:false` are the
|
|
22788
|
+
* same thing, and both mean "deliver exactly as before". */
|
|
22789
|
+
enabled: external_exports.boolean().default(false),
|
|
22790
|
+
/** Explicit vision profile; absent = the `purpose:'vision'` cluster default. */
|
|
22791
|
+
profileId: external_exports.string().optional(),
|
|
22792
|
+
/**
|
|
22793
|
+
* The operator's question, in his own words. Absent = a question derived
|
|
22794
|
+
* from the rule (its class and its expectation).
|
|
22795
|
+
*
|
|
22796
|
+
* NEVER composed with text READ OUT OF THE FRAME. The model reads OSD
|
|
22797
|
+
* banners, signage and plates as instructions if you let them reach the
|
|
22798
|
+
* prompt — proven live — so the authoritative contract stays in the system
|
|
22799
|
+
* turn and only rule-authored words land here.
|
|
22800
|
+
*/
|
|
22801
|
+
prompt: external_exports.string().max(1e3).optional(),
|
|
22802
|
+
/** Fire only when the model's count satisfies this. Absent = the model's
|
|
22803
|
+
* own boolean verdict decides. */
|
|
22804
|
+
expect: NcConfirmExpectSchema.optional(),
|
|
22805
|
+
timeoutMs: external_exports.number().int().min(NC_CONFIRM_MIN_TIMEOUT_MS).max(NC_CONFIRM_MAX_TIMEOUT_MS).default(NC_CONFIRM_DEFAULT_TIMEOUT_MS),
|
|
22806
|
+
/** What a timeout / unreachable model MEANS. `fire` (default) = fail-open. */
|
|
22807
|
+
onTimeout: external_exports.enum(["fire", "suppress"]).default("fire"),
|
|
22808
|
+
/** Longest edge the judged image is downscaled to before it is sent. */
|
|
22809
|
+
maxImagePx: external_exports.number().int().min(64).max(2048).default(448)
|
|
22810
|
+
});
|
|
22617
22811
|
var NcRuleInputSchema = external_exports.object({
|
|
22618
22812
|
name: external_exports.string().min(1).max(200),
|
|
22619
22813
|
enabled: external_exports.boolean().default(true),
|
|
@@ -22673,9 +22867,45 @@ var NcRuleInputSchema = external_exports.object({
|
|
|
22673
22867
|
* `{ cap: 'alarm-panel', method: 'arm', args: { mode: 'away' } }`, the same
|
|
22674
22868
|
* shape as every other actuation.
|
|
22675
22869
|
*/
|
|
22676
|
-
actions: NcRuleActionsSchema.optional()
|
|
22870
|
+
actions: NcRuleActionsSchema.optional(),
|
|
22871
|
+
/**
|
|
22872
|
+
* AI CONFIRM — see {@link NcConfirmSchema}. `.optional()`, never defaulted:
|
|
22873
|
+
* a rule that predates the gate must keep delivering byte-for-byte as it
|
|
22874
|
+
* did, and absent is the only way to say that without a migration.
|
|
22875
|
+
*/
|
|
22876
|
+
confirm: NcConfirmSchema.optional()
|
|
22877
|
+
});
|
|
22878
|
+
var NcRulePatchSchema = NcRuleInputSchema.partial().extend({
|
|
22879
|
+
disabledTargetIds: external_exports.array(external_exports.string()).optional(),
|
|
22880
|
+
/**
|
|
22881
|
+
* `.partial()` DOES NOT REMOVE A FIELD'S `.default()`.
|
|
22882
|
+
*
|
|
22883
|
+
* It makes the key optional to SUPPLY; the parse still materialises the
|
|
22884
|
+
* default when the key is absent. And `NcRuleStore.update` merges with
|
|
22885
|
+
* `{ ...existing, ...patch }`, so a materialised key OVERWRITES the stored
|
|
22886
|
+
* one — which made every partial edit destructive:
|
|
22887
|
+
*
|
|
22888
|
+
* nc.updateRule({ throttle }) → conditions reset to `{}`
|
|
22889
|
+
* nc.setRuleTargetEnabled(...) → conditions reset to `{}`
|
|
22890
|
+
* setEnabled(ruleId, false) → conditions reset to `{}`
|
|
22891
|
+
*
|
|
22892
|
+
* A rule scoped to one camera and one zone silently became a rule that
|
|
22893
|
+
* matches EVERY event on EVERY camera, and lost its `media` policy
|
|
22894
|
+
* (zoneCrop / gif / clip / frame) and its `priority` at the same time. Seen
|
|
22895
|
+
* live on 2026-08-12: a rule scoped to device 617 fired on 590 and 615
|
|
22896
|
+
* within a minute of a two-field patch.
|
|
22897
|
+
*
|
|
22898
|
+
* So every defaulted field is re-declared here WITHOUT its default. The
|
|
22899
|
+
* inner defaults still apply when the caller DOES send the key — `{}` for
|
|
22900
|
+
* conditions remains a real instruction ("clear them") — and only the
|
|
22901
|
+
* absent key is now genuinely absent.
|
|
22902
|
+
*/
|
|
22903
|
+
enabled: external_exports.boolean().optional(),
|
|
22904
|
+
conditions: NcConditionsSchema.optional(),
|
|
22905
|
+
media: NcMediaPolicySchema.optional(),
|
|
22906
|
+
throttle: NcThrottleSchema.optional(),
|
|
22907
|
+
priority: external_exports.number().int().min(1).max(5).optional()
|
|
22677
22908
|
});
|
|
22678
|
-
var NcRulePatchSchema = NcRuleInputSchema.partial().extend({ disabledTargetIds: external_exports.array(external_exports.string()).optional() });
|
|
22679
22909
|
var NcRuleSchema = NcRuleInputSchema.extend({
|
|
22680
22910
|
id: external_exports.string(),
|
|
22681
22911
|
/** userId of the admin who created the rule (server-stamped caller). */
|
|
@@ -22918,6 +23148,15 @@ var NcAlarmSettingsSchema = external_exports.object({
|
|
|
22918
23148
|
announceTargets: external_exports.array(external_exports.string().min(1)).max(16)
|
|
22919
23149
|
});
|
|
22920
23150
|
var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.partial();
|
|
23151
|
+
var NcAlarmSkipReasonSchema = external_exports.enum([
|
|
23152
|
+
"muted",
|
|
23153
|
+
"detection-off",
|
|
23154
|
+
"offline"
|
|
23155
|
+
]);
|
|
23156
|
+
var NcAlarmSkippedDeviceSchema = external_exports.object({
|
|
23157
|
+
deviceId: external_exports.number().int(),
|
|
23158
|
+
reason: NcAlarmSkipReasonSchema
|
|
23159
|
+
});
|
|
22921
23160
|
var NcAlarmModeCoverageSchema = external_exports.object({
|
|
22922
23161
|
mode: AlarmArmModeSchema,
|
|
22923
23162
|
/** Enabled rules gated on `armed_<mode>`. Zero means the mode does nothing. */
|
|
@@ -22925,7 +23164,18 @@ var NcAlarmModeCoverageSchema = external_exports.object({
|
|
|
22925
23164
|
/** At least one covering rule has no device scope, so the mode covers all. */
|
|
22926
23165
|
allDevices: external_exports.boolean(),
|
|
22927
23166
|
/** Ids named by the covering rules. A SUBSET when `allDevices` is true. */
|
|
22928
|
-
deviceIds: external_exports.array(external_exports.number().int())
|
|
23167
|
+
deviceIds: external_exports.array(external_exports.number().int()),
|
|
23168
|
+
/**
|
|
23169
|
+
* Devices this mode NAMES but cannot actually arm, each with the switch that
|
|
23170
|
+
* excludes it.
|
|
23171
|
+
*
|
|
23172
|
+
* "Away armed — 12 cameras" is a promise, and a muted camera among those
|
|
23173
|
+
* twelve makes it false in exactly the way nobody notices until an incident.
|
|
23174
|
+
* Defaulted to `[]` so a coverage answer computed before this field existed
|
|
23175
|
+
* still parses as "nothing known to be skipped" rather than failing the whole
|
|
23176
|
+
* alarm tab.
|
|
23177
|
+
*/
|
|
23178
|
+
skippedDevices: external_exports.array(NcAlarmSkippedDeviceSchema).default([])
|
|
22929
23179
|
});
|
|
22930
23180
|
var NcAlarmConfigSchema = external_exports.object({
|
|
22931
23181
|
/**
|
|
@@ -24185,20 +24435,28 @@ var pipelineAnalyticsCapability = {
|
|
|
24185
24435
|
}),
|
|
24186
24436
|
/** The events ops-log rows (newest-first), optionally scoped to one camera.
|
|
24187
24437
|
* Backed by a declared pipeline-analytics SQLite collection. */
|
|
24188
|
-
/**
|
|
24189
|
-
*
|
|
24190
|
-
|
|
24191
|
-
* at the target are skipped, so a re-run resumes. Single-flight.
|
|
24192
|
-
*/
|
|
24193
|
-
relocateMedia: method(RelocateMediaInputSchema, external_exports.object({ jobId: external_exports.string() }), {
|
|
24438
|
+
/** Internal migration-participant lease. It drains active MediaStore
|
|
24439
|
+
* writes and refuses new ones without changing analytics bindings. */
|
|
24440
|
+
pauseForStorageMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ paused: external_exports.literal(true) }), {
|
|
24194
24441
|
kind: "mutation",
|
|
24195
24442
|
auth: "admin"
|
|
24196
24443
|
}),
|
|
24197
|
-
|
|
24198
|
-
kind: "
|
|
24444
|
+
resumeForStorageMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ resumed: external_exports.literal(true) }), {
|
|
24445
|
+
kind: "mutation",
|
|
24446
|
+
auth: "admin"
|
|
24447
|
+
}),
|
|
24448
|
+
/** Drops cached location roots after the storage coordinator repoints a
|
|
24449
|
+
* default so future event-media writes use the new root. */
|
|
24450
|
+
refreshStorageLocationsForMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ refreshed: external_exports.literal(true) }), {
|
|
24451
|
+
kind: "mutation",
|
|
24452
|
+
auth: "admin"
|
|
24453
|
+
}),
|
|
24454
|
+
startStorageMigrationMove: method(StorageMigrationMediaMoveInputSchema, external_exports.object({ jobId: external_exports.string() }), {
|
|
24455
|
+
kind: "mutation",
|
|
24199
24456
|
auth: "admin"
|
|
24200
24457
|
}),
|
|
24201
|
-
|
|
24458
|
+
getStorageMigrationMoveStatus: method(external_exports.object({ jobId: external_exports.string() }), RelocateJobSchema.nullable(), { auth: "admin" }),
|
|
24459
|
+
cancelStorageMigrationMove: method(external_exports.object({ jobId: external_exports.string() }), external_exports.object({ cancelled: external_exports.boolean() }), {
|
|
24202
24460
|
kind: "mutation",
|
|
24203
24461
|
auth: "admin"
|
|
24204
24462
|
}),
|
|
@@ -26035,9 +26293,16 @@ var CameraStatusSchema = external_exports.object({
|
|
|
26035
26293
|
audio: CameraAudioStatusSchema.nullable(),
|
|
26036
26294
|
recording: CameraRecordingStatusSchema.nullable(),
|
|
26037
26295
|
/**
|
|
26038
|
-
* Per-camera
|
|
26296
|
+
* Per-camera functions an OPERATOR has turned off
|
|
26039
26297
|
* ([D61](../../../../docs/decisions/adr-0067.md)).
|
|
26040
26298
|
*
|
|
26299
|
+
* Composed from the AUTHORITIES themselves — the wrapper bindings,
|
|
26300
|
+
* `RecordingConfig.enabled`, the notification mute, the broker's audio
|
|
26301
|
+
* policy, the camera's own microphone — via `composeSwitchedOff`, not from
|
|
26302
|
+
* the deprecated `getCameraSwitches` group ([D113](../../../../docs/decisions/adr-0113.md)).
|
|
26303
|
+
* The badge outlives the control panel: the panel was a convenience, this is
|
|
26304
|
+
* the difference between a camera being off and a camera being dead.
|
|
26305
|
+
*
|
|
26041
26306
|
* This is the difference between DISABLED and BROKEN. A camera whose
|
|
26042
26307
|
* `detection` block reports zero fps and whose `switchedOff` contains
|
|
26043
26308
|
* `'object-detection'` was switched off by a person; the same camera with an
|
|
@@ -26115,6 +26380,16 @@ var pipelineOrchestratorCapability = {
|
|
|
26115
26380
|
nodeIdMode: "data",
|
|
26116
26381
|
exposesDeviceSettings: true,
|
|
26117
26382
|
methods: {
|
|
26383
|
+
/** Internal storage-migration participant lease. While held, dispatch
|
|
26384
|
+
* requests are accepted as pending but no new runner attachment starts. */
|
|
26385
|
+
pauseForStorageMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ paused: external_exports.literal(true) }), {
|
|
26386
|
+
kind: "mutation",
|
|
26387
|
+
auth: "admin"
|
|
26388
|
+
}),
|
|
26389
|
+
resumeForStorageMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ resumed: external_exports.literal(true) }), {
|
|
26390
|
+
kind: "mutation",
|
|
26391
|
+
auth: "admin"
|
|
26392
|
+
}),
|
|
26118
26393
|
/**
|
|
26119
26394
|
* Pin a camera's pipeline to a specific agent (L1 affinity).
|
|
26120
26395
|
* The orchestrator re-evaluates the assignment immediately and persists
|
|
@@ -26454,6 +26729,10 @@ var pipelineOrchestratorCapability = {
|
|
|
26454
26729
|
agentNodeId: external_exports.string().optional()
|
|
26455
26730
|
}), CameraPipelineConfigSchema),
|
|
26456
26731
|
/**
|
|
26732
|
+
* @deprecated The aggregate switch group is being withdrawn
|
|
26733
|
+
* ([D113](../../../../docs/decisions/adr-0113.md)). Build nothing new on
|
|
26734
|
+
* this pair; read the authority directly.
|
|
26735
|
+
*
|
|
26457
26736
|
* The whole per-camera function switch group, DERIVED — never a stored
|
|
26458
26737
|
* list ([D61](../../../../docs/decisions/adr-0067.md)).
|
|
26459
26738
|
*
|
|
@@ -26467,9 +26746,23 @@ var pipelineOrchestratorCapability = {
|
|
|
26467
26746
|
* `auth: 'view'` deliberately — a NON-admin must be able to see that a
|
|
26468
26747
|
* camera is quiet because somebody switched it off. Only the mutation is
|
|
26469
26748
|
* admin-gated.
|
|
26749
|
+
*
|
|
26750
|
+
* **Removal plan.** It stays and it KEEPS WORKING while shipped viewers
|
|
26751
|
+
* (v1.0.305) and the admin UI still call it — removing it now is a broken
|
|
26752
|
+
* app on a device nobody can redeploy from here. It is served by a thin
|
|
26753
|
+
* shim over the same authorities (`camera-switch-service.ts`), so the
|
|
26754
|
+
* behaviour of the pair is the behaviour of the authorities by
|
|
26755
|
+
* construction. It is deleted once every surface reaches its own
|
|
26756
|
+
* component's options and the last caller is gone. Nothing on this server
|
|
26757
|
+
* reads it: `CameraStatus.switchedOff` is composed from the authorities
|
|
26758
|
+
* directly via `composeSwitchedOff`.
|
|
26470
26759
|
*/
|
|
26471
26760
|
getCameraSwitches: method(external_exports.object({ deviceId: external_exports.number() }), CameraSwitchGroupSchema),
|
|
26472
26761
|
/**
|
|
26762
|
+
* @deprecated See {@link getCameraSwitches}. Write the authority — the
|
|
26763
|
+
* wrapper binding, `RecordingConfig.enabled`, the notification mute — not
|
|
26764
|
+
* this ([D113](../../../../docs/decisions/adr-0113.md)).
|
|
26765
|
+
*
|
|
26473
26766
|
* Flip ONE switch, routed to its existing authority.
|
|
26474
26767
|
*
|
|
26475
26768
|
* Never writes a parallel map: `recording` patches `RecordingConfig.enabled`
|
|
@@ -26847,24 +27140,28 @@ var snapshotCapability = {
|
|
|
26847
27140
|
*
|
|
26848
27141
|
* `getSnapshotOverview` is cache-only by contract: it answers from whatever
|
|
26849
27142
|
* the wrapper happens to hold and never captures. Under D93 the client
|
|
26850
|
-
* versions its image URL on that answer, and an image REQUEST
|
|
26851
|
-
*
|
|
26852
|
-
*
|
|
26853
|
-
*
|
|
26854
|
-
*
|
|
26855
|
-
*
|
|
26856
|
-
*
|
|
26857
|
-
*
|
|
27143
|
+
* versions its image URL on that answer, and an image REQUEST was the only
|
|
27144
|
+
* demand signal. Both of those are satisfiable by the client's own image
|
|
27145
|
+
* cache — `expo-image` is URL-keyed and never revalidates — so a URL painted
|
|
27146
|
+
* in a previous session comes off disk with no network, no demand, and no
|
|
27147
|
+
* capture. Measured on the live hub: reopening after two minutes idle
|
|
27148
|
+
* painted 15 of 16 tiles at **168 s old** with zero HTTP requests, and the
|
|
27149
|
+
* fleet only recovered because a later poll happened to observe a different
|
|
27150
|
+
* identity.
|
|
26858
27151
|
*
|
|
26859
27152
|
* ## The two properties that fix it
|
|
26860
27153
|
*
|
|
26861
27154
|
* **It is an RPC, so no client cache can answer it.** The demand signal
|
|
26862
|
-
* always reaches the wrapper. This method therefore
|
|
26863
|
-
*
|
|
26864
|
-
*
|
|
26865
|
-
*
|
|
26866
|
-
*
|
|
26867
|
-
*
|
|
27155
|
+
* always reaches the wrapper. This method therefore CAPTURES, where
|
|
27156
|
+
* `getSnapshotOverview` must never (D93) — the distinction is not "one is
|
|
27157
|
+
* newer" but that the overview poll is app-wide (a capturing overview would
|
|
27158
|
+
* dial every camera on the install) while this is called by a rendered
|
|
27159
|
+
* surface naming the tiles it is actually painting, at the width it is
|
|
27160
|
+
* painting them.
|
|
27161
|
+
*
|
|
27162
|
+
* Since 2026-08-11 this is the ONLY thing that refreshes a snapshot: the
|
|
27163
|
+
* server-side keep-warm loop was removed (operator directive — on-demand,
|
|
27164
|
+
* always), so a camera nobody is looking at costs nothing at all.
|
|
26868
27165
|
*
|
|
26869
27166
|
* **It waits, briefly and boundedly, for the capture it triggered.** The
|
|
26870
27167
|
* returned `capturedAt` is the frame the link will serve, not the frame the
|
|
@@ -27108,6 +27405,23 @@ var storageEvictableCapability = {
|
|
|
27108
27405
|
}), EvictResultSchema, { kind: "mutation" })
|
|
27109
27406
|
}
|
|
27110
27407
|
};
|
|
27408
|
+
var storageMigrationCapability = {
|
|
27409
|
+
name: "storage-migration",
|
|
27410
|
+
scope: "system",
|
|
27411
|
+
mode: "singleton",
|
|
27412
|
+
methods: {
|
|
27413
|
+
plan: method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin" }),
|
|
27414
|
+
start: method(StorageMigrationInputSchema, external_exports.object({ jobId: external_exports.string() }), {
|
|
27415
|
+
kind: "mutation",
|
|
27416
|
+
auth: "admin"
|
|
27417
|
+
}),
|
|
27418
|
+
status: method(external_exports.object({ jobId: external_exports.string().optional() }), StorageMigrationJobSchema.nullable(), { auth: "admin" }),
|
|
27419
|
+
cancel: method(external_exports.object({ jobId: external_exports.string() }), external_exports.object({ cancelled: external_exports.boolean() }), {
|
|
27420
|
+
kind: "mutation",
|
|
27421
|
+
auth: "admin"
|
|
27422
|
+
})
|
|
27423
|
+
}
|
|
27424
|
+
};
|
|
27111
27425
|
var ProviderInfoSchema = external_exports.discriminatedUnion("shouldSaveDiskSpace", [external_exports.object({
|
|
27112
27426
|
providerId: external_exports.string().min(1),
|
|
27113
27427
|
displayName: external_exports.string().min(1),
|
|
@@ -27222,6 +27536,24 @@ var TerminalProfileInfoSchema = external_exports.object({
|
|
|
27222
27536
|
label: external_exports.string(),
|
|
27223
27537
|
description: external_exports.string().optional()
|
|
27224
27538
|
});
|
|
27539
|
+
var TerminalInstanceInfoSchema = external_exports.object({
|
|
27540
|
+
instanceId: external_exports.string(),
|
|
27541
|
+
cameraStableId: external_exports.string(),
|
|
27542
|
+
nodeId: external_exports.string(),
|
|
27543
|
+
profileId: external_exports.string(),
|
|
27544
|
+
profileLabel: external_exports.string(),
|
|
27545
|
+
name: external_exports.string(),
|
|
27546
|
+
enabled: external_exports.boolean()
|
|
27547
|
+
});
|
|
27548
|
+
var TerminalLegacyCameraSchema = external_exports.object({
|
|
27549
|
+
stableId: external_exports.string(),
|
|
27550
|
+
nodeId: external_exports.string(),
|
|
27551
|
+
profileId: external_exports.string(),
|
|
27552
|
+
profileLabel: external_exports.string(),
|
|
27553
|
+
name: external_exports.string(),
|
|
27554
|
+
/** Only legacy monitor cameras can retain their historic stable identity. */
|
|
27555
|
+
adoptable: external_exports.boolean()
|
|
27556
|
+
});
|
|
27225
27557
|
var TerminalOutputEventSchema = external_exports.discriminatedUnion("kind", [external_exports.object({
|
|
27226
27558
|
seq: external_exports.number().int().positive(),
|
|
27227
27559
|
kind: external_exports.literal("data"),
|
|
@@ -27245,6 +27577,37 @@ var terminalSessionCapability = {
|
|
|
27245
27577
|
methods: {
|
|
27246
27578
|
/** Pre-declared profiles the operator may open. */
|
|
27247
27579
|
listProfiles: method(external_exports.void(), external_exports.array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
27580
|
+
/** Explicit durable Terminal instances, managed centrally on the hub. */
|
|
27581
|
+
listInstances: method(external_exports.void(), external_exports.array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }),
|
|
27582
|
+
createInstance: method(external_exports.object({
|
|
27583
|
+
targetNodeId: external_exports.string().min(1),
|
|
27584
|
+
profileId: external_exports.string().min(1),
|
|
27585
|
+
name: external_exports.string().trim().min(1).max(160).optional()
|
|
27586
|
+
}), TerminalInstanceInfoSchema, {
|
|
27587
|
+
kind: "mutation",
|
|
27588
|
+
auth: "admin"
|
|
27589
|
+
}),
|
|
27590
|
+
deleteInstance: method(external_exports.object({ instanceId: external_exports.string().min(1) }), external_exports.void(), {
|
|
27591
|
+
kind: "mutation",
|
|
27592
|
+
auth: "admin"
|
|
27593
|
+
}),
|
|
27594
|
+
setInstanceEnabled: method(external_exports.object({
|
|
27595
|
+
instanceId: external_exports.string().min(1),
|
|
27596
|
+
enabled: external_exports.boolean()
|
|
27597
|
+
}), TerminalInstanceInfoSchema, {
|
|
27598
|
+
kind: "mutation",
|
|
27599
|
+
auth: "admin"
|
|
27600
|
+
}),
|
|
27601
|
+
/** Existing automatic cameras are shown for explicit migration only. */
|
|
27602
|
+
listLegacyCameras: method(external_exports.void(), external_exports.array(TerminalLegacyCameraSchema).readonly(), { auth: "admin" }),
|
|
27603
|
+
/** Explicitly adopt one legacy monitor camera, retaining its stable id. */
|
|
27604
|
+
adoptLegacyMonitor: method(external_exports.object({
|
|
27605
|
+
stableId: external_exports.string().min(1),
|
|
27606
|
+
name: external_exports.string().trim().min(1).max(160).optional()
|
|
27607
|
+
}), TerminalInstanceInfoSchema, {
|
|
27608
|
+
kind: "mutation",
|
|
27609
|
+
auth: "admin"
|
|
27610
|
+
}),
|
|
27248
27611
|
/** Live sessions currently hosted by the provider. */
|
|
27249
27612
|
listSessions: method(external_exports.void(), external_exports.array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }),
|
|
27250
27613
|
/**
|
|
@@ -27276,7 +27639,13 @@ var terminalSessionCapability = {
|
|
|
27276
27639
|
pullOutput: method(external_exports.object({
|
|
27277
27640
|
sessionId: external_exports.string(),
|
|
27278
27641
|
afterSeq: external_exports.number().int().nonnegative(),
|
|
27279
|
-
waitMs: external_exports.number().int().min(0).max(2e3).default(0)
|
|
27642
|
+
waitMs: external_exports.number().int().min(0).max(2e3).default(0),
|
|
27643
|
+
/**
|
|
27644
|
+
* Wait when a just-opened session has no output yet. Kept opt-in so a
|
|
27645
|
+
* browser's initial repaint remains immediate; the camera snapshot
|
|
27646
|
+
* relay uses it to avoid encoding a blank startup frame.
|
|
27647
|
+
*/
|
|
27648
|
+
waitForOutput: external_exports.boolean().optional()
|
|
27280
27649
|
}), TerminalOutputBatchSchema, {
|
|
27281
27650
|
kind: "mutation",
|
|
27282
27651
|
auth: "admin",
|
|
@@ -29656,6 +30025,7 @@ var FaceInfoSchema = external_exports.object({
|
|
|
29656
30025
|
var FaceFilterEnum = external_exports.enum([
|
|
29657
30026
|
"unassigned",
|
|
29658
30027
|
"recognized",
|
|
30028
|
+
"identified",
|
|
29659
30029
|
"all"
|
|
29660
30030
|
]);
|
|
29661
30031
|
var MediaFileLiteSchema$1 = external_exports.object({
|
|
@@ -29695,6 +30065,8 @@ var faceGalleryCapability = {
|
|
|
29695
30065
|
auth: "admin"
|
|
29696
30066
|
}),
|
|
29697
30067
|
listRecentFaces: method(external_exports.object({
|
|
30068
|
+
/** Restrict to one camera. Absent keeps the cluster-wide gallery view. */
|
|
30069
|
+
deviceId: external_exports.number().int().optional(),
|
|
29698
30070
|
limit: external_exports.number().int().positive().optional(),
|
|
29699
30071
|
filter: FaceFilterEnum.optional(),
|
|
29700
30072
|
/**
|
|
@@ -31771,6 +32143,10 @@ var OsdSourceSchema = external_exports.discriminatedUnion("kind", [
|
|
|
31771
32143
|
capName: external_exports.string().min(1).max(64),
|
|
31772
32144
|
/** Dot path inside the slice, e.g. `detected`, `value`, `mode`. */
|
|
31773
32145
|
valuePath: external_exports.string().min(1).max(64)
|
|
32146
|
+
}),
|
|
32147
|
+
external_exports.object({
|
|
32148
|
+
kind: external_exports.literal("latest-recognition"),
|
|
32149
|
+
recognition: external_exports.enum(["person", "plate"])
|
|
31774
32150
|
})
|
|
31775
32151
|
]);
|
|
31776
32152
|
var OsdSlotBindingSchema = external_exports.object({
|
|
@@ -31897,6 +32273,21 @@ var osdManagerCapability = {
|
|
|
31897
32273
|
auth: "admin"
|
|
31898
32274
|
}),
|
|
31899
32275
|
/**
|
|
32276
|
+
* Replace one camera's binding configuration from another. Writable source
|
|
32277
|
+
* slots are mapped by ordinal onto writable target slots so different
|
|
32278
|
+
* provider slot ids do not make the copied configuration disappear.
|
|
32279
|
+
*/
|
|
32280
|
+
copyDeviceConfiguration: method(external_exports.object({
|
|
32281
|
+
sourceDeviceId: external_exports.number().int(),
|
|
32282
|
+
targetDeviceId: external_exports.number().int()
|
|
32283
|
+
}), external_exports.object({
|
|
32284
|
+
copied: external_exports.number().int().nonnegative(),
|
|
32285
|
+
skipped: external_exports.number().int().nonnegative()
|
|
32286
|
+
}), {
|
|
32287
|
+
kind: "mutation",
|
|
32288
|
+
auth: "admin"
|
|
32289
|
+
}),
|
|
32290
|
+
/**
|
|
31900
32291
|
* Render without writing. `binding` overrides the stored one so an
|
|
31901
32292
|
* editor can preview an unsaved change. Mutation kind only to carry
|
|
31902
32293
|
* the binding object safely; no side effects.
|
|
@@ -32808,7 +33199,19 @@ var RecordingManifestSchema = external_exports.object({
|
|
|
32808
33199
|
});
|
|
32809
33200
|
var RecordingDeviceUsageSchema = external_exports.object({
|
|
32810
33201
|
deviceId: external_exports.number(),
|
|
32811
|
-
usedBytes: external_exports.number()
|
|
33202
|
+
usedBytes: external_exports.number(),
|
|
33203
|
+
/**
|
|
33204
|
+
* Start of this camera's OLDEST indexed segment, across every profile and
|
|
33205
|
+
* location — the "Oldest footage" column in Recordings → Storage, and the
|
|
33206
|
+
* only honest answer to "is retention actually holding?" per camera.
|
|
33207
|
+
*
|
|
33208
|
+
* `null` = the camera has no footage. OPTIONAL because a recorder that
|
|
33209
|
+
* predates this field omits it entirely, and a hub whose types carry the
|
|
33210
|
+
* field must keep validating that older provider's payload: the framework
|
|
33211
|
+
* (types) and the addon ship on different trains, and the addon is usually
|
|
33212
|
+
* the later of the two.
|
|
33213
|
+
*/
|
|
33214
|
+
oldestMs: external_exports.number().nullable().optional()
|
|
32812
33215
|
});
|
|
32813
33216
|
var RecordingLocationUsageSchema = external_exports.object({
|
|
32814
33217
|
/** StorageLocation id; null for the legacy/degraded single-root fallback. */
|
|
@@ -32831,6 +33234,42 @@ var RecordingStorageUsageSchema = external_exports.object({
|
|
|
32831
33234
|
devices: external_exports.array(RecordingDeviceUsageSchema),
|
|
32832
33235
|
locations: external_exports.array(RecordingLocationUsageSchema)
|
|
32833
33236
|
});
|
|
33237
|
+
var RecordingRebalanceMoveSchema = external_exports.object({
|
|
33238
|
+
deviceId: external_exports.number(),
|
|
33239
|
+
profile: external_exports.string(),
|
|
33240
|
+
fromLocationId: external_exports.string(),
|
|
33241
|
+
toLocationId: external_exports.string(),
|
|
33242
|
+
bytes: external_exports.number(),
|
|
33243
|
+
files: external_exports.number().int()
|
|
33244
|
+
});
|
|
33245
|
+
var RecordingRebalanceSkipReasonSchema = external_exports.enum([
|
|
33246
|
+
"unassigned",
|
|
33247
|
+
"target-not-writable",
|
|
33248
|
+
"below-threshold",
|
|
33249
|
+
"no-headroom"
|
|
33250
|
+
]);
|
|
33251
|
+
var RecordingRebalanceSkipSchema = external_exports.object({
|
|
33252
|
+
deviceId: external_exports.number(),
|
|
33253
|
+
profile: external_exports.string(),
|
|
33254
|
+
fromLocationId: external_exports.string(),
|
|
33255
|
+
/** The location the plan wants; null when the camera has no assignment. */
|
|
33256
|
+
toLocationId: external_exports.string().nullable(),
|
|
33257
|
+
bytes: external_exports.number(),
|
|
33258
|
+
reason: RecordingRebalanceSkipReasonSchema
|
|
33259
|
+
});
|
|
33260
|
+
var RecordingRebalancePlanSchema = external_exports.object({
|
|
33261
|
+
moves: external_exports.array(RecordingRebalanceMoveSchema),
|
|
33262
|
+
skipped: external_exports.array(RecordingRebalanceSkipSchema),
|
|
33263
|
+
bytesToMove: external_exports.number(),
|
|
33264
|
+
/** Relocate job ids enqueued. Always empty for the plan (dry-run) call. */
|
|
33265
|
+
jobIds: external_exports.array(external_exports.string())
|
|
33266
|
+
});
|
|
33267
|
+
var RecordingRebalanceInputSchema = external_exports.object({
|
|
33268
|
+
/** Copy throttle in MB/s (default 40) — a rebalance is a background chore. */
|
|
33269
|
+
throttleMbps: external_exports.number().min(1).max(1e3).optional(),
|
|
33270
|
+
/** Ignore piles smaller than this (default 1 GB). */
|
|
33271
|
+
minMoveGb: external_exports.number().min(0).optional()
|
|
33272
|
+
});
|
|
32834
33273
|
var LocateSegmentResultSchema = external_exports.discriminatedUnion("kind", [external_exports.object({
|
|
32835
33274
|
kind: external_exports.literal("segment"),
|
|
32836
33275
|
startMs: external_exports.number(),
|
|
@@ -32981,12 +33420,6 @@ var recordingCapability = {
|
|
|
32981
33420
|
auth: "admin"
|
|
32982
33421
|
}),
|
|
32983
33422
|
/**
|
|
32984
|
-
* Move footage (recorded segments) from one recordings location to
|
|
32985
|
-
* another — the drain workflow's mover (entity-routing spec Phase 4).
|
|
32986
|
-
* Throttled copy → size-verify → delete source → index refresh; resumable
|
|
32987
|
-
* by construction (copy-if-absent). Single-flight: one job at a time.
|
|
32988
|
-
*/
|
|
32989
|
-
/**
|
|
32990
33423
|
* Render a short GIF from recorded footage around `aroundMs` (low profile,
|
|
32991
33424
|
* palette-free ffmpeg gif). Synchronous — the window is a few seconds of
|
|
32992
33425
|
* `low`, rendering in ~1-2s. Built for notification attachments: the
|
|
@@ -33038,25 +33471,83 @@ var recordingCapability = {
|
|
|
33038
33471
|
kind: "mutation",
|
|
33039
33472
|
auth: "admin"
|
|
33040
33473
|
}),
|
|
33474
|
+
/** Internal migration-participant lease. Blocks new recorder attaches and
|
|
33475
|
+
* waits for writers to exit while their watchers index final segments. */
|
|
33476
|
+
pauseForStorageMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ paused: external_exports.literal(true) }), {
|
|
33477
|
+
kind: "mutation",
|
|
33478
|
+
auth: "admin"
|
|
33479
|
+
}),
|
|
33480
|
+
resumeForStorageMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ resumed: external_exports.literal(true) }), {
|
|
33481
|
+
kind: "mutation",
|
|
33482
|
+
auth: "admin"
|
|
33483
|
+
}),
|
|
33484
|
+
/** Re-resolve location roots after the coordinator changes defaults. */
|
|
33485
|
+
refreshStorageLocationsForMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ refreshed: external_exports.literal(true) }), {
|
|
33486
|
+
kind: "mutation",
|
|
33487
|
+
auth: "admin"
|
|
33488
|
+
}),
|
|
33489
|
+
/** Internal mover, callable only by the storage-migration coordinator. */
|
|
33490
|
+
startStorageMigrationMove: method(StorageMigrationFootageMoveInputSchema, external_exports.object({ jobId: external_exports.string() }), {
|
|
33491
|
+
kind: "mutation",
|
|
33492
|
+
auth: "admin"
|
|
33493
|
+
}),
|
|
33494
|
+
getStorageMigrationMoveStatus: method(external_exports.object({ jobId: external_exports.string() }), RelocateJobSchema.nullable(), { auth: "admin" }),
|
|
33495
|
+
cancelStorageMigrationMove: method(external_exports.object({ jobId: external_exports.string() }), external_exports.object({ cancelled: external_exports.boolean() }), {
|
|
33496
|
+
kind: "mutation",
|
|
33497
|
+
auth: "admin"
|
|
33498
|
+
}),
|
|
33499
|
+
/**
|
|
33500
|
+
* Move footage between locations — the OPERATOR's mover, scoped to one
|
|
33501
|
+
* camera (and optionally to specific profiles) rather than a whole disk.
|
|
33502
|
+
* Queued FIFO behind the single-flight engine, so arming several is safe.
|
|
33503
|
+
*/
|
|
33041
33504
|
relocateFootage: method(RelocateFootageInputSchema, external_exports.object({ jobId: external_exports.string() }), {
|
|
33042
33505
|
kind: "mutation",
|
|
33043
33506
|
auth: "admin"
|
|
33044
33507
|
}),
|
|
33045
|
-
/** Every
|
|
33046
|
-
|
|
33508
|
+
/** Every relocate job this recorder knows about, newest first (in RAM: the
|
|
33509
|
+
* move is resumable, so a lost list costs nothing but the display). */
|
|
33510
|
+
listRelocateJobs: method(external_exports.object({}), external_exports.array(RelocateJobSchema).readonly(), {
|
|
33511
|
+
kind: "query",
|
|
33512
|
+
auth: "admin"
|
|
33513
|
+
}),
|
|
33514
|
+
/** Cancel a running or queued relocate job. A queued job never runs. */
|
|
33515
|
+
cancelRelocateJob: method(external_exports.object({ jobId: external_exports.string() }), external_exports.object({ cancelled: external_exports.boolean() }), {
|
|
33516
|
+
kind: "mutation",
|
|
33517
|
+
auth: "admin"
|
|
33518
|
+
}),
|
|
33519
|
+
/** What a rebalance WOULD move, and what it would refuse. Moves nothing. */
|
|
33520
|
+
planStorageRebalance: method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
|
|
33047
33521
|
kind: "query",
|
|
33048
33522
|
auth: "admin"
|
|
33049
33523
|
}),
|
|
33050
|
-
|
|
33524
|
+
/** Arm the rebalance: enqueue one relocate job per planned move. */
|
|
33525
|
+
startStorageRebalance: method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
|
|
33051
33526
|
kind: "mutation",
|
|
33052
33527
|
auth: "admin"
|
|
33053
33528
|
})
|
|
33054
33529
|
}
|
|
33055
33530
|
};
|
|
33056
33531
|
var ExportSpeedSchema = external_exports.number().min(0.25).max(32);
|
|
33532
|
+
var ExportDenseRangeSchema = external_exports.object({
|
|
33533
|
+
fromSec: external_exports.number().nonnegative(),
|
|
33534
|
+
toSec: external_exports.number().nonnegative()
|
|
33535
|
+
}).refine((r) => r.toSec > r.fromSec, { message: "dense range must have toSec > fromSec" });
|
|
33536
|
+
var ExportDenseSchema = external_exports.object({
|
|
33537
|
+
everyMs: external_exports.number().int().positive(),
|
|
33538
|
+
ranges: external_exports.array(ExportDenseRangeSchema).min(1).max(200)
|
|
33539
|
+
});
|
|
33057
33540
|
var ExportTimelapseSchema = external_exports.object({
|
|
33058
33541
|
everyMs: external_exports.number().int().positive(),
|
|
33059
|
-
outputFps: external_exports.number().int().min(1).max(60).optional()
|
|
33542
|
+
outputFps: external_exports.number().int().min(1).max(60).optional(),
|
|
33543
|
+
/** Optional second, FASTER rate over the intervals that matter. */
|
|
33544
|
+
dense: ExportDenseSchema.optional()
|
|
33545
|
+
}).superRefine((v, ctx) => {
|
|
33546
|
+
if (v.dense !== void 0 && v.dense.everyMs >= v.everyMs) ctx.addIssue({
|
|
33547
|
+
code: external_exports.ZodIssueCode.custom,
|
|
33548
|
+
message: "dense.everyMs must be strictly smaller than the base everyMs",
|
|
33549
|
+
path: ["dense", "everyMs"]
|
|
33550
|
+
});
|
|
33060
33551
|
});
|
|
33061
33552
|
var ExportOptionsSchema = external_exports.object({
|
|
33062
33553
|
speed: ExportSpeedSchema.optional(),
|
|
@@ -33106,8 +33597,16 @@ var ExportDownloadSchema = external_exports.object({
|
|
|
33106
33597
|
url: external_exports.string(),
|
|
33107
33598
|
endpoints: external_exports.array(external_exports.string())
|
|
33108
33599
|
});
|
|
33600
|
+
var RECORDING_EXPORT_MAX_READ_BYTES = 50 * 1024 * 1024;
|
|
33601
|
+
var ExportBytesSchema = external_exports.object({
|
|
33602
|
+
base64: external_exports.string(),
|
|
33603
|
+
contentType: external_exports.string(),
|
|
33604
|
+
/** Suggested filename, extension included. */
|
|
33605
|
+
name: external_exports.string(),
|
|
33606
|
+
bytes: external_exports.number().int().nonnegative()
|
|
33607
|
+
});
|
|
33109
33608
|
var recordingExportCapability = {
|
|
33110
|
-
name: "
|
|
33609
|
+
name: "recording-export",
|
|
33111
33610
|
scope: "system",
|
|
33112
33611
|
mode: "singleton",
|
|
33113
33612
|
methods: {
|
|
@@ -33145,6 +33644,27 @@ var recordingExportCapability = {
|
|
|
33145
33644
|
getDownloadUrl: method(external_exports.object({ exportId: external_exports.string() }), ExportDownloadSchema, {
|
|
33146
33645
|
kind: "query",
|
|
33147
33646
|
auth: "protected"
|
|
33647
|
+
}),
|
|
33648
|
+
/**
|
|
33649
|
+
* The finished file's BYTES, base64, for a caller that must republish them
|
|
33650
|
+
* somewhere a session-less fetcher can reach.
|
|
33651
|
+
*
|
|
33652
|
+
* `getDownloadUrl` is the right answer for a human: the download route is
|
|
33653
|
+
* served `access: 'authenticated'`, which a browser satisfies and a
|
|
33654
|
+
* notifier BACKEND does not. It answers a RELATIVE path, so it is not even
|
|
33655
|
+
* a URL an outside fetcher could try. This method exists for the one case
|
|
33656
|
+
* that needs the other thing — a scheduled timelapse whose video has to
|
|
33657
|
+
* become a public attachment on the notification artifact plane.
|
|
33658
|
+
*
|
|
33659
|
+
* Deliberately narrow: `ready` only (a queued, rendering, failed, expired
|
|
33660
|
+
* or deleted export has no file, and answering "0 bytes" for one is how a
|
|
33661
|
+
* caller ships an empty attachment), still inside its lifetime, and under
|
|
33662
|
+
* {@link RECORDING_EXPORT_MAX_READ_BYTES}. Every refusal throws with the
|
|
33663
|
+
* reason — none of them is silent.
|
|
33664
|
+
*/
|
|
33665
|
+
readExportBytes: method(external_exports.object({ exportId: external_exports.string() }), ExportBytesSchema, {
|
|
33666
|
+
kind: "query",
|
|
33667
|
+
auth: "protected"
|
|
33148
33668
|
})
|
|
33149
33669
|
}
|
|
33150
33670
|
};
|
|
@@ -37396,6 +37916,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
37396
37916
|
addonId: null,
|
|
37397
37917
|
access: "delete"
|
|
37398
37918
|
},
|
|
37919
|
+
"osdManager.copyDeviceConfiguration": {
|
|
37920
|
+
capName: "osd-manager",
|
|
37921
|
+
capScope: "system",
|
|
37922
|
+
addonId: null,
|
|
37923
|
+
access: "create"
|
|
37924
|
+
},
|
|
37399
37925
|
"osdManager.getConditionSupport": {
|
|
37400
37926
|
capName: "osd-manager",
|
|
37401
37927
|
capScope: "system",
|
|
@@ -37492,7 +38018,7 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
37492
38018
|
addonId: null,
|
|
37493
38019
|
access: "create"
|
|
37494
38020
|
},
|
|
37495
|
-
"pipelineAnalytics.
|
|
38021
|
+
"pipelineAnalytics.cancelStorageMigrationMove": {
|
|
37496
38022
|
capName: "pipeline-analytics",
|
|
37497
38023
|
capScope: "device",
|
|
37498
38024
|
addonId: null,
|
|
@@ -37564,12 +38090,6 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
37564
38090
|
addonId: null,
|
|
37565
38091
|
access: "view"
|
|
37566
38092
|
},
|
|
37567
|
-
"pipelineAnalytics.getMediaRelocateStatus": {
|
|
37568
|
-
capName: "pipeline-analytics",
|
|
37569
|
-
capScope: "device",
|
|
37570
|
-
addonId: null,
|
|
37571
|
-
access: "view"
|
|
37572
|
-
},
|
|
37573
38093
|
"pipelineAnalytics.getMotionEvents": {
|
|
37574
38094
|
capName: "pipeline-analytics",
|
|
37575
38095
|
capScope: "device",
|
|
@@ -37606,6 +38126,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
37606
38126
|
addonId: null,
|
|
37607
38127
|
access: "view"
|
|
37608
38128
|
},
|
|
38129
|
+
"pipelineAnalytics.getStorageMigrationMoveStatus": {
|
|
38130
|
+
capName: "pipeline-analytics",
|
|
38131
|
+
capScope: "device",
|
|
38132
|
+
addonId: null,
|
|
38133
|
+
access: "view"
|
|
38134
|
+
},
|
|
37609
38135
|
"pipelineAnalytics.getTrack": {
|
|
37610
38136
|
capName: "pipeline-analytics",
|
|
37611
38137
|
capScope: "device",
|
|
@@ -37684,6 +38210,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
37684
38210
|
addonId: null,
|
|
37685
38211
|
access: "view"
|
|
37686
38212
|
},
|
|
38213
|
+
"pipelineAnalytics.pauseForStorageMigration": {
|
|
38214
|
+
capName: "pipeline-analytics",
|
|
38215
|
+
capScope: "device",
|
|
38216
|
+
addonId: null,
|
|
38217
|
+
access: "create"
|
|
38218
|
+
},
|
|
37687
38219
|
"pipelineAnalytics.proposeRetrainAnnotations": {
|
|
37688
38220
|
capName: "pipeline-analytics",
|
|
37689
38221
|
capScope: "device",
|
|
@@ -37714,7 +38246,7 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
37714
38246
|
addonId: null,
|
|
37715
38247
|
access: "create"
|
|
37716
38248
|
},
|
|
37717
|
-
"pipelineAnalytics.
|
|
38249
|
+
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
37718
38250
|
capName: "pipeline-analytics",
|
|
37719
38251
|
capScope: "device",
|
|
37720
38252
|
addonId: null,
|
|
@@ -37726,6 +38258,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
37726
38258
|
addonId: null,
|
|
37727
38259
|
access: "create"
|
|
37728
38260
|
},
|
|
38261
|
+
"pipelineAnalytics.resumeForStorageMigration": {
|
|
38262
|
+
capName: "pipeline-analytics",
|
|
38263
|
+
capScope: "device",
|
|
38264
|
+
addonId: null,
|
|
38265
|
+
access: "create"
|
|
38266
|
+
},
|
|
37729
38267
|
"pipelineAnalytics.saveRetrainAnnotations": {
|
|
37730
38268
|
capName: "pipeline-analytics",
|
|
37731
38269
|
capScope: "device",
|
|
@@ -37750,6 +38288,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
37750
38288
|
addonId: null,
|
|
37751
38289
|
access: "create"
|
|
37752
38290
|
},
|
|
38291
|
+
"pipelineAnalytics.startStorageMigrationMove": {
|
|
38292
|
+
capName: "pipeline-analytics",
|
|
38293
|
+
capScope: "device",
|
|
38294
|
+
addonId: null,
|
|
38295
|
+
access: "create"
|
|
38296
|
+
},
|
|
37753
38297
|
"pipelineAnalytics.wipeAllAnalytics": {
|
|
37754
38298
|
capName: "pipeline-analytics",
|
|
37755
38299
|
capScope: "device",
|
|
@@ -38116,6 +38660,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
38116
38660
|
addonId: null,
|
|
38117
38661
|
access: "view"
|
|
38118
38662
|
},
|
|
38663
|
+
"pipelineOrchestrator.pauseForStorageMigration": {
|
|
38664
|
+
capName: "pipeline-orchestrator",
|
|
38665
|
+
capScope: "system",
|
|
38666
|
+
addonId: null,
|
|
38667
|
+
access: "create"
|
|
38668
|
+
},
|
|
38119
38669
|
"pipelineOrchestrator.rebalance": {
|
|
38120
38670
|
capName: "pipeline-orchestrator",
|
|
38121
38671
|
capScope: "system",
|
|
@@ -38140,6 +38690,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
38140
38690
|
addonId: null,
|
|
38141
38691
|
access: "view"
|
|
38142
38692
|
},
|
|
38693
|
+
"pipelineOrchestrator.resumeForStorageMigration": {
|
|
38694
|
+
capName: "pipeline-orchestrator",
|
|
38695
|
+
capScope: "system",
|
|
38696
|
+
addonId: null,
|
|
38697
|
+
access: "create"
|
|
38698
|
+
},
|
|
38143
38699
|
"pipelineOrchestrator.saveTemplate": {
|
|
38144
38700
|
capName: "pipeline-orchestrator",
|
|
38145
38701
|
capScope: "system",
|
|
@@ -38536,7 +39092,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
38536
39092
|
addonId: null,
|
|
38537
39093
|
access: "create"
|
|
38538
39094
|
},
|
|
38539
|
-
"recording.
|
|
39095
|
+
"recording.cancelRelocateJob": {
|
|
39096
|
+
capName: "recording",
|
|
39097
|
+
capScope: "system",
|
|
39098
|
+
addonId: null,
|
|
39099
|
+
access: "create"
|
|
39100
|
+
},
|
|
39101
|
+
"recording.cancelStorageMigrationMove": {
|
|
38540
39102
|
capName: "recording",
|
|
38541
39103
|
capScope: "system",
|
|
38542
39104
|
addonId: null,
|
|
@@ -38572,7 +39134,7 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
38572
39134
|
addonId: null,
|
|
38573
39135
|
access: "view"
|
|
38574
39136
|
},
|
|
38575
|
-
"recording.
|
|
39137
|
+
"recording.getStorageMigrationMoveStatus": {
|
|
38576
39138
|
capName: "recording",
|
|
38577
39139
|
capScope: "system",
|
|
38578
39140
|
addonId: null,
|
|
@@ -38590,12 +39152,30 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
38590
39152
|
addonId: null,
|
|
38591
39153
|
access: "view"
|
|
38592
39154
|
},
|
|
39155
|
+
"recording.listRelocateJobs": {
|
|
39156
|
+
capName: "recording",
|
|
39157
|
+
capScope: "system",
|
|
39158
|
+
addonId: null,
|
|
39159
|
+
access: "view"
|
|
39160
|
+
},
|
|
38593
39161
|
"recording.locateSegment": {
|
|
38594
39162
|
capName: "recording",
|
|
38595
39163
|
capScope: "system",
|
|
38596
39164
|
addonId: null,
|
|
38597
39165
|
access: "view"
|
|
38598
39166
|
},
|
|
39167
|
+
"recording.pauseForStorageMigration": {
|
|
39168
|
+
capName: "recording",
|
|
39169
|
+
capScope: "system",
|
|
39170
|
+
addonId: null,
|
|
39171
|
+
access: "create"
|
|
39172
|
+
},
|
|
39173
|
+
"recording.planStorageRebalance": {
|
|
39174
|
+
capName: "recording",
|
|
39175
|
+
capScope: "system",
|
|
39176
|
+
addonId: null,
|
|
39177
|
+
access: "view"
|
|
39178
|
+
},
|
|
38599
39179
|
"recording.pruneFootage": {
|
|
38600
39180
|
capName: "recording",
|
|
38601
39181
|
capScope: "system",
|
|
@@ -38614,6 +39194,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
38614
39194
|
addonId: null,
|
|
38615
39195
|
access: "view"
|
|
38616
39196
|
},
|
|
39197
|
+
"recording.refreshStorageLocationsForMigration": {
|
|
39198
|
+
capName: "recording",
|
|
39199
|
+
capScope: "system",
|
|
39200
|
+
addonId: null,
|
|
39201
|
+
access: "create"
|
|
39202
|
+
},
|
|
38617
39203
|
"recording.relocateFootage": {
|
|
38618
39204
|
capName: "recording",
|
|
38619
39205
|
capScope: "system",
|
|
@@ -38638,44 +39224,68 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
38638
39224
|
addonId: null,
|
|
38639
39225
|
access: "create"
|
|
38640
39226
|
},
|
|
39227
|
+
"recording.resumeForStorageMigration": {
|
|
39228
|
+
capName: "recording",
|
|
39229
|
+
capScope: "system",
|
|
39230
|
+
addonId: null,
|
|
39231
|
+
access: "create"
|
|
39232
|
+
},
|
|
38641
39233
|
"recording.setDeviceConfig": {
|
|
38642
39234
|
capName: "recording",
|
|
38643
39235
|
capScope: "system",
|
|
38644
39236
|
addonId: null,
|
|
38645
39237
|
access: "create"
|
|
38646
39238
|
},
|
|
39239
|
+
"recording.startStorageMigrationMove": {
|
|
39240
|
+
capName: "recording",
|
|
39241
|
+
capScope: "system",
|
|
39242
|
+
addonId: null,
|
|
39243
|
+
access: "create"
|
|
39244
|
+
},
|
|
39245
|
+
"recording.startStorageRebalance": {
|
|
39246
|
+
capName: "recording",
|
|
39247
|
+
capScope: "system",
|
|
39248
|
+
addonId: null,
|
|
39249
|
+
access: "create"
|
|
39250
|
+
},
|
|
38647
39251
|
"recordingExport.cancelExport": {
|
|
38648
|
-
capName: "
|
|
39252
|
+
capName: "recording-export",
|
|
38649
39253
|
capScope: "system",
|
|
38650
39254
|
addonId: null,
|
|
38651
39255
|
access: "create"
|
|
38652
39256
|
},
|
|
38653
39257
|
"recordingExport.createExport": {
|
|
38654
|
-
capName: "
|
|
39258
|
+
capName: "recording-export",
|
|
38655
39259
|
capScope: "system",
|
|
38656
39260
|
addonId: null,
|
|
38657
39261
|
access: "create"
|
|
38658
39262
|
},
|
|
38659
39263
|
"recordingExport.deleteExport": {
|
|
38660
|
-
capName: "
|
|
39264
|
+
capName: "recording-export",
|
|
38661
39265
|
capScope: "system",
|
|
38662
39266
|
addonId: null,
|
|
38663
39267
|
access: "delete"
|
|
38664
39268
|
},
|
|
38665
39269
|
"recordingExport.getDownloadUrl": {
|
|
38666
|
-
capName: "
|
|
39270
|
+
capName: "recording-export",
|
|
38667
39271
|
capScope: "system",
|
|
38668
39272
|
addonId: null,
|
|
38669
39273
|
access: "view"
|
|
38670
39274
|
},
|
|
38671
39275
|
"recordingExport.getExport": {
|
|
38672
|
-
capName: "
|
|
39276
|
+
capName: "recording-export",
|
|
38673
39277
|
capScope: "system",
|
|
38674
39278
|
addonId: null,
|
|
38675
39279
|
access: "view"
|
|
38676
39280
|
},
|
|
38677
39281
|
"recordingExport.listExports": {
|
|
38678
|
-
capName: "
|
|
39282
|
+
capName: "recording-export",
|
|
39283
|
+
capScope: "system",
|
|
39284
|
+
addonId: null,
|
|
39285
|
+
access: "view"
|
|
39286
|
+
},
|
|
39287
|
+
"recordingExport.readExportBytes": {
|
|
39288
|
+
capName: "recording-export",
|
|
38679
39289
|
capScope: "system",
|
|
38680
39290
|
addonId: null,
|
|
38681
39291
|
access: "view"
|
|
@@ -39034,6 +39644,30 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
39034
39644
|
addonId: null,
|
|
39035
39645
|
access: "view"
|
|
39036
39646
|
},
|
|
39647
|
+
"storageMigration.cancel": {
|
|
39648
|
+
capName: "storage-migration",
|
|
39649
|
+
capScope: "system",
|
|
39650
|
+
addonId: null,
|
|
39651
|
+
access: "create"
|
|
39652
|
+
},
|
|
39653
|
+
"storageMigration.plan": {
|
|
39654
|
+
capName: "storage-migration",
|
|
39655
|
+
capScope: "system",
|
|
39656
|
+
addonId: null,
|
|
39657
|
+
access: "view"
|
|
39658
|
+
},
|
|
39659
|
+
"storageMigration.start": {
|
|
39660
|
+
capName: "storage-migration",
|
|
39661
|
+
capScope: "system",
|
|
39662
|
+
addonId: null,
|
|
39663
|
+
access: "create"
|
|
39664
|
+
},
|
|
39665
|
+
"storageMigration.status": {
|
|
39666
|
+
capName: "storage-migration",
|
|
39667
|
+
capScope: "system",
|
|
39668
|
+
addonId: null,
|
|
39669
|
+
access: "view"
|
|
39670
|
+
},
|
|
39037
39671
|
"storageProvider.abortUpload": {
|
|
39038
39672
|
capName: "storage-provider",
|
|
39039
39673
|
capScope: "system",
|
|
@@ -39412,12 +40046,42 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
39412
40046
|
addonId: null,
|
|
39413
40047
|
access: "create"
|
|
39414
40048
|
},
|
|
40049
|
+
"terminalSession.adoptLegacyMonitor": {
|
|
40050
|
+
capName: "terminal-session",
|
|
40051
|
+
capScope: "system",
|
|
40052
|
+
addonId: null,
|
|
40053
|
+
access: "create"
|
|
40054
|
+
},
|
|
39415
40055
|
"terminalSession.close": {
|
|
39416
40056
|
capName: "terminal-session",
|
|
39417
40057
|
capScope: "system",
|
|
39418
40058
|
addonId: null,
|
|
39419
40059
|
access: "create"
|
|
39420
40060
|
},
|
|
40061
|
+
"terminalSession.createInstance": {
|
|
40062
|
+
capName: "terminal-session",
|
|
40063
|
+
capScope: "system",
|
|
40064
|
+
addonId: null,
|
|
40065
|
+
access: "create"
|
|
40066
|
+
},
|
|
40067
|
+
"terminalSession.deleteInstance": {
|
|
40068
|
+
capName: "terminal-session",
|
|
40069
|
+
capScope: "system",
|
|
40070
|
+
addonId: null,
|
|
40071
|
+
access: "delete"
|
|
40072
|
+
},
|
|
40073
|
+
"terminalSession.listInstances": {
|
|
40074
|
+
capName: "terminal-session",
|
|
40075
|
+
capScope: "system",
|
|
40076
|
+
addonId: null,
|
|
40077
|
+
access: "view"
|
|
40078
|
+
},
|
|
40079
|
+
"terminalSession.listLegacyCameras": {
|
|
40080
|
+
capName: "terminal-session",
|
|
40081
|
+
capScope: "system",
|
|
40082
|
+
addonId: null,
|
|
40083
|
+
access: "view"
|
|
40084
|
+
},
|
|
39421
40085
|
"terminalSession.listProfiles": {
|
|
39422
40086
|
capName: "terminal-session",
|
|
39423
40087
|
capScope: "system",
|
|
@@ -39448,6 +40112,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
39448
40112
|
addonId: null,
|
|
39449
40113
|
access: "create"
|
|
39450
40114
|
},
|
|
40115
|
+
"terminalSession.setInstanceEnabled": {
|
|
40116
|
+
capName: "terminal-session",
|
|
40117
|
+
capScope: "system",
|
|
40118
|
+
addonId: null,
|
|
40119
|
+
access: "create"
|
|
40120
|
+
},
|
|
39451
40121
|
"terminalSession.writeInput": {
|
|
39452
40122
|
capName: "terminal-session",
|
|
39453
40123
|
capScope: "system",
|
|
@@ -39968,6 +40638,12 @@ var CadenceSecField = external_exports.number().int().min(2).max(3600);
|
|
|
39968
40638
|
var FramerateField = external_exports.number().int().min(1).max(60);
|
|
39969
40639
|
var TargetsField = external_exports.array(NcRuleTargetSchema).min(1);
|
|
39970
40640
|
var PriorityField = external_exports.number().int().min(1).max(5);
|
|
40641
|
+
var TIMELAPSE_DENSE_FLOOR_SEC = 0.1;
|
|
40642
|
+
var DenseCadenceSecField = external_exports.number().min(TIMELAPSE_DENSE_FLOOR_SEC).max(3600);
|
|
40643
|
+
var MinDwellSecField = external_exports.number().min(0).max(60);
|
|
40644
|
+
var PreviewTextField = external_exports.string().max(200);
|
|
40645
|
+
var PreviewModeField = external_exports.enum(["image", "gif"]);
|
|
40646
|
+
var ReportClassesField = external_exports.array(external_exports.string().min(1).max(40)).max(20);
|
|
39971
40647
|
var TimelapseRuleInputSchema = external_exports.object({
|
|
39972
40648
|
name: NameField,
|
|
39973
40649
|
enabled: external_exports.boolean().default(true),
|
|
@@ -39983,9 +40659,30 @@ var TimelapseRuleInputSchema = external_exports.object({
|
|
|
39983
40659
|
cadenceSec: CadenceSecField.default(15),
|
|
39984
40660
|
/** Output frames per second of the assembled mp4 (predecessor parity). */
|
|
39985
40661
|
framerate: FramerateField.default(10),
|
|
40662
|
+
/**
|
|
40663
|
+
* Explicit dense cadence — see {@link DenseCadenceSecField}. Absent ⇒ derived
|
|
40664
|
+
* as `max(1s, cadenceSec / 30)`, which is what every rule written before this
|
|
40665
|
+
* field gets.
|
|
40666
|
+
*/
|
|
40667
|
+
denseCadenceSec: DenseCadenceSecField.optional(),
|
|
40668
|
+
/** Output-seconds guarantee per detection range — see {@link MinDwellSecField}. */
|
|
40669
|
+
minDwellSec: MinDwellSecField.optional(),
|
|
39986
40670
|
/** `notification-output` targets the finished video/thumbnail is sent to. */
|
|
39987
40671
|
targets: TargetsField,
|
|
39988
40672
|
template: TimelapseTemplateSchema.optional(),
|
|
40673
|
+
/**
|
|
40674
|
+
* Caption on the notification's PREVIEW FRAME — see {@link PreviewTextField}
|
|
40675
|
+
* and {@link DEFAULT_TIMELAPSE_PREVIEW_TEXT}.
|
|
40676
|
+
*
|
|
40677
|
+
* Deliberately NOT part of {@link TimelapseTemplateSchema}: that object is
|
|
40678
|
+
* the notification's title/body, and clearing it (`template: null`) must not
|
|
40679
|
+
* silently clear the caption too.
|
|
40680
|
+
*/
|
|
40681
|
+
previewText: PreviewTextField.optional(),
|
|
40682
|
+
/** Still or animation — see {@link PreviewModeField}. */
|
|
40683
|
+
previewMode: PreviewModeField.default("image"),
|
|
40684
|
+
/** Classes the notification counts — see {@link ReportClassesField}. */
|
|
40685
|
+
reportClasses: ReportClassesField.optional(),
|
|
39989
40686
|
/** Canonical notification priority ordinal (1..5); per-target overridable. */
|
|
39990
40687
|
priority: PriorityField.default(3)
|
|
39991
40688
|
});
|
|
@@ -39996,8 +40693,13 @@ var TimelapseRulePatchSchema = external_exports.object({
|
|
|
39996
40693
|
schedule: NcScheduleSchema.optional(),
|
|
39997
40694
|
cadenceSec: CadenceSecField.optional(),
|
|
39998
40695
|
framerate: FramerateField.optional(),
|
|
40696
|
+
denseCadenceSec: DenseCadenceSecField.optional(),
|
|
40697
|
+
minDwellSec: MinDwellSecField.optional(),
|
|
39999
40698
|
targets: TargetsField.optional(),
|
|
40000
40699
|
template: TimelapseTemplateSchema.nullable().optional(),
|
|
40700
|
+
previewText: PreviewTextField.optional(),
|
|
40701
|
+
previewMode: PreviewModeField.optional(),
|
|
40702
|
+
reportClasses: ReportClassesField.optional(),
|
|
40001
40703
|
priority: PriorityField.optional()
|
|
40002
40704
|
});
|
|
40003
40705
|
var TimelapseRuleSchema = TimelapseRuleInputSchema.extend({
|
|
@@ -40009,10 +40711,28 @@ var TimelapseRuleSchema = TimelapseRuleInputSchema.extend({
|
|
|
40009
40711
|
*/
|
|
40010
40712
|
ownerUserId: external_exports.string().optional(),
|
|
40011
40713
|
/**
|
|
40012
|
-
* Epoch-ms of the
|
|
40013
|
-
*
|
|
40714
|
+
* Epoch-ms of the NEWEST successful generation across every camera of this
|
|
40715
|
+
* rule. What a UI shows, and the compatibility floor for
|
|
40716
|
+
* {@link readTimelapseGeneratedAt}. Absent = never generated.
|
|
40014
40717
|
*/
|
|
40015
40718
|
lastGeneratedAt: external_exports.number().optional(),
|
|
40719
|
+
/**
|
|
40720
|
+
* PER-CAMERA generation state, keyed by `String(deviceId)` — the
|
|
40721
|
+
* re-generation guard's real durable state.
|
|
40722
|
+
*
|
|
40723
|
+
* One rule covers several cameras and each renders its own video, so a rule
|
|
40724
|
+
* -wide stamp is wrong in the direction that DESTROYS work: camera A
|
|
40725
|
+
* succeeding at 06:05 tells camera B, whose render failed, that it is
|
|
40726
|
+
* already done — and B's night is gone for good, because the window will not
|
|
40727
|
+
* come back.
|
|
40728
|
+
*
|
|
40729
|
+
* ADDITIVE, so the migration is free: a row written before this field simply
|
|
40730
|
+
* has no map, and {@link readTimelapseGeneratedAt} falls back to
|
|
40731
|
+
* {@link TimelapseRuleSchema.shape.lastGeneratedAt}. Reading an old row as
|
|
40732
|
+
* "never generated" would re-render and re-notify every camera of every rule
|
|
40733
|
+
* once, on the deploy that shipped the map.
|
|
40734
|
+
*/
|
|
40735
|
+
generatedByDevice: external_exports.record(external_exports.string(), external_exports.number()).optional(),
|
|
40016
40736
|
/** userId of the caller who created the rule (server-stamped). */
|
|
40017
40737
|
createdBy: external_exports.string(),
|
|
40018
40738
|
createdAt: external_exports.number(),
|