@camstack/addon-pipeline 1.2.18 → 1.2.27
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/audio-analyzer/index.js +1 -1
- package/dist/audio-analyzer/index.mjs +3 -2
- package/dist/chunk-DnnnRqeS.mjs +26 -0
- package/dist/detection-pipeline/index.js +2 -2
- package/dist/detection-pipeline/index.mjs +4 -3
- package/dist/{dist-OjOsoSZG.mjs → dist-CsKaXmP8.mjs} +500 -130
- package/dist/{dist-DDTAur0R.js → dist-DUHlZcWd.js} +505 -135
- package/dist/{model-download-service-Cp9f4dk6-WO-foD7W.mjs → model-download-service-Cp9f4dk6-CwUb5v3Y.mjs} +1 -26
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +162 -19
- package/dist/pipeline-runner/index.mjs +162 -19
- package/dist/recorder/index.js +2604 -458
- package/dist/recorder/index.mjs +2599 -459
- package/dist/{step-definitions-DGCuoeCj.js → step-definitions-Bo1hJQQw.js} +1 -1
- package/dist/{step-definitions-B9vyUNYR.mjs → step-definitions-C8A78dBi.mjs} +1 -1
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-S2nRs43G.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-tHahN-lK.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D1zJmtUe.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CSWUWZ8c.mjs +26 -0
- package/dist/stream-broker/{hostInit-p_rpDGqr.mjs → hostInit-CIwilJUZ.mjs} +3 -3
- package/dist/stream-broker/index.js +7374 -250
- package/dist/stream-broker/index.mjs +7371 -249
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-Hg_mGtXA.js → MaskShapeCanvas-DI4BY7W2-B7VdNJlO.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-C0-pVeBX.js → MotionZonesSettings-NcxxQN8r-YyB4uLdH.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-QVePTd0g.js → PrivacyMaskSettings-APgPLF7p-DfqixEad.js} +1 -1
- package/embed-dist/assets/{index-Dv5H-32C.css → index-CAe6Dg-X.css} +1 -1
- package/embed-dist/assets/index-zVLLVdIS.js +343 -0
- package/embed-dist/index.html +2 -2
- package/package.json +5 -1
- package/dist/fmp4-keyframes-5s8T9Mng.js +0 -6711
- package/dist/fmp4-keyframes-CV0X1mGy.mjs +0 -6694
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BDeEvhV5.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BGtpZkgz.mjs +0 -26
- package/embed-dist/assets/index-CsaHbiy9.js +0 -116
|
@@ -7371,35 +7371,22 @@ var EVENT_PAD_MS = {
|
|
|
7371
7371
|
*/
|
|
7372
7372
|
var RecordingWeekdaySchema = number().int().min(0).max(6);
|
|
7373
7373
|
var HHMM = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
7374
|
-
var RecordingScheduleSchema = discriminatedUnion("kind", [object({ kind: literal("always") }), object({
|
|
7375
|
-
kind: literal("timeOfDay"),
|
|
7376
|
-
start: string().regex(HHMM),
|
|
7377
|
-
end: string().regex(HHMM),
|
|
7378
|
-
/** Restrict to these weekdays; omit = every day. */
|
|
7379
|
-
days: array(RecordingWeekdaySchema).optional()
|
|
7380
|
-
})]);
|
|
7381
|
-
var RecordingModeSchema = _enum([
|
|
7382
|
-
"continuous",
|
|
7383
|
-
"onMotion",
|
|
7384
|
-
"onAudioThreshold"
|
|
7385
|
-
]);
|
|
7386
7374
|
/**
|
|
7387
|
-
*
|
|
7388
|
-
*
|
|
7389
|
-
* - `off` —
|
|
7390
|
-
* - `events` —
|
|
7391
|
-
*
|
|
7392
|
-
* - `continuous` — record 24/7 within the schedule.
|
|
7375
|
+
* DERIVED per-camera storage summary — the single field cheap consumers read
|
|
7376
|
+
* (the viewer's status dot, the camera list) instead of walking `bands`:
|
|
7377
|
+
* - `off` — no band covers the camera (or it is disabled).
|
|
7378
|
+
* - `events` — every band records around triggers only.
|
|
7379
|
+
* - `continuous` — at least one band records continuously.
|
|
7393
7380
|
*
|
|
7394
|
-
*
|
|
7395
|
-
*
|
|
7381
|
+
* NEVER authored: the recorder stamps it from the authoritative `bands` on
|
|
7382
|
+
* every save (`activeModeForConfig`). Writing it has no effect.
|
|
7396
7383
|
*/
|
|
7397
7384
|
var RecordingStorageModeSchema = _enum([
|
|
7398
7385
|
"off",
|
|
7399
7386
|
"events",
|
|
7400
7387
|
"continuous"
|
|
7401
7388
|
]);
|
|
7402
|
-
/** Which detectors trigger an `events`-mode
|
|
7389
|
+
/** Which detectors trigger an `events`-mode band. */
|
|
7403
7390
|
var RecordingTriggersSchema = object({
|
|
7404
7391
|
motion: boolean().optional(),
|
|
7405
7392
|
audioThresholdDbfs: number().optional()
|
|
@@ -7435,18 +7422,6 @@ var RecordingBandSchema = object({
|
|
|
7435
7422
|
preBufferSec: number().min(0).optional(),
|
|
7436
7423
|
postBufferSec: number().min(0).optional()
|
|
7437
7424
|
});
|
|
7438
|
-
var RecordingRuleSchema = object({
|
|
7439
|
-
schedule: RecordingScheduleSchema,
|
|
7440
|
-
mode: RecordingModeSchema,
|
|
7441
|
-
/** Seconds of footage to retain BEFORE a trigger (applied at keep/discard). */
|
|
7442
|
-
preBufferSec: number().min(0).default(0),
|
|
7443
|
-
/** Keep recording until this many seconds after the last trigger. */
|
|
7444
|
-
postBufferSec: number().min(0).default(0),
|
|
7445
|
-
/** Each new trigger restarts the post-buffer window. */
|
|
7446
|
-
resetTimeoutOnNewEvent: boolean().default(true),
|
|
7447
|
-
/** onAudioThreshold only — dBFS level that counts as a trigger. */
|
|
7448
|
-
thresholdDbfs: number().optional()
|
|
7449
|
-
});
|
|
7450
7425
|
/**
|
|
7451
7426
|
* Per-device retention overrides. Every field is optional; an unset or `0`
|
|
7452
7427
|
* value inherits the node-wide recorder default. Only footage-lifetime limits
|
|
@@ -7480,40 +7455,28 @@ var ScrubThumbnailPresetSchema = _enum([
|
|
|
7480
7455
|
/**
|
|
7481
7456
|
* The full per-camera recording intent — the wire shape of a RecordingTarget.
|
|
7482
7457
|
*
|
|
7483
|
-
* `
|
|
7484
|
-
*
|
|
7485
|
-
*
|
|
7486
|
-
*
|
|
7458
|
+
* `bands` is the ONLY authored recording intent: what to record, when, and on
|
|
7459
|
+
* which trigger. `mode` is a derived summary the recorder stamps on save; every
|
|
7460
|
+
* other field is a storage knob (profiles, segment length, retention, scrub).
|
|
7461
|
+
*
|
|
7462
|
+
* STRICT on purpose: the legacy authoring surface (`schedule`/`schedules`/
|
|
7463
|
+
* `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30.
|
|
7464
|
+
* A stale caller must fail loudly — silently stripping its legacy intent would
|
|
7465
|
+
* persist a band-less config, i.e. silently stop recording the camera.
|
|
7487
7466
|
*/
|
|
7488
7467
|
var RecordingConfigSchema = object({
|
|
7489
7468
|
enabled: boolean(),
|
|
7490
|
-
/**
|
|
7491
|
-
*
|
|
7469
|
+
/** DERIVED summary of `bands`, stamped by the recorder on every save.
|
|
7470
|
+
* Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
|
|
7492
7471
|
mode: RecordingStorageModeSchema.optional(),
|
|
7493
7472
|
profiles: array(CamProfileSchema).optional(),
|
|
7494
7473
|
segmentSeconds: number().int().positive().optional(),
|
|
7495
|
-
/** Shared recording time-bands for `events` & `continuous` — record only when
|
|
7496
|
-
* the wall-clock falls inside one of these windows. Omit or empty = always.
|
|
7497
|
-
* `continuous` compiles to one rule per band; `events` to band × trigger. */
|
|
7498
|
-
schedules: array(RecordingScheduleSchema).optional(),
|
|
7499
|
-
/** Legacy single-band predecessor of `schedules`. Read-compat only — it is
|
|
7500
|
-
* normalized into `schedules` on read and never written going forward. (Not
|
|
7501
|
-
* tagged `@deprecated`: the normalization paths must read it cast-free.) */
|
|
7502
|
-
schedule: RecordingScheduleSchema.optional(),
|
|
7503
|
-
/** `events`-mode only — which detectors trigger a recording. */
|
|
7504
|
-
triggers: RecordingTriggersSchema.optional(),
|
|
7505
|
-
/** `events`-mode only — seconds retained before / after a trigger. */
|
|
7506
|
-
preBufferSec: number().min(0).optional(),
|
|
7507
|
-
postBufferSec: number().min(0).optional(),
|
|
7508
|
-
/** DEPRECATED authoring input; retained for migration/transition. */
|
|
7509
|
-
rules: array(RecordingRuleSchema).optional(),
|
|
7510
7474
|
/**
|
|
7511
|
-
* AUTHORITATIVE mode-per-band recording model
|
|
7512
|
-
*
|
|
7513
|
-
*
|
|
7514
|
-
* derived into bands once via `migrateConfigToBands`.
|
|
7475
|
+
* AUTHORITATIVE mode-per-band recording model — the single source of truth
|
|
7476
|
+
* the recorder's band engine consumes. An empty array = record nothing;
|
|
7477
|
+
* "off" is the absence of a covering band, never a band value.
|
|
7515
7478
|
*/
|
|
7516
|
-
bands: array(RecordingBandSchema).
|
|
7479
|
+
bands: array(RecordingBandSchema).default([]),
|
|
7517
7480
|
retention: RecordingRetentionSchema.optional(),
|
|
7518
7481
|
/**
|
|
7519
7482
|
* Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
|
|
@@ -7521,60 +7484,30 @@ var RecordingConfigSchema = object({
|
|
|
7521
7484
|
* windows only — existing sheets are immutable, and each window's index
|
|
7522
7485
|
* carries its own tile dims so mixed-preset history renders correctly.
|
|
7523
7486
|
*/
|
|
7524
|
-
scrubThumbnails: ScrubThumbnailPresetSchema.optional()
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
*
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
*
|
|
7539
|
-
*
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
if (mode === "continuous") return
|
|
7547
|
-
|
|
7548
|
-
start,
|
|
7549
|
-
end,
|
|
7550
|
-
mode
|
|
7551
|
-
};
|
|
7552
|
-
return {
|
|
7553
|
-
days,
|
|
7554
|
-
start,
|
|
7555
|
-
end,
|
|
7556
|
-
mode,
|
|
7557
|
-
...config.triggers ? { triggers: config.triggers } : {},
|
|
7558
|
-
...config.preBufferSec !== void 0 ? { preBufferSec: config.preBufferSec } : {},
|
|
7559
|
-
...config.postBufferSec !== void 0 ? { postBufferSec: config.postBufferSec } : {}
|
|
7560
|
-
};
|
|
7561
|
-
}
|
|
7562
|
-
/**
|
|
7563
|
-
* One-way migration from the legacy recording-config shape to the authoritative
|
|
7564
|
-
* mode-per-band model. Pure (no clock access).
|
|
7565
|
-
*
|
|
7566
|
-
* - If `config.bands` is present, it is authoritative and returned as-is.
|
|
7567
|
-
* - Otherwise the result is derived from the legacy fields: a disabled config or
|
|
7568
|
-
* `mode: 'off'` yields no bands; every legacy schedule maps to one band whose
|
|
7569
|
-
* mode is `continuous` when `config.mode === 'continuous'`, else `events`
|
|
7570
|
-
* (carrying `triggers`/`preBufferSec`/`postBufferSec`).
|
|
7571
|
-
*/
|
|
7572
|
-
function migrateConfigToBands(config) {
|
|
7573
|
-
if (config.bands) return config.bands;
|
|
7574
|
-
if (!config.enabled || config.mode === "off") return [];
|
|
7575
|
-
const schedules = config.schedules ?? (config.schedule ? [config.schedule] : [{ kind: "always" }]);
|
|
7576
|
-
const bandMode = config.mode === "continuous" ? "continuous" : "events";
|
|
7577
|
-
return schedules.map((schedule) => bandFromSchedule(schedule, bandMode, config));
|
|
7487
|
+
scrubThumbnails: ScrubThumbnailPresetSchema.optional(),
|
|
7488
|
+
/**
|
|
7489
|
+
* OPT-IN thumbnail-strip generation for this camera: every keyframe of the
|
|
7490
|
+
* low recording saved as a JPEG (the fast-drag scrub depth), a derived
|
|
7491
|
+
* cache that eviction reclaims with the footage. Absent/false = no strips
|
|
7492
|
+
* are written and scrub reads exact keyframes at every velocity.
|
|
7493
|
+
*/
|
|
7494
|
+
stripsEnabled: boolean().optional()
|
|
7495
|
+
}).strict();
|
|
7496
|
+
/**
|
|
7497
|
+
* Derive the {@link RecordingStorageModeSchema} summary from the authoritative
|
|
7498
|
+
* bands: `continuous` when any band records continuously, `events` when a band
|
|
7499
|
+
* exists at all, else `off`. Pure.
|
|
7500
|
+
*
|
|
7501
|
+
* THE one definition: the recorder stamps `config.mode` with it on save and
|
|
7502
|
+
* reports it as the cap's `activeMode`, and the settings UI reads it back to
|
|
7503
|
+
* decide which authoring tab a config opens in. Two copies would drift, and the
|
|
7504
|
+
* symptom (a status dot disagreeing with what is being recorded) is exactly the
|
|
7505
|
+
* bug this field was added to fix.
|
|
7506
|
+
*/
|
|
7507
|
+
function deriveRecordingMode(config) {
|
|
7508
|
+
if (!config.enabled || config.bands.length === 0) return "off";
|
|
7509
|
+
if (config.bands.some((band) => band.mode === "continuous")) return "continuous";
|
|
7510
|
+
return "events";
|
|
7578
7511
|
}
|
|
7579
7512
|
/**
|
|
7580
7513
|
* Ops-log — the durable, append-only operations audit shared by the
|
|
@@ -7594,7 +7527,8 @@ var OpsLogOpSchema = _enum([
|
|
|
7594
7527
|
"prune",
|
|
7595
7528
|
"manual-delete",
|
|
7596
7529
|
"rescan",
|
|
7597
|
-
"retention-run"
|
|
7530
|
+
"retention-run",
|
|
7531
|
+
"relocate"
|
|
7598
7532
|
]);
|
|
7599
7533
|
/** Why the operation ran. */
|
|
7600
7534
|
var OpsLogReasonSchema = _enum([
|
|
@@ -7633,6 +7567,55 @@ var OpsLogQueryInputSchema = object({
|
|
|
7633
7567
|
limit: number().int().min(1).max(1e3).optional()
|
|
7634
7568
|
});
|
|
7635
7569
|
/**
|
|
7570
|
+
* Entity-relocation job state (storage entity-routing spec, Phase 4).
|
|
7571
|
+
*
|
|
7572
|
+
* One shape shared by the recorder's `relocateFootage` (segments + strips) and
|
|
7573
|
+
* pipeline-analytics' `relocateMedia` (event media blobs) so the admin Data
|
|
7574
|
+
* page renders both movers with one component. Jobs are in-RAM (a restart
|
|
7575
|
+
* forgets them — re-running is safe by construction: copy-if-absent, delete
|
|
7576
|
+
* after verify) and each completed/failed run also lands one durable ops-log
|
|
7577
|
+
* row on the owning addon's surface.
|
|
7578
|
+
*/
|
|
7579
|
+
var RelocateJobStateSchema = _enum([
|
|
7580
|
+
"running",
|
|
7581
|
+
"done",
|
|
7582
|
+
"failed",
|
|
7583
|
+
"cancelled"
|
|
7584
|
+
]);
|
|
7585
|
+
var RelocateJobSchema = object({
|
|
7586
|
+
jobId: string(),
|
|
7587
|
+
state: RelocateJobStateSchema,
|
|
7588
|
+
/** Source location — for media relocation this is informational ('*': rows
|
|
7589
|
+
* move from wherever they are to the target). */
|
|
7590
|
+
fromLocationId: string(),
|
|
7591
|
+
toLocationId: string(),
|
|
7592
|
+
/** Scoped device, or null = every device. */
|
|
7593
|
+
deviceId: number().nullable(),
|
|
7594
|
+
/** What the job moves (owner-addon specific: segments/strips or media). */
|
|
7595
|
+
entities: array(string()),
|
|
7596
|
+
filesMoved: number().int(),
|
|
7597
|
+
bytesMoved: number().int(),
|
|
7598
|
+
/** Total files discovered up front; null while (or when) unknown. */
|
|
7599
|
+
filesTotal: number().int().nullable(),
|
|
7600
|
+
startedAt: number(),
|
|
7601
|
+
finishedAt: number().nullable(),
|
|
7602
|
+
error: string().nullable()
|
|
7603
|
+
});
|
|
7604
|
+
var RelocateFootageInputSchema = object({
|
|
7605
|
+
deviceId: number().optional(),
|
|
7606
|
+
fromLocationId: string(),
|
|
7607
|
+
toLocationId: string(),
|
|
7608
|
+
entities: array(_enum(["segments", "strips"])).optional(),
|
|
7609
|
+
/** Copy throttle in MB/s (default 40) — the drain is a background chore,
|
|
7610
|
+
* never allowed to starve live writers. */
|
|
7611
|
+
throttleMbps: number().min(1).max(1e3).optional()
|
|
7612
|
+
});
|
|
7613
|
+
var RelocateMediaInputSchema = object({
|
|
7614
|
+
deviceId: number().optional(),
|
|
7615
|
+
toLocationId: string(),
|
|
7616
|
+
throttleMbps: number().min(1).max(1e3).optional()
|
|
7617
|
+
});
|
|
7618
|
+
/**
|
|
7636
7619
|
* `StorageLocationType` — an addon-declared id that identifies the *kind* of
|
|
7637
7620
|
* storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
|
|
7638
7621
|
* so the persisted record schema and the consumer-facing cap can both consume it
|
|
@@ -7682,6 +7665,13 @@ var StorageLocationSchema = object({
|
|
|
7682
7665
|
nodeId: string().optional(),
|
|
7683
7666
|
isDefault: boolean().default(false),
|
|
7684
7667
|
isSystem: boolean().default(false),
|
|
7668
|
+
/** COMPUTED at read time by the orchestrator (statfs of the backing volume
|
|
7669
|
+
* for node-local locations it can reach) — never persisted, absent when the
|
|
7670
|
+
* volume is remote/unreachable. The single capacity truth every UI reads. */
|
|
7671
|
+
capacity: object({
|
|
7672
|
+
totalBytes: number(),
|
|
7673
|
+
availableBytes: number()
|
|
7674
|
+
}).nullable().optional(),
|
|
7685
7675
|
createdAt: number(),
|
|
7686
7676
|
updatedAt: number()
|
|
7687
7677
|
});
|
|
@@ -7743,16 +7733,23 @@ var StorageLocationDeclarationSchema = object({
|
|
|
7743
7733
|
* Which node root the seeded `<id>:default` instance is placed under on a
|
|
7744
7734
|
* FRESH install:
|
|
7745
7735
|
* - `'data'` (default) — the node's data dir (`CAMSTACK_DATA` / boot dir),
|
|
7746
|
-
* the appData volume. Right for small/durable data (
|
|
7736
|
+
* the appData volume. Right for small/durable data (logs, models).
|
|
7747
7737
|
* - `'media'` — the dedicated media volume (`CAMSTACK_MEDIA_ROOT`) when that
|
|
7748
7738
|
* env is set, else falls back to the data root. Right for bulky, hot media
|
|
7749
7739
|
* (recordings, event media) that should stay off the appData disk.
|
|
7740
|
+
* - `'backup'` — the dedicated backup volume (`CAMSTACK_BACKUP_ROOT`, default
|
|
7741
|
+
* `/backups` in the image) so archives live on their own mount rather than
|
|
7742
|
+
* filling the appData disk. Falls back to the data root when unset.
|
|
7750
7743
|
*
|
|
7751
7744
|
* Only affects the seeded default's `basePath`; operators can repoint any
|
|
7752
7745
|
* location afterwards, and a `defaultsTo` slot inherits its parent's root
|
|
7753
7746
|
* regardless of this field. Absent (the common case) is treated as `'data'`.
|
|
7754
7747
|
*/
|
|
7755
|
-
defaultRoot: _enum([
|
|
7748
|
+
defaultRoot: _enum([
|
|
7749
|
+
"data",
|
|
7750
|
+
"media",
|
|
7751
|
+
"backup"
|
|
7752
|
+
]).optional()
|
|
7756
7753
|
});
|
|
7757
7754
|
var DecoderStatsSchema = object({
|
|
7758
7755
|
inputFps: number(),
|
|
@@ -8790,7 +8787,8 @@ var NcTaxonomyEntrySchema = object({
|
|
|
8790
8787
|
/** Macro/category parent for grouping ('car' → 'vehicle'); null for a top. */
|
|
8791
8788
|
parentKind: string().nullable()
|
|
8792
8789
|
});
|
|
8793
|
-
|
|
8790
|
+
/** The complete NC picker taxonomy — three grouped buckets. */
|
|
8791
|
+
var NcTaxonomySchema = object({
|
|
8794
8792
|
videoClasses: array(NcTaxonomyEntrySchema),
|
|
8795
8793
|
audioKinds: array(NcTaxonomyEntrySchema),
|
|
8796
8794
|
labels: array(NcTaxonomyEntrySchema)
|
|
@@ -9450,6 +9448,7 @@ var AccessoryKind = {
|
|
|
9450
9448
|
};
|
|
9451
9449
|
AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
|
|
9452
9450
|
DeviceFeature.BatteryOperated;
|
|
9451
|
+
new Set(["devices", "classes"]);
|
|
9453
9452
|
/**
|
|
9454
9453
|
* Shared geometry vocabulary for on-frame shape caps — privacy-mask,
|
|
9455
9454
|
* motion-zones, and the detection zones/lines editor all speak this one
|
|
@@ -9766,12 +9765,60 @@ var NcRuleTargetSchema = object({
|
|
|
9766
9765
|
* - `keyFrame` — the clean scene frame (no subject box).
|
|
9767
9766
|
* - `none` — no attachment.
|
|
9768
9767
|
*/
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9768
|
+
/**
|
|
9769
|
+
* WHAT THE PICTURE SHOWS — chosen explicitly, because the implicit ladders
|
|
9770
|
+
* conflate it with the selection strategy and betray the request: asking for
|
|
9771
|
+
* the clean scene frame on an object-event owner used to start at
|
|
9772
|
+
* `fullFrameBoxed`, i.e. the annotated frame (2026-07-30).
|
|
9773
|
+
* - `cropped` — the subject, tight. What "who is at the door" wants.
|
|
9774
|
+
* - `full` — the clean scene, no annotation. What "what is going on" wants.
|
|
9775
|
+
* - `boxed` — the scene WITH the detection boxes drawn, for verifying what
|
|
9776
|
+
* the pipeline actually saw.
|
|
9777
|
+
*/
|
|
9778
|
+
var NcMediaFrameSchema = _enum([
|
|
9779
|
+
"cropped",
|
|
9780
|
+
"full",
|
|
9781
|
+
"boxed"
|
|
9782
|
+
]);
|
|
9783
|
+
var NcMediaPolicySchema = object({
|
|
9784
|
+
attach: _enum([
|
|
9785
|
+
"best",
|
|
9786
|
+
"best-matching",
|
|
9787
|
+
"keyFrame",
|
|
9788
|
+
"none"
|
|
9789
|
+
]).default("best"),
|
|
9790
|
+
/** What the still shows. Absent = `cropped` (the historical `best` shape). */
|
|
9791
|
+
frame: NcMediaFrameSchema.optional(),
|
|
9792
|
+
/** Crop the attached still to the rule's condition-zone bbox (padded) —
|
|
9793
|
+
* "show me the ZONE", not the whole scene or the subject crop. */
|
|
9794
|
+
zoneCrop: boolean().optional(),
|
|
9795
|
+
/**
|
|
9796
|
+
* Also attach a short GIF cut from the stream broker's clip ring around the
|
|
9797
|
+
* event — NOT from the recording, so the camera does not have to be
|
|
9798
|
+
* recording, and the window sits AROUND the moment instead of a segment
|
|
9799
|
+
* behind it. Fail-closed: a window the ring does not cover contributes no
|
|
9800
|
+
* gif, never a failed notification.
|
|
9801
|
+
*/
|
|
9802
|
+
gif: boolean().optional(),
|
|
9803
|
+
/**
|
|
9804
|
+
* Also attach a short MP4 CLIP of the same cut. Same source and the same
|
|
9805
|
+
* fail-closed rule as `gif`. Prefer it where the backend takes video
|
|
9806
|
+
* (telegram, discord, zentik, webhook); backends that do not are handled by
|
|
9807
|
+
* the degrade engine, which drops the video and keeps the still.
|
|
9808
|
+
*/
|
|
9809
|
+
clip: boolean().optional(),
|
|
9810
|
+
/** Seconds of footage BEFORE / AFTER the event instant. Defaults 3 / 7. */
|
|
9811
|
+
clipPreRollSec: number().int().min(0).max(30).optional(),
|
|
9812
|
+
clipPostRollSec: number().int().min(0).max(30).optional(),
|
|
9813
|
+
/**
|
|
9814
|
+
* Which stream profile the footage is cut from. Absent = the CHEAPEST
|
|
9815
|
+
* assigned profile: a notification is watched on a phone, so the 4K
|
|
9816
|
+
* rendition would burn CPU to produce a file the client downscales anyway.
|
|
9817
|
+
* A profile that is not assigned falls back to the cheapest, and the render
|
|
9818
|
+
* reports which one actually ran.
|
|
9819
|
+
*/
|
|
9820
|
+
profile: CamProfileSchema.optional()
|
|
9821
|
+
});
|
|
9775
9822
|
/** Throttle — cooldown survives restarts (rebuilt from the outbox on boot). */
|
|
9776
9823
|
var NcThrottleSchema = object({
|
|
9777
9824
|
cooldownSec: number().int().min(0).max(86400).default(60),
|
|
@@ -9785,7 +9832,19 @@ var NcRuleInputSchema = object({
|
|
|
9785
9832
|
delivery: NcDeliverySchema,
|
|
9786
9833
|
conditions: NcConditionsSchema.default({}),
|
|
9787
9834
|
schedule: NcScheduleSchema.optional(),
|
|
9788
|
-
|
|
9835
|
+
/** May be empty when `targetUsers` addresses at least one user — the
|
|
9836
|
+
* "at least one addressee" invariant is enforced by the provider, because
|
|
9837
|
+
* a cross-field refine here would break `NcRulePatchSchema.partial()`. */
|
|
9838
|
+
targets: array(NcRuleTargetSchema),
|
|
9839
|
+
/**
|
|
9840
|
+
* USERS this rule addresses in addition to `targets` (Phase 4). At fire
|
|
9841
|
+
* time each user fans out to the personal targets they own
|
|
9842
|
+
* (`config.ownerUserId`), filtered by that user's `allowedDevices` for the
|
|
9843
|
+
* firing camera — a user is never notified about a device they cannot open.
|
|
9844
|
+
* Per-user opt-outs (`disabledTargetIds`) still apply to the fanned-out
|
|
9845
|
+
* targets.
|
|
9846
|
+
*/
|
|
9847
|
+
targetUsers: array(string()).optional(),
|
|
9789
9848
|
media: NcMediaPolicySchema.default({ attach: "best" }),
|
|
9790
9849
|
throttle: NcThrottleSchema.default({
|
|
9791
9850
|
cooldownSec: 60,
|
|
@@ -9998,7 +10057,10 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
|
|
|
9998
10057
|
}), object({ results: array(NcTestResultSchema) }), {
|
|
9999
10058
|
kind: "mutation",
|
|
10000
10059
|
auth: "admin"
|
|
10001
|
-
}), method(object({}), object({
|
|
10060
|
+
}), method(object({}), object({
|
|
10061
|
+
catalog: array(NcConditionDescriptorSchema),
|
|
10062
|
+
taxonomy: NcTaxonomySchema.optional()
|
|
10063
|
+
})), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" });
|
|
10002
10064
|
/**
|
|
10003
10065
|
* TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
|
|
10004
10066
|
*
|
|
@@ -10745,6 +10807,48 @@ var streamBrokerCapability = {
|
|
|
10745
10807
|
kind: "mutation",
|
|
10746
10808
|
auth: "admin"
|
|
10747
10809
|
}),
|
|
10810
|
+
/**
|
|
10811
|
+
* Render a short GIF or MP4 from the broker's PRE-BUFFER around an instant.
|
|
10812
|
+
*
|
|
10813
|
+
* The pre-buffer is the only source that already holds the seconds BEFORE
|
|
10814
|
+
* an event (it is what it exists for — "when an addon needs the last N
|
|
10815
|
+
* seconds of video for clip creation"). Cutting from recorded segments
|
|
10816
|
+
* cannot: only finalized segments are addressable and the one covering the
|
|
10817
|
+
* moment is still being written, so a segment-sourced clip is either
|
|
10818
|
+
* delayed or shows the approach instead of the event (operator,
|
|
10819
|
+
* 2026-07-30). It also means a camera does not have to be RECORDING to
|
|
10820
|
+
* produce a notification clip.
|
|
10821
|
+
*
|
|
10822
|
+
* Returns muxed BYTES, never packets: the frame plane never crosses a
|
|
10823
|
+
* process boundary at frame rate (D9/D18) — this is one compressed,
|
|
10824
|
+
* on-demand payload per notification.
|
|
10825
|
+
*
|
|
10826
|
+
* Waits up to `postRollSec` for the tail to accumulate, so the window is
|
|
10827
|
+
* CENTRED on `aroundMs`.
|
|
10828
|
+
*/
|
|
10829
|
+
renderPreBufferClip: method(object({
|
|
10830
|
+
deviceId: number(),
|
|
10831
|
+
/** Absent = the LOWEST assigned profile — a notification attachment is
|
|
10832
|
+
* watched on a phone, and the cheap rendition is the right default. */
|
|
10833
|
+
profile: CamProfileSchema.optional(),
|
|
10834
|
+
aroundMs: number(),
|
|
10835
|
+
preRollSec: number().min(0).max(20).default(3),
|
|
10836
|
+
postRollSec: number().min(0).max(20).default(5),
|
|
10837
|
+
format: _enum(["gif", "mp4"]).default("gif"),
|
|
10838
|
+
maxWidth: number().int().min(120).max(1920).default(480),
|
|
10839
|
+
/** GIF only — MP4 keeps the source cadence. */
|
|
10840
|
+
fps: number().int().min(1).max(15).default(5)
|
|
10841
|
+
}), object({
|
|
10842
|
+
base64: string(),
|
|
10843
|
+
mime: string(),
|
|
10844
|
+
bytes: number().int(),
|
|
10845
|
+
/** The profile actually rendered (what the default resolved to). */
|
|
10846
|
+
profile: CamProfileSchema,
|
|
10847
|
+
durationMs: number()
|
|
10848
|
+
}), {
|
|
10849
|
+
kind: "mutation",
|
|
10850
|
+
auth: "admin"
|
|
10851
|
+
}),
|
|
10748
10852
|
listAllCameraStreams: method(_void(), array(CameraStreamSchema).readonly()),
|
|
10749
10853
|
listAllProfileSlots: method(_void(), array(ProfileSlotSchema).readonly()),
|
|
10750
10854
|
getBrokerStats: method(object({ brokerId: string() }), BrokerStatsSchema),
|
|
@@ -15691,11 +15795,53 @@ var LocationStatSchema = object({
|
|
|
15691
15795
|
fileCount: number(),
|
|
15692
15796
|
present: boolean()
|
|
15693
15797
|
});
|
|
15798
|
+
/**
|
|
15799
|
+
* A backup schedule — the N:M "entry" that binds one cron cadence to a
|
|
15800
|
+
* SET of destination locations. Supersedes the per-location cron on
|
|
15801
|
+
* `BackupDestinationPolicy`: an operator creates a schedule, picks the
|
|
15802
|
+
* `backups` locations it should write to, and the orchestrator fans a
|
|
15803
|
+
* single archive out to all of them when the cron fires.
|
|
15804
|
+
*
|
|
15805
|
+
* `retentionCount` is per-schedule (D-decision 2026-07-28): every
|
|
15806
|
+
* location targeted by this schedule keeps this many archives from
|
|
15807
|
+
* this schedule's runs.
|
|
15808
|
+
*
|
|
15809
|
+
* `dataSources` optionally narrows which top-level state locations
|
|
15810
|
+
* (db, addons, tls, …) are archived; omitted = the orchestrator's
|
|
15811
|
+
* default full set.
|
|
15812
|
+
*/
|
|
15813
|
+
var BackupScheduleSchema = object({
|
|
15814
|
+
/** Stable id. Generated by the orchestrator on first upsert if absent. */
|
|
15815
|
+
id: string(),
|
|
15816
|
+
/** Operator-facing display name. */
|
|
15817
|
+
label: string(),
|
|
15818
|
+
/** 5-field POSIX cron. Empty = disabled cadence (kept for editing). */
|
|
15819
|
+
cron: string(),
|
|
15820
|
+
/** Master on/off toggle for the whole schedule. */
|
|
15821
|
+
enabled: boolean(),
|
|
15822
|
+
/** `backups`-location ids this schedule writes to (fan-out set). */
|
|
15823
|
+
locationIds: array(string()).readonly(),
|
|
15824
|
+
/** Archives kept per targeted location for this schedule. */
|
|
15825
|
+
retentionCount: number().int().min(1).max(1e3),
|
|
15826
|
+
/** Optional subset of source locations to include; omitted = all. */
|
|
15827
|
+
dataSources: array(string()).readonly().optional(),
|
|
15828
|
+
/** ms-epoch of last successful run. */
|
|
15829
|
+
lastRunAt: number().optional(),
|
|
15830
|
+
/** ms-epoch of next computed firing (read-only, filled on list). */
|
|
15831
|
+
nextRunAt: number().optional()
|
|
15832
|
+
});
|
|
15694
15833
|
method(_void(), array(BackupDestinationInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
15695
15834
|
/** Subset of registered `backup-destination` addon ids to write to. */
|
|
15696
15835
|
destinations: array(string()).optional(),
|
|
15697
15836
|
locations: array(string()).optional(),
|
|
15698
|
-
label: string().optional()
|
|
15837
|
+
label: string().optional(),
|
|
15838
|
+
/**
|
|
15839
|
+
* Per-run retention override applied to every targeted
|
|
15840
|
+
* destination. Used by schedule-driven runs (per-entry
|
|
15841
|
+
* retention). Omitted = each destination's own policy
|
|
15842
|
+
* retention (manual runs).
|
|
15843
|
+
*/
|
|
15844
|
+
retentionCount: number().int().min(1).max(1e3).optional()
|
|
15699
15845
|
}).optional(), array(BackupEntrySchema).readonly(), {
|
|
15700
15846
|
kind: "mutation",
|
|
15701
15847
|
auth: "admin"
|
|
@@ -15744,7 +15890,21 @@ method(_void(), array(BackupDestinationInfoSchema).readonly(), { auth: "admin" }
|
|
|
15744
15890
|
ok: boolean(),
|
|
15745
15891
|
error: string().optional(),
|
|
15746
15892
|
nextRuns: array(number()).readonly()
|
|
15747
|
-
}))
|
|
15893
|
+
})), method(_void(), array(BackupScheduleSchema).readonly(), { auth: "admin" }), method(object({
|
|
15894
|
+
id: string().optional(),
|
|
15895
|
+
label: string(),
|
|
15896
|
+
cron: string(),
|
|
15897
|
+
enabled: boolean(),
|
|
15898
|
+
locationIds: array(string()).readonly(),
|
|
15899
|
+
retentionCount: number().int().min(1).max(1e3),
|
|
15900
|
+
dataSources: array(string()).readonly().optional()
|
|
15901
|
+
}), BackupScheduleSchema, {
|
|
15902
|
+
kind: "mutation",
|
|
15903
|
+
auth: "admin"
|
|
15904
|
+
}), method(object({ id: string() }), _void(), {
|
|
15905
|
+
kind: "mutation",
|
|
15906
|
+
auth: "admin"
|
|
15907
|
+
});
|
|
15748
15908
|
/**
|
|
15749
15909
|
* `broker` — unified pub/sub broker registry, system-scoped collection.
|
|
15750
15910
|
*
|
|
@@ -17863,6 +18023,36 @@ var TargetKindSchema = object({
|
|
|
17863
18023
|
icon: string(),
|
|
17864
18024
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
17865
18025
|
addonId: string(),
|
|
18026
|
+
/**
|
|
18027
|
+
* URL of the kind's bundled BRAND icon, served by the providing addon over
|
|
18028
|
+
* its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
|
|
18029
|
+
* when the addon bundles no icon for that kind — the client then falls back
|
|
18030
|
+
* to a neutral glyph rather than rendering the raw `icon` NAME as text.
|
|
18031
|
+
*
|
|
18032
|
+
* Root-relative on purpose: it resolves against whatever origin serves a web
|
|
18033
|
+
* client, and a native client joins it onto its own hub base.
|
|
18034
|
+
*
|
|
18035
|
+
* DECLARED here deliberately. It used to travel as an undeclared passthrough
|
|
18036
|
+
* field that survived only because the runtime cap-router forwards provider
|
|
18037
|
+
* output verbatim — so every consumer had to re-declare it by hand to stop
|
|
18038
|
+
* its own Zod parse from stripping it, and the whole arrangement would have
|
|
18039
|
+
* broken silently the moment output validation was tightened anywhere.
|
|
18040
|
+
*/
|
|
18041
|
+
iconUrl: string().optional(),
|
|
18042
|
+
/**
|
|
18043
|
+
* Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
|
|
18044
|
+
*
|
|
18045
|
+
* The server knows this and therefore says it, because the client cannot
|
|
18046
|
+
* safely guess: a React-Native client renders SVG and raster through two
|
|
18047
|
+
* DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
|
|
18048
|
+
* not decode SVG on iOS/Android), so without this it silently fell back to a
|
|
18049
|
+
* placeholder glyph for every vector icon while the web build looked fine.
|
|
18050
|
+
*
|
|
18051
|
+
* Absent when {@link iconUrl} is absent, or for a legacy provider that has
|
|
18052
|
+
* not been updated — a client that cannot determine the type should prefer
|
|
18053
|
+
* its raster path, which is the safe default for an unknown image.
|
|
18054
|
+
*/
|
|
18055
|
+
iconMediaType: string().optional(),
|
|
17866
18056
|
configSchema: ConfigSchemaPassthrough,
|
|
17867
18057
|
supportsDiscovery: boolean(),
|
|
17868
18058
|
caps: TargetKindCapsSchema
|
|
@@ -18594,6 +18784,15 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
18594
18784
|
}), method(object({ deviceId: number() }), EventPruneCountsSchema, {
|
|
18595
18785
|
kind: "mutation",
|
|
18596
18786
|
auth: "admin"
|
|
18787
|
+
}), method(RelocateMediaInputSchema, object({ jobId: string() }), {
|
|
18788
|
+
kind: "mutation",
|
|
18789
|
+
auth: "admin"
|
|
18790
|
+
}), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
18791
|
+
kind: "query",
|
|
18792
|
+
auth: "admin"
|
|
18793
|
+
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
18794
|
+
kind: "mutation",
|
|
18795
|
+
auth: "admin"
|
|
18597
18796
|
}), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
|
|
18598
18797
|
kind: "query",
|
|
18599
18798
|
auth: "admin"
|
|
@@ -21168,6 +21367,17 @@ var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
|
|
|
21168
21367
|
*/
|
|
21169
21368
|
priority: number()
|
|
21170
21369
|
})).readonly() });
|
|
21370
|
+
/**
|
|
21371
|
+
* The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
|
|
21372
|
+
* AUTO (resolved from the candidate ranking at send time); `resolved` reports
|
|
21373
|
+
* what AUTO currently picks, so the UI can show the effective value either way.
|
|
21374
|
+
*/
|
|
21375
|
+
var NotificationEndpointSchema = object({
|
|
21376
|
+
/** The operator's explicit choice, or null for AUTO. */
|
|
21377
|
+
baseUrl: string().nullable(),
|
|
21378
|
+
/** What the ranking currently resolves to (null when nothing is reachable). */
|
|
21379
|
+
resolved: string().nullable()
|
|
21380
|
+
});
|
|
21171
21381
|
var AllowedAddressesSchema = object({
|
|
21172
21382
|
/**
|
|
21173
21383
|
* Allowlist of interface addresses operators have explicitly opted
|
|
@@ -21190,7 +21400,7 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
21190
21400
|
* to avoid mixed-content blocks in the browser. The public
|
|
21191
21401
|
* tunnel always emits `https://` regardless. */
|
|
21192
21402
|
scheme: _enum(["http", "https"]).optional()
|
|
21193
|
-
}), GetConnectionEndpointsResultSchema), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" });
|
|
21403
|
+
}), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" });
|
|
21194
21404
|
/**
|
|
21195
21405
|
* mesh-network — collection cap for mesh-VPN providers.
|
|
21196
21406
|
*
|
|
@@ -21989,7 +22199,12 @@ var RecordingDeviceUsageSchema = object({
|
|
|
21989
22199
|
var RecordingLocationUsageSchema = object({
|
|
21990
22200
|
/** StorageLocation id; null for the legacy/degraded single-root fallback. */
|
|
21991
22201
|
locationId: string().nullable(),
|
|
21992
|
-
/**
|
|
22202
|
+
/** Every location id sharing this row's PHYSICAL volume (aliases). One row
|
|
22203
|
+
* is emitted per physical disk (2026-07-29): two locations on one root
|
|
22204
|
+
* previously rendered as two identical "disks" with a nonsensical used
|
|
22205
|
+
* split — hydrate attribution across aliases is arbitrary by nature. */
|
|
22206
|
+
locationIds: array(string()).optional(),
|
|
22207
|
+
/** Bytes of recordings stored on this PHYSICAL volume (all aliases). */
|
|
21993
22208
|
usedBytes: number(),
|
|
21994
22209
|
/** Free bytes on the location's volume; null when capacity is unknown (remote). */
|
|
21995
22210
|
availableBytes: number().nullable(),
|
|
@@ -22145,6 +22360,77 @@ var recordingCapability = {
|
|
|
22145
22360
|
listOpsLog: method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
|
|
22146
22361
|
kind: "query",
|
|
22147
22362
|
auth: "admin"
|
|
22363
|
+
}),
|
|
22364
|
+
/**
|
|
22365
|
+
* Move footage (segments and/or strips) from one recordings location to
|
|
22366
|
+
* another — the drain workflow's mover (entity-routing spec Phase 4).
|
|
22367
|
+
* Throttled copy → size-verify → delete source → index refresh; resumable
|
|
22368
|
+
* by construction (copy-if-absent). Single-flight: one job at a time.
|
|
22369
|
+
*/
|
|
22370
|
+
/**
|
|
22371
|
+
* Render a short GIF from recorded footage around `aroundMs` (low profile,
|
|
22372
|
+
* palette-free ffmpeg gif). Synchronous — the window is a few seconds of
|
|
22373
|
+
* `low`, rendering in ~1-2s. Built for notification attachments: the
|
|
22374
|
+
* caller gets bytes, nothing persists. Fails when no footage covers the
|
|
22375
|
+
* window (camera not recording → caller skips the attachment).
|
|
22376
|
+
*/
|
|
22377
|
+
renderGif: method(object({
|
|
22378
|
+
deviceId: number(),
|
|
22379
|
+
aroundMs: number(),
|
|
22380
|
+
preRollSec: number().min(0).max(30).default(2),
|
|
22381
|
+
postRollSec: number().min(0).max(30).default(5),
|
|
22382
|
+
maxWidth: number().int().min(120).max(1280).default(480),
|
|
22383
|
+
fps: number().int().min(1).max(15).default(5)
|
|
22384
|
+
}), object({
|
|
22385
|
+
gifBase64: string(),
|
|
22386
|
+
fromMs: number(),
|
|
22387
|
+
toMs: number()
|
|
22388
|
+
}), {
|
|
22389
|
+
kind: "mutation",
|
|
22390
|
+
auth: "admin"
|
|
22391
|
+
}),
|
|
22392
|
+
/**
|
|
22393
|
+
* Render a short MP4 clip from recorded footage around `aroundMs` — the
|
|
22394
|
+
* "send a clip, not a gif" notification attachment. Same window, source
|
|
22395
|
+
* and fail-closed contract as {@link renderGif}: no footage covering the
|
|
22396
|
+
* window ⇒ it throws and the caller ships no attachment. Re-encoded (never
|
|
22397
|
+
* stream-copied) so a concat across segments with different encoder
|
|
22398
|
+
* parameters still plays. Silent by contract.
|
|
22399
|
+
*
|
|
22400
|
+
* The returned window is SEGMENT-GRANULAR: the source playlist is built
|
|
22401
|
+
* from whole segments, so the clip starts at or before `fromMs` and ends
|
|
22402
|
+
* at or after `toMs` — overshoot bounded by one segment per side (an 8 s
|
|
22403
|
+
* ask over 4 s segments measured 12 s). `fromMs`/`toMs` report the window
|
|
22404
|
+
* ASKED for, not the encoded duration.
|
|
22405
|
+
*/
|
|
22406
|
+
renderClip: method(object({
|
|
22407
|
+
deviceId: number(),
|
|
22408
|
+
aroundMs: number(),
|
|
22409
|
+
preRollSec: number().min(0).max(30).default(3),
|
|
22410
|
+
postRollSec: number().min(0).max(30).default(7),
|
|
22411
|
+
maxWidth: number().int().min(160).max(1920).default(640)
|
|
22412
|
+
}), object({
|
|
22413
|
+
clipBase64: string(),
|
|
22414
|
+
mime: string(),
|
|
22415
|
+
fromMs: number(),
|
|
22416
|
+
toMs: number(),
|
|
22417
|
+
bytes: number().int()
|
|
22418
|
+
}), {
|
|
22419
|
+
kind: "mutation",
|
|
22420
|
+
auth: "admin"
|
|
22421
|
+
}),
|
|
22422
|
+
relocateFootage: method(RelocateFootageInputSchema, object({ jobId: string() }), {
|
|
22423
|
+
kind: "mutation",
|
|
22424
|
+
auth: "admin"
|
|
22425
|
+
}),
|
|
22426
|
+
/** Every known relocate job (in-RAM, newest-first). */
|
|
22427
|
+
getRelocateStatus: method(object({}), array(RelocateJobSchema).readonly(), {
|
|
22428
|
+
kind: "query",
|
|
22429
|
+
auth: "admin"
|
|
22430
|
+
}),
|
|
22431
|
+
cancelRelocate: method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
22432
|
+
kind: "mutation",
|
|
22433
|
+
auth: "admin"
|
|
22148
22434
|
})
|
|
22149
22435
|
}
|
|
22150
22436
|
};
|
|
@@ -23077,6 +23363,12 @@ Object.freeze({
|
|
|
23077
23363
|
addonId: null,
|
|
23078
23364
|
access: "delete"
|
|
23079
23365
|
},
|
|
23366
|
+
"backup.deleteSchedule": {
|
|
23367
|
+
capName: "backup",
|
|
23368
|
+
capScope: "system",
|
|
23369
|
+
addonId: null,
|
|
23370
|
+
access: "delete"
|
|
23371
|
+
},
|
|
23080
23372
|
"backup.getEntries": {
|
|
23081
23373
|
capName: "backup",
|
|
23082
23374
|
capScope: "system",
|
|
@@ -23107,6 +23399,12 @@ Object.freeze({
|
|
|
23107
23399
|
addonId: null,
|
|
23108
23400
|
access: "view"
|
|
23109
23401
|
},
|
|
23402
|
+
"backup.listSchedules": {
|
|
23403
|
+
capName: "backup",
|
|
23404
|
+
capScope: "system",
|
|
23405
|
+
addonId: null,
|
|
23406
|
+
access: "view"
|
|
23407
|
+
},
|
|
23110
23408
|
"backup.previewSchedule": {
|
|
23111
23409
|
capName: "backup",
|
|
23112
23410
|
capScope: "system",
|
|
@@ -23131,6 +23429,12 @@ Object.freeze({
|
|
|
23131
23429
|
addonId: null,
|
|
23132
23430
|
access: "create"
|
|
23133
23431
|
},
|
|
23432
|
+
"backup.upsertSchedule": {
|
|
23433
|
+
capName: "backup",
|
|
23434
|
+
capScope: "system",
|
|
23435
|
+
addonId: null,
|
|
23436
|
+
access: "create"
|
|
23437
|
+
},
|
|
23134
23438
|
"battery.wakeForStream": {
|
|
23135
23439
|
capName: "battery",
|
|
23136
23440
|
capScope: "device",
|
|
@@ -24631,6 +24935,12 @@ Object.freeze({
|
|
|
24631
24935
|
addonId: null,
|
|
24632
24936
|
access: "view"
|
|
24633
24937
|
},
|
|
24938
|
+
"localNetwork.getNotificationEndpoint": {
|
|
24939
|
+
capName: "local-network",
|
|
24940
|
+
capScope: "system",
|
|
24941
|
+
addonId: null,
|
|
24942
|
+
access: "view"
|
|
24943
|
+
},
|
|
24634
24944
|
"localNetwork.getPreferred": {
|
|
24635
24945
|
capName: "local-network",
|
|
24636
24946
|
capScope: "system",
|
|
@@ -24655,6 +24965,12 @@ Object.freeze({
|
|
|
24655
24965
|
addonId: null,
|
|
24656
24966
|
access: "create"
|
|
24657
24967
|
},
|
|
24968
|
+
"localNetwork.setNotificationEndpoint": {
|
|
24969
|
+
capName: "local-network",
|
|
24970
|
+
capScope: "system",
|
|
24971
|
+
addonId: null,
|
|
24972
|
+
access: "create"
|
|
24973
|
+
},
|
|
24658
24974
|
"lockControl.lock": {
|
|
24659
24975
|
capName: "lock-control",
|
|
24660
24976
|
capScope: "device",
|
|
@@ -25297,6 +25613,12 @@ Object.freeze({
|
|
|
25297
25613
|
addonId: null,
|
|
25298
25614
|
access: "create"
|
|
25299
25615
|
},
|
|
25616
|
+
"pipelineAnalytics.cancelMediaRelocate": {
|
|
25617
|
+
capName: "pipeline-analytics",
|
|
25618
|
+
capScope: "device",
|
|
25619
|
+
addonId: null,
|
|
25620
|
+
access: "create"
|
|
25621
|
+
},
|
|
25300
25622
|
"pipelineAnalytics.clearTracks": {
|
|
25301
25623
|
capName: "pipeline-analytics",
|
|
25302
25624
|
capScope: "device",
|
|
@@ -25351,6 +25673,12 @@ Object.freeze({
|
|
|
25351
25673
|
addonId: null,
|
|
25352
25674
|
access: "view"
|
|
25353
25675
|
},
|
|
25676
|
+
"pipelineAnalytics.getMediaRelocateStatus": {
|
|
25677
|
+
capName: "pipeline-analytics",
|
|
25678
|
+
capScope: "device",
|
|
25679
|
+
addonId: null,
|
|
25680
|
+
access: "view"
|
|
25681
|
+
},
|
|
25354
25682
|
"pipelineAnalytics.getMotionEvents": {
|
|
25355
25683
|
capName: "pipeline-analytics",
|
|
25356
25684
|
capScope: "device",
|
|
@@ -25423,6 +25751,12 @@ Object.freeze({
|
|
|
25423
25751
|
addonId: null,
|
|
25424
25752
|
access: "create"
|
|
25425
25753
|
},
|
|
25754
|
+
"pipelineAnalytics.relocateMedia": {
|
|
25755
|
+
capName: "pipeline-analytics",
|
|
25756
|
+
capScope: "device",
|
|
25757
|
+
addonId: null,
|
|
25758
|
+
access: "create"
|
|
25759
|
+
},
|
|
25426
25760
|
"pipelineAnalytics.searchObjectEvents": {
|
|
25427
25761
|
capName: "pipeline-analytics",
|
|
25428
25762
|
capScope: "device",
|
|
@@ -26185,6 +26519,12 @@ Object.freeze({
|
|
|
26185
26519
|
addonId: null,
|
|
26186
26520
|
access: "create"
|
|
26187
26521
|
},
|
|
26522
|
+
"recording.cancelRelocate": {
|
|
26523
|
+
capName: "recording",
|
|
26524
|
+
capScope: "system",
|
|
26525
|
+
addonId: null,
|
|
26526
|
+
access: "create"
|
|
26527
|
+
},
|
|
26188
26528
|
"recording.deleteFootprint": {
|
|
26189
26529
|
capName: "recording",
|
|
26190
26530
|
capScope: "system",
|
|
@@ -26215,6 +26555,12 @@ Object.freeze({
|
|
|
26215
26555
|
addonId: null,
|
|
26216
26556
|
access: "view"
|
|
26217
26557
|
},
|
|
26558
|
+
"recording.getRelocateStatus": {
|
|
26559
|
+
capName: "recording",
|
|
26560
|
+
capScope: "system",
|
|
26561
|
+
addonId: null,
|
|
26562
|
+
access: "view"
|
|
26563
|
+
},
|
|
26218
26564
|
"recording.getStorageUsage": {
|
|
26219
26565
|
capName: "recording",
|
|
26220
26566
|
capScope: "system",
|
|
@@ -26245,6 +26591,24 @@ Object.freeze({
|
|
|
26245
26591
|
addonId: null,
|
|
26246
26592
|
access: "view"
|
|
26247
26593
|
},
|
|
26594
|
+
"recording.relocateFootage": {
|
|
26595
|
+
capName: "recording",
|
|
26596
|
+
capScope: "system",
|
|
26597
|
+
addonId: null,
|
|
26598
|
+
access: "create"
|
|
26599
|
+
},
|
|
26600
|
+
"recording.renderClip": {
|
|
26601
|
+
capName: "recording",
|
|
26602
|
+
capScope: "system",
|
|
26603
|
+
addonId: null,
|
|
26604
|
+
access: "create"
|
|
26605
|
+
},
|
|
26606
|
+
"recording.renderGif": {
|
|
26607
|
+
capName: "recording",
|
|
26608
|
+
capScope: "system",
|
|
26609
|
+
addonId: null,
|
|
26610
|
+
access: "create"
|
|
26611
|
+
},
|
|
26248
26612
|
"recording.rescanStorage": {
|
|
26249
26613
|
capName: "recording",
|
|
26250
26614
|
capScope: "system",
|
|
@@ -26839,6 +27203,12 @@ Object.freeze({
|
|
|
26839
27203
|
addonId: null,
|
|
26840
27204
|
access: "create"
|
|
26841
27205
|
},
|
|
27206
|
+
"streamBroker.renderPreBufferClip": {
|
|
27207
|
+
capName: "stream-broker",
|
|
27208
|
+
capScope: "system",
|
|
27209
|
+
addonId: null,
|
|
27210
|
+
access: "create"
|
|
27211
|
+
},
|
|
26842
27212
|
"streamBroker.restartProfile": {
|
|
26843
27213
|
capName: "stream-broker",
|
|
26844
27214
|
capScope: "system",
|
|
@@ -27957,6 +28327,12 @@ Object.defineProperty(exports, "defineCustomActions", {
|
|
|
27957
28327
|
return defineCustomActions;
|
|
27958
28328
|
}
|
|
27959
28329
|
});
|
|
28330
|
+
Object.defineProperty(exports, "deriveRecordingMode", {
|
|
28331
|
+
enumerable: true,
|
|
28332
|
+
get: function() {
|
|
28333
|
+
return deriveRecordingMode;
|
|
28334
|
+
}
|
|
28335
|
+
});
|
|
27960
28336
|
Object.defineProperty(exports, "detectionPipelineCapability", {
|
|
27961
28337
|
enumerable: true,
|
|
27962
28338
|
get: function() {
|
|
@@ -28041,12 +28417,6 @@ Object.defineProperty(exports, "maskUrlCredentials", {
|
|
|
28041
28417
|
return maskUrlCredentials;
|
|
28042
28418
|
}
|
|
28043
28419
|
});
|
|
28044
|
-
Object.defineProperty(exports, "migrateConfigToBands", {
|
|
28045
|
-
enumerable: true,
|
|
28046
|
-
get: function() {
|
|
28047
|
-
return migrateConfigToBands;
|
|
28048
|
-
}
|
|
28049
|
-
});
|
|
28050
28420
|
Object.defineProperty(exports, "motionDetectionCapability", {
|
|
28051
28421
|
enumerable: true,
|
|
28052
28422
|
get: function() {
|