@camstack/system 1.2.178 → 1.2.180
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/addon-runner.js +1 -1
- package/dist/addon-runner.mjs +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +56 -10
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +657 -62
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +658 -63
- package/dist/builtins/backup-orchestrator/run-queue.d.ts +103 -0
- package/dist/builtins/backup-orchestrator/staging-paths.d.ts +36 -0
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
- package/dist/builtins/device-manager/device-linked-devices.d.ts +8 -0
- package/dist/builtins/device-manager/device-manager.addon.js +148 -7
- package/dist/builtins/device-manager/device-manager.addon.mjs +148 -7
- package/dist/builtins/device-manager/device-meta-actions.d.ts +1 -1
- package/dist/builtins/device-manager/migrate-device.d.ts +33 -0
- package/dist/builtins/device-manager/migrate-hardware-state.d.ts +24 -0
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-backup/system-backup.service.d.ts +55 -2
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{dist-DMJ-DM7n.mjs → dist-DqNTelT4.mjs} +207 -5
- package/dist/{dist-C0IXtI_E.js → dist-TEwQZL3g.js} +207 -5
- package/dist/index.js +530 -2
- package/dist/index.mjs +533 -5
- package/dist/kernel/addon-installer.d.ts +69 -0
- package/dist/kernel/deps/closure-dedupe.d.ts +105 -0
- package/dist/{manifest-system-deps-D6ncx0jA.js → manifest-system-deps-BVIr3MYJ.js} +1 -1
- package/dist/{manifest-system-deps-BMD6YE4f.mjs → manifest-system-deps-Ci-jhrxY.mjs} +1 -1
- package/dist/{retired-settings-keys-D66kBk8P.mjs → retired-settings-keys-CeSITpIX.mjs} +1 -1
- package/dist/{retired-settings-keys-eLv_RCfp.js → retired-settings-keys-DauPtbFR.js} +1 -1
- package/package.json +1 -1
|
@@ -7164,6 +7164,89 @@ var LocationStatSchema = z.object({
|
|
|
7164
7164
|
fileCount: z.number(),
|
|
7165
7165
|
present: z.boolean()
|
|
7166
7166
|
});
|
|
7167
|
+
/** Lifecycle of a backup run. Terminal states: succeeded / failed / cancelled. */
|
|
7168
|
+
var BackupRunStateSchema = z.enum([
|
|
7169
|
+
"queued",
|
|
7170
|
+
"running",
|
|
7171
|
+
"succeeded",
|
|
7172
|
+
"failed",
|
|
7173
|
+
"cancelled"
|
|
7174
|
+
]);
|
|
7175
|
+
/**
|
|
7176
|
+
* Where a running backup currently is. `queued` before it starts,
|
|
7177
|
+
* `building` while the tar.gz is being staged, `uploading` during the
|
|
7178
|
+
* per-destination fan-out, `done` once terminal.
|
|
7179
|
+
*/
|
|
7180
|
+
var BackupRunPhaseSchema = z.enum([
|
|
7181
|
+
"queued",
|
|
7182
|
+
"building",
|
|
7183
|
+
"uploading",
|
|
7184
|
+
"done"
|
|
7185
|
+
]);
|
|
7186
|
+
/**
|
|
7187
|
+
* Observable state of one backup run — readable WHILE it runs via
|
|
7188
|
+
* `backup.listRuns`. This is what makes the execution queue and
|
|
7189
|
+
* `backup.cancel` usable: the 2026-09-04 incident (two concurrent
|
|
7190
|
+
* multi-GB builds, staging 5.1 GB → 18 GB, load 62) was only
|
|
7191
|
+
* diagnosable with `du` because nothing reported that runs existed or
|
|
7192
|
+
* how large the staged archive had grown.
|
|
7193
|
+
*/
|
|
7194
|
+
var BackupRunSchema = z.object({
|
|
7195
|
+
/** Stable run id — the handle `backup.cancel` takes. */
|
|
7196
|
+
id: z.string(),
|
|
7197
|
+
state: BackupRunStateSchema,
|
|
7198
|
+
phase: BackupRunPhaseSchema,
|
|
7199
|
+
/**
|
|
7200
|
+
* Resolved destination location ids. Empty while queued (targets are
|
|
7201
|
+
* resolved when the run starts, against the then-current policies).
|
|
7202
|
+
*/
|
|
7203
|
+
destinationIds: z.array(z.string()).readonly(),
|
|
7204
|
+
label: z.string().optional(),
|
|
7205
|
+
/** ms-epoch when the run was submitted (trigger call / schedule fire). */
|
|
7206
|
+
requestedAt: z.number(),
|
|
7207
|
+
/** ms-epoch when the run left the queue and started building. */
|
|
7208
|
+
startedAt: z.number().optional(),
|
|
7209
|
+
/** ms-epoch when the run reached a terminal state. */
|
|
7210
|
+
finishedAt: z.number().optional(),
|
|
7211
|
+
/** Compressed bytes of the staging archive written so far. */
|
|
7212
|
+
stagedBytes: z.number(),
|
|
7213
|
+
/** Final staged archive size, once the build phase completes. */
|
|
7214
|
+
archiveSizeBytes: z.number().optional(),
|
|
7215
|
+
/** Bytes pushed to the destination currently uploading. */
|
|
7216
|
+
uploadedBytes: z.number(),
|
|
7217
|
+
/** Destinations where the archive fully landed (uploaded + indexed). */
|
|
7218
|
+
completedDestinationIds: z.array(z.string()).readonly(),
|
|
7219
|
+
/** Destinations that failed during the fan-out. */
|
|
7220
|
+
failedDestinationIds: z.array(z.string()).readonly(),
|
|
7221
|
+
/** Failure message when `state === 'failed'`. */
|
|
7222
|
+
error: z.string().optional(),
|
|
7223
|
+
/**
|
|
7224
|
+
* 1-based place in the execution queue — 1 = runs next. Present only
|
|
7225
|
+
* while `state === 'queued'`. Stamped by the orchestrator from the
|
|
7226
|
+
* queue's OWN pending order, never derived from timestamps, so the
|
|
7227
|
+
* UI cannot show an order the executor will not honour.
|
|
7228
|
+
*/
|
|
7229
|
+
queuePosition: z.number().int().min(1).optional()
|
|
7230
|
+
});
|
|
7231
|
+
/**
|
|
7232
|
+
* Result of `backup.trigger`. The call still resolves when the run
|
|
7233
|
+
* terminates (compat with schedule-driven runs and the admin UI), but
|
|
7234
|
+
* it now names the run and says whether it had to WAIT: a trigger that
|
|
7235
|
+
* arrives while another run is in flight is enqueued (or joined onto
|
|
7236
|
+
* an identical already-queued run), never started concurrently.
|
|
7237
|
+
*/
|
|
7238
|
+
var BackupTriggerResultSchema = z.object({
|
|
7239
|
+
/** The run this trigger mapped to — poll it via `listRuns`, stop it via `cancel`. */
|
|
7240
|
+
runId: z.string(),
|
|
7241
|
+
/** True when the run waited behind an in-flight run instead of starting immediately. */
|
|
7242
|
+
queued: z.boolean(),
|
|
7243
|
+
/** True when this trigger was coalesced onto an identical already-queued run. */
|
|
7244
|
+
joined: z.boolean(),
|
|
7245
|
+
/** True when the run was cancelled before completing every destination. */
|
|
7246
|
+
cancelled: z.boolean(),
|
|
7247
|
+
/** One entry per destination the archive landed at (partial on cancel). */
|
|
7248
|
+
entries: z.array(BackupEntrySchema).readonly()
|
|
7249
|
+
});
|
|
7167
7250
|
/**
|
|
7168
7251
|
* A backup schedule — the N:M "entry" that binds one cron cadence to a
|
|
7169
7252
|
* SET of destination locations. Supersedes the per-location cron on
|
|
@@ -7221,6 +7304,11 @@ var backupCapability = {
|
|
|
7221
7304
|
* Trigger a backup. Without `destinations` the orchestrator fans
|
|
7222
7305
|
* out to every destination flagged as enabled in the routing
|
|
7223
7306
|
* config; with it, only the listed addons receive the archive.
|
|
7307
|
+
*
|
|
7308
|
+
* At most ONE backup run executes at a time — the source tree and
|
|
7309
|
+
* the staging disk are shared by every run, so a second trigger
|
|
7310
|
+
* while one is in flight is enqueued (or joined onto an identical
|
|
7311
|
+
* queued run) and the result says so. See D342.
|
|
7224
7312
|
*/
|
|
7225
7313
|
trigger: method(z.object({
|
|
7226
7314
|
/** Subset of registered `backup-destination` addon ids to write to. */
|
|
@@ -7234,7 +7322,30 @@ var backupCapability = {
|
|
|
7234
7322
|
* retention (manual runs).
|
|
7235
7323
|
*/
|
|
7236
7324
|
retentionCount: z.number().int().min(1).max(1e3).optional()
|
|
7237
|
-
}).optional(),
|
|
7325
|
+
}).optional(), BackupTriggerResultSchema, {
|
|
7326
|
+
kind: "mutation",
|
|
7327
|
+
auth: "admin"
|
|
7328
|
+
}),
|
|
7329
|
+
/**
|
|
7330
|
+
* Every run the orchestrator knows about, in EXECUTION order: the
|
|
7331
|
+
* running run first, then queued runs in the exact order they will
|
|
7332
|
+
* execute (each with `queuePosition`, 1 = next), then the bounded
|
|
7333
|
+
* finished history newest-first. Only one run executes at a time
|
|
7334
|
+
* (D342) — the queued section IS the line. Each run carries live
|
|
7335
|
+
* phase + byte counters so a runaway build is visible in seconds,
|
|
7336
|
+
* not via `du`.
|
|
7337
|
+
*/
|
|
7338
|
+
listRuns: method(z.void(), z.array(BackupRunSchema).readonly(), { auth: "admin" }),
|
|
7339
|
+
/**
|
|
7340
|
+
* Stop a backup run. Mirrors `storage-migration.cancel` semantics:
|
|
7341
|
+
* id in, `{ cancelled }` out — `false` when the run is unknown or
|
|
7342
|
+
* already terminal. A QUEUED run is removed before it ever starts;
|
|
7343
|
+
* the RUNNING run has its tar/upload stream actually aborted, the
|
|
7344
|
+
* half-written staging archive is deleted, and the in-flight
|
|
7345
|
+
* destination upload is aborted server-side (partial discarded).
|
|
7346
|
+
* Destinations that already completed keep their archive.
|
|
7347
|
+
*/
|
|
7348
|
+
cancel: method(z.object({ runId: z.string() }), z.object({ cancelled: z.boolean() }), {
|
|
7238
7349
|
kind: "mutation",
|
|
7239
7350
|
auth: "admin"
|
|
7240
7351
|
}),
|
|
@@ -8112,6 +8223,38 @@ var streamBrokerCapability = {
|
|
|
8112
8223
|
auth: "admin"
|
|
8113
8224
|
}),
|
|
8114
8225
|
/**
|
|
8226
|
+
* The HARDWARE behind a device number was replaced
|
|
8227
|
+
* (`deviceManager.migrateDevice`). Forget every piece of broker state that
|
|
8228
|
+
* described the old box, so the next catalog pull derives everything from
|
|
8229
|
+
* the camera that is actually there:
|
|
8230
|
+
*
|
|
8231
|
+
* - every `derived:*` stream definition — a derived is authored against a
|
|
8232
|
+
* specific profile layout, and against the wrong hardware its feeder
|
|
8233
|
+
* respawns forever (observed at attempt 2732 on the live hub,
|
|
8234
|
+
* 2026-09-01);
|
|
8235
|
+
* - the profile-slot assignment entry, PURGED (not unassigned — unassign
|
|
8236
|
+
* marks the slot manual, which would pin the stale choice instead of
|
|
8237
|
+
* letting `computeInitialAssignment` re-derive it);
|
|
8238
|
+
* - the probe snapshots (`<deviceId>/…` — probed codec/resolution of the
|
|
8239
|
+
* old hardware);
|
|
8240
|
+
* - the persisted RTSP token rows for the device's brokers (keyed
|
|
8241
|
+
* `<deviceId>/<camStreamId>`; the stream ids change with the hardware,
|
|
8242
|
+
* so the rows are dead URLs).
|
|
8243
|
+
*
|
|
8244
|
+
* An RPC, deliberately — an event is telemetry and may be dropped (D8),
|
|
8245
|
+
* and a dropped forget leaves a feeder respawning against a stream that
|
|
8246
|
+
* does not exist.
|
|
8247
|
+
*/
|
|
8248
|
+
forgetDeviceHardware: method(z.object({ deviceId: z.number().int().nonnegative() }), z.object({
|
|
8249
|
+
derivedStreamsDeleted: z.array(z.string()).readonly(),
|
|
8250
|
+
assignmentsPurged: z.boolean(),
|
|
8251
|
+
probeSnapshotsDropped: z.number().int().nonnegative(),
|
|
8252
|
+
rtspTokenRowsDeleted: z.number().int().nonnegative()
|
|
8253
|
+
}), {
|
|
8254
|
+
kind: "mutation",
|
|
8255
|
+
auth: "admin"
|
|
8256
|
+
}),
|
|
8257
|
+
/**
|
|
8115
8258
|
* Render a short GIF or MP4 from the broker's PRE-BUFFER around an instant.
|
|
8116
8259
|
*
|
|
8117
8260
|
* The pre-buffer is the only source that already holds the seconds BEFORE
|
|
@@ -18017,7 +18160,20 @@ var RecentTracksQueryInput = z.object({
|
|
|
18017
18160
|
projection: TrackProjectionSchema.optional(),
|
|
18018
18161
|
/** Include stationary-promoted rows (parked objects). Default false: the
|
|
18019
18162
|
* feed lists passages; parking records live on the stationary registry. */
|
|
18020
|
-
includeStationary: z.boolean().optional()
|
|
18163
|
+
includeStationary: z.boolean().optional(),
|
|
18164
|
+
/**
|
|
18165
|
+
* Restrict to these track classes. ABSENT or EMPTY means no filter.
|
|
18166
|
+
*
|
|
18167
|
+
* The same filter `listTracks` takes, because the timeline's class chips must
|
|
18168
|
+
* mean the same thing whether the scope is one camera or twelve. Until this
|
|
18169
|
+
* existed the scoped feed downloaded a page and narrowed it on the phone
|
|
18170
|
+
* while the single-camera path narrowed the read — one filter, two costs.
|
|
18171
|
+
*
|
|
18172
|
+
* A SUPERSET prefilter on `classes[]`, like its single-camera twin: rows
|
|
18173
|
+
* whose class list is unreadable are kept, and the client's rule stays the
|
|
18174
|
+
* exact one.
|
|
18175
|
+
*/
|
|
18176
|
+
classes: z.array(z.string()).optional()
|
|
18021
18177
|
});
|
|
18022
18178
|
var RecentTracksPageSchema = z.object({
|
|
18023
18179
|
/** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
|
|
@@ -24064,8 +24220,22 @@ var automationControlCapability = {
|
|
|
24064
24220
|
* threshold.
|
|
24065
24221
|
*/
|
|
24066
24222
|
var BatteryStatusSchema = z.object({
|
|
24067
|
-
/**
|
|
24068
|
-
|
|
24223
|
+
/**
|
|
24224
|
+
* 0..100 inclusive, firmware-reported. **`null` means NOT YET KNOWN** — the
|
|
24225
|
+
* provider has registered the capability but no reading has landed.
|
|
24226
|
+
*
|
|
24227
|
+
* It is nullable because it was not, and the only value a provider could
|
|
24228
|
+
* seed with was `0`. A battery camera behind an NVR therefore announced
|
|
24229
|
+
* itself at 0% on every start and corrected itself a moment later, which is
|
|
24230
|
+
* indistinguishable from a real flat battery: it fires the low-battery alert
|
|
24231
|
+
* every time the hub restarts. Unknown is not empty (D315), and on a battery
|
|
24232
|
+
* reading the difference is an alarm.
|
|
24233
|
+
*
|
|
24234
|
+
* `vacuum-control` and `lawn-mower-control` already model it this way.
|
|
24235
|
+
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
24236
|
+
* declines to band a non-finite reading, which is the correct shape.
|
|
24237
|
+
*/
|
|
24238
|
+
percentage: z.number().min(0).max(100).nullable(),
|
|
24069
24239
|
/**
|
|
24070
24240
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
24071
24241
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -33748,6 +33918,15 @@ var RUNTIME_DEFAULTS = {
|
|
|
33748
33918
|
* Each carried the same list and a comment asking the others to stay in sync.
|
|
33749
33919
|
* This is that list, in the one package all of them already depend on.
|
|
33750
33920
|
*
|
|
33921
|
+
* `ui-library` and the server's linked-devices expansion IMPORT it. Two
|
|
33922
|
+
* consumers cannot, and keep a checked copy instead: the viewer resolves
|
|
33923
|
+
* `@camstack/types` from its own `node_modules` (an installed release, where a
|
|
33924
|
+
* newly added export simply is not there), and the Home Assistant provider
|
|
33925
|
+
* expresses the same precedence over the `DeviceType` enum because it answers
|
|
33926
|
+
* a different question from the same ordering. `scripts/check-container-
|
|
33927
|
+
* priority-in-sync.ts` fails the build when either drifts — the comment that
|
|
33928
|
+
* used to ask for this could not.
|
|
33929
|
+
*
|
|
33751
33930
|
* The rule has two halves and the ORDER matters: an operator's explicit pick
|
|
33752
33931
|
* wins outright, and only in its absence does type priority decide. The pick is
|
|
33753
33932
|
* keyed on the child's re-sync-stable `entityId`, not its numeric id, so it
|
|
@@ -33803,7 +33982,7 @@ function rank(type) {
|
|
|
33803
33982
|
*/
|
|
33804
33983
|
function resolveContainerPrimaryChild(children, overrideEntityId) {
|
|
33805
33984
|
if (overrideEntityId !== void 0 && overrideEntityId !== null) {
|
|
33806
|
-
const picked = children.find((c) => c.entityId === overrideEntityId);
|
|
33985
|
+
const picked = children.find((c) => c.stableId === overrideEntityId) ?? children.find((c) => c.entityId !== void 0 && c.entityId === overrideEntityId);
|
|
33807
33986
|
if (picked !== void 0) return picked;
|
|
33808
33987
|
}
|
|
33809
33988
|
return [...children].toSorted((a, b) => rank(a.type) - rank(b.type))[0] ?? null;
|
|
@@ -34897,6 +35076,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
34897
35076
|
addonId: null,
|
|
34898
35077
|
access: "create"
|
|
34899
35078
|
},
|
|
35079
|
+
"backup.cancel": {
|
|
35080
|
+
capName: "backup",
|
|
35081
|
+
capScope: "system",
|
|
35082
|
+
addonId: null,
|
|
35083
|
+
access: "create"
|
|
35084
|
+
},
|
|
34900
35085
|
"backup.delete": {
|
|
34901
35086
|
capName: "backup",
|
|
34902
35087
|
capScope: "system",
|
|
@@ -34939,6 +35124,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
34939
35124
|
addonId: null,
|
|
34940
35125
|
access: "view"
|
|
34941
35126
|
},
|
|
35127
|
+
"backup.listRuns": {
|
|
35128
|
+
capName: "backup",
|
|
35129
|
+
capScope: "system",
|
|
35130
|
+
addonId: null,
|
|
35131
|
+
access: "view"
|
|
35132
|
+
},
|
|
34942
35133
|
"backup.listSchedules": {
|
|
34943
35134
|
capName: "backup",
|
|
34944
35135
|
capScope: "system",
|
|
@@ -39505,6 +39696,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
39505
39696
|
addonId: null,
|
|
39506
39697
|
access: "create"
|
|
39507
39698
|
},
|
|
39699
|
+
"streamBroker.forgetDeviceHardware": {
|
|
39700
|
+
capName: "stream-broker",
|
|
39701
|
+
capScope: "system",
|
|
39702
|
+
addonId: null,
|
|
39703
|
+
access: "delete"
|
|
39704
|
+
},
|
|
39508
39705
|
"streamBroker.getAllRtspEntries": {
|
|
39509
39706
|
capName: "stream-broker",
|
|
39510
39707
|
capScope: "system",
|
|
@@ -41963,6 +42160,11 @@ Object.freeze({
|
|
|
41963
42160
|
form: "single",
|
|
41964
42161
|
optional: false
|
|
41965
42162
|
}],
|
|
42163
|
+
"streamBroker.forgetDeviceHardware": [{
|
|
42164
|
+
name: "deviceId",
|
|
42165
|
+
form: "single",
|
|
42166
|
+
optional: false
|
|
42167
|
+
}],
|
|
41966
42168
|
"streamBroker.getDeviceAudioMute": [{
|
|
41967
42169
|
name: "deviceId",
|
|
41968
42170
|
form: "single",
|
|
@@ -7164,6 +7164,89 @@ var LocationStatSchema = zod.z.object({
|
|
|
7164
7164
|
fileCount: zod.z.number(),
|
|
7165
7165
|
present: zod.z.boolean()
|
|
7166
7166
|
});
|
|
7167
|
+
/** Lifecycle of a backup run. Terminal states: succeeded / failed / cancelled. */
|
|
7168
|
+
var BackupRunStateSchema = zod.z.enum([
|
|
7169
|
+
"queued",
|
|
7170
|
+
"running",
|
|
7171
|
+
"succeeded",
|
|
7172
|
+
"failed",
|
|
7173
|
+
"cancelled"
|
|
7174
|
+
]);
|
|
7175
|
+
/**
|
|
7176
|
+
* Where a running backup currently is. `queued` before it starts,
|
|
7177
|
+
* `building` while the tar.gz is being staged, `uploading` during the
|
|
7178
|
+
* per-destination fan-out, `done` once terminal.
|
|
7179
|
+
*/
|
|
7180
|
+
var BackupRunPhaseSchema = zod.z.enum([
|
|
7181
|
+
"queued",
|
|
7182
|
+
"building",
|
|
7183
|
+
"uploading",
|
|
7184
|
+
"done"
|
|
7185
|
+
]);
|
|
7186
|
+
/**
|
|
7187
|
+
* Observable state of one backup run — readable WHILE it runs via
|
|
7188
|
+
* `backup.listRuns`. This is what makes the execution queue and
|
|
7189
|
+
* `backup.cancel` usable: the 2026-09-04 incident (two concurrent
|
|
7190
|
+
* multi-GB builds, staging 5.1 GB → 18 GB, load 62) was only
|
|
7191
|
+
* diagnosable with `du` because nothing reported that runs existed or
|
|
7192
|
+
* how large the staged archive had grown.
|
|
7193
|
+
*/
|
|
7194
|
+
var BackupRunSchema = zod.z.object({
|
|
7195
|
+
/** Stable run id — the handle `backup.cancel` takes. */
|
|
7196
|
+
id: zod.z.string(),
|
|
7197
|
+
state: BackupRunStateSchema,
|
|
7198
|
+
phase: BackupRunPhaseSchema,
|
|
7199
|
+
/**
|
|
7200
|
+
* Resolved destination location ids. Empty while queued (targets are
|
|
7201
|
+
* resolved when the run starts, against the then-current policies).
|
|
7202
|
+
*/
|
|
7203
|
+
destinationIds: zod.z.array(zod.z.string()).readonly(),
|
|
7204
|
+
label: zod.z.string().optional(),
|
|
7205
|
+
/** ms-epoch when the run was submitted (trigger call / schedule fire). */
|
|
7206
|
+
requestedAt: zod.z.number(),
|
|
7207
|
+
/** ms-epoch when the run left the queue and started building. */
|
|
7208
|
+
startedAt: zod.z.number().optional(),
|
|
7209
|
+
/** ms-epoch when the run reached a terminal state. */
|
|
7210
|
+
finishedAt: zod.z.number().optional(),
|
|
7211
|
+
/** Compressed bytes of the staging archive written so far. */
|
|
7212
|
+
stagedBytes: zod.z.number(),
|
|
7213
|
+
/** Final staged archive size, once the build phase completes. */
|
|
7214
|
+
archiveSizeBytes: zod.z.number().optional(),
|
|
7215
|
+
/** Bytes pushed to the destination currently uploading. */
|
|
7216
|
+
uploadedBytes: zod.z.number(),
|
|
7217
|
+
/** Destinations where the archive fully landed (uploaded + indexed). */
|
|
7218
|
+
completedDestinationIds: zod.z.array(zod.z.string()).readonly(),
|
|
7219
|
+
/** Destinations that failed during the fan-out. */
|
|
7220
|
+
failedDestinationIds: zod.z.array(zod.z.string()).readonly(),
|
|
7221
|
+
/** Failure message when `state === 'failed'`. */
|
|
7222
|
+
error: zod.z.string().optional(),
|
|
7223
|
+
/**
|
|
7224
|
+
* 1-based place in the execution queue — 1 = runs next. Present only
|
|
7225
|
+
* while `state === 'queued'`. Stamped by the orchestrator from the
|
|
7226
|
+
* queue's OWN pending order, never derived from timestamps, so the
|
|
7227
|
+
* UI cannot show an order the executor will not honour.
|
|
7228
|
+
*/
|
|
7229
|
+
queuePosition: zod.z.number().int().min(1).optional()
|
|
7230
|
+
});
|
|
7231
|
+
/**
|
|
7232
|
+
* Result of `backup.trigger`. The call still resolves when the run
|
|
7233
|
+
* terminates (compat with schedule-driven runs and the admin UI), but
|
|
7234
|
+
* it now names the run and says whether it had to WAIT: a trigger that
|
|
7235
|
+
* arrives while another run is in flight is enqueued (or joined onto
|
|
7236
|
+
* an identical already-queued run), never started concurrently.
|
|
7237
|
+
*/
|
|
7238
|
+
var BackupTriggerResultSchema = zod.z.object({
|
|
7239
|
+
/** The run this trigger mapped to — poll it via `listRuns`, stop it via `cancel`. */
|
|
7240
|
+
runId: zod.z.string(),
|
|
7241
|
+
/** True when the run waited behind an in-flight run instead of starting immediately. */
|
|
7242
|
+
queued: zod.z.boolean(),
|
|
7243
|
+
/** True when this trigger was coalesced onto an identical already-queued run. */
|
|
7244
|
+
joined: zod.z.boolean(),
|
|
7245
|
+
/** True when the run was cancelled before completing every destination. */
|
|
7246
|
+
cancelled: zod.z.boolean(),
|
|
7247
|
+
/** One entry per destination the archive landed at (partial on cancel). */
|
|
7248
|
+
entries: zod.z.array(BackupEntrySchema).readonly()
|
|
7249
|
+
});
|
|
7167
7250
|
/**
|
|
7168
7251
|
* A backup schedule — the N:M "entry" that binds one cron cadence to a
|
|
7169
7252
|
* SET of destination locations. Supersedes the per-location cron on
|
|
@@ -7221,6 +7304,11 @@ var backupCapability = {
|
|
|
7221
7304
|
* Trigger a backup. Without `destinations` the orchestrator fans
|
|
7222
7305
|
* out to every destination flagged as enabled in the routing
|
|
7223
7306
|
* config; with it, only the listed addons receive the archive.
|
|
7307
|
+
*
|
|
7308
|
+
* At most ONE backup run executes at a time — the source tree and
|
|
7309
|
+
* the staging disk are shared by every run, so a second trigger
|
|
7310
|
+
* while one is in flight is enqueued (or joined onto an identical
|
|
7311
|
+
* queued run) and the result says so. See D342.
|
|
7224
7312
|
*/
|
|
7225
7313
|
trigger: method(zod.z.object({
|
|
7226
7314
|
/** Subset of registered `backup-destination` addon ids to write to. */
|
|
@@ -7234,7 +7322,30 @@ var backupCapability = {
|
|
|
7234
7322
|
* retention (manual runs).
|
|
7235
7323
|
*/
|
|
7236
7324
|
retentionCount: zod.z.number().int().min(1).max(1e3).optional()
|
|
7237
|
-
}).optional(),
|
|
7325
|
+
}).optional(), BackupTriggerResultSchema, {
|
|
7326
|
+
kind: "mutation",
|
|
7327
|
+
auth: "admin"
|
|
7328
|
+
}),
|
|
7329
|
+
/**
|
|
7330
|
+
* Every run the orchestrator knows about, in EXECUTION order: the
|
|
7331
|
+
* running run first, then queued runs in the exact order they will
|
|
7332
|
+
* execute (each with `queuePosition`, 1 = next), then the bounded
|
|
7333
|
+
* finished history newest-first. Only one run executes at a time
|
|
7334
|
+
* (D342) — the queued section IS the line. Each run carries live
|
|
7335
|
+
* phase + byte counters so a runaway build is visible in seconds,
|
|
7336
|
+
* not via `du`.
|
|
7337
|
+
*/
|
|
7338
|
+
listRuns: method(zod.z.void(), zod.z.array(BackupRunSchema).readonly(), { auth: "admin" }),
|
|
7339
|
+
/**
|
|
7340
|
+
* Stop a backup run. Mirrors `storage-migration.cancel` semantics:
|
|
7341
|
+
* id in, `{ cancelled }` out — `false` when the run is unknown or
|
|
7342
|
+
* already terminal. A QUEUED run is removed before it ever starts;
|
|
7343
|
+
* the RUNNING run has its tar/upload stream actually aborted, the
|
|
7344
|
+
* half-written staging archive is deleted, and the in-flight
|
|
7345
|
+
* destination upload is aborted server-side (partial discarded).
|
|
7346
|
+
* Destinations that already completed keep their archive.
|
|
7347
|
+
*/
|
|
7348
|
+
cancel: method(zod.z.object({ runId: zod.z.string() }), zod.z.object({ cancelled: zod.z.boolean() }), {
|
|
7238
7349
|
kind: "mutation",
|
|
7239
7350
|
auth: "admin"
|
|
7240
7351
|
}),
|
|
@@ -8112,6 +8223,38 @@ var streamBrokerCapability = {
|
|
|
8112
8223
|
auth: "admin"
|
|
8113
8224
|
}),
|
|
8114
8225
|
/**
|
|
8226
|
+
* The HARDWARE behind a device number was replaced
|
|
8227
|
+
* (`deviceManager.migrateDevice`). Forget every piece of broker state that
|
|
8228
|
+
* described the old box, so the next catalog pull derives everything from
|
|
8229
|
+
* the camera that is actually there:
|
|
8230
|
+
*
|
|
8231
|
+
* - every `derived:*` stream definition — a derived is authored against a
|
|
8232
|
+
* specific profile layout, and against the wrong hardware its feeder
|
|
8233
|
+
* respawns forever (observed at attempt 2732 on the live hub,
|
|
8234
|
+
* 2026-09-01);
|
|
8235
|
+
* - the profile-slot assignment entry, PURGED (not unassigned — unassign
|
|
8236
|
+
* marks the slot manual, which would pin the stale choice instead of
|
|
8237
|
+
* letting `computeInitialAssignment` re-derive it);
|
|
8238
|
+
* - the probe snapshots (`<deviceId>/…` — probed codec/resolution of the
|
|
8239
|
+
* old hardware);
|
|
8240
|
+
* - the persisted RTSP token rows for the device's brokers (keyed
|
|
8241
|
+
* `<deviceId>/<camStreamId>`; the stream ids change with the hardware,
|
|
8242
|
+
* so the rows are dead URLs).
|
|
8243
|
+
*
|
|
8244
|
+
* An RPC, deliberately — an event is telemetry and may be dropped (D8),
|
|
8245
|
+
* and a dropped forget leaves a feeder respawning against a stream that
|
|
8246
|
+
* does not exist.
|
|
8247
|
+
*/
|
|
8248
|
+
forgetDeviceHardware: method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.object({
|
|
8249
|
+
derivedStreamsDeleted: zod.z.array(zod.z.string()).readonly(),
|
|
8250
|
+
assignmentsPurged: zod.z.boolean(),
|
|
8251
|
+
probeSnapshotsDropped: zod.z.number().int().nonnegative(),
|
|
8252
|
+
rtspTokenRowsDeleted: zod.z.number().int().nonnegative()
|
|
8253
|
+
}), {
|
|
8254
|
+
kind: "mutation",
|
|
8255
|
+
auth: "admin"
|
|
8256
|
+
}),
|
|
8257
|
+
/**
|
|
8115
8258
|
* Render a short GIF or MP4 from the broker's PRE-BUFFER around an instant.
|
|
8116
8259
|
*
|
|
8117
8260
|
* The pre-buffer is the only source that already holds the seconds BEFORE
|
|
@@ -18017,7 +18160,20 @@ var RecentTracksQueryInput = zod.z.object({
|
|
|
18017
18160
|
projection: TrackProjectionSchema.optional(),
|
|
18018
18161
|
/** Include stationary-promoted rows (parked objects). Default false: the
|
|
18019
18162
|
* feed lists passages; parking records live on the stationary registry. */
|
|
18020
|
-
includeStationary: zod.z.boolean().optional()
|
|
18163
|
+
includeStationary: zod.z.boolean().optional(),
|
|
18164
|
+
/**
|
|
18165
|
+
* Restrict to these track classes. ABSENT or EMPTY means no filter.
|
|
18166
|
+
*
|
|
18167
|
+
* The same filter `listTracks` takes, because the timeline's class chips must
|
|
18168
|
+
* mean the same thing whether the scope is one camera or twelve. Until this
|
|
18169
|
+
* existed the scoped feed downloaded a page and narrowed it on the phone
|
|
18170
|
+
* while the single-camera path narrowed the read — one filter, two costs.
|
|
18171
|
+
*
|
|
18172
|
+
* A SUPERSET prefilter on `classes[]`, like its single-camera twin: rows
|
|
18173
|
+
* whose class list is unreadable are kept, and the client's rule stays the
|
|
18174
|
+
* exact one.
|
|
18175
|
+
*/
|
|
18176
|
+
classes: zod.z.array(zod.z.string()).optional()
|
|
18021
18177
|
});
|
|
18022
18178
|
var RecentTracksPageSchema = zod.z.object({
|
|
18023
18179
|
/** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
|
|
@@ -24064,8 +24220,22 @@ var automationControlCapability = {
|
|
|
24064
24220
|
* threshold.
|
|
24065
24221
|
*/
|
|
24066
24222
|
var BatteryStatusSchema = zod.z.object({
|
|
24067
|
-
/**
|
|
24068
|
-
|
|
24223
|
+
/**
|
|
24224
|
+
* 0..100 inclusive, firmware-reported. **`null` means NOT YET KNOWN** — the
|
|
24225
|
+
* provider has registered the capability but no reading has landed.
|
|
24226
|
+
*
|
|
24227
|
+
* It is nullable because it was not, and the only value a provider could
|
|
24228
|
+
* seed with was `0`. A battery camera behind an NVR therefore announced
|
|
24229
|
+
* itself at 0% on every start and corrected itself a moment later, which is
|
|
24230
|
+
* indistinguishable from a real flat battery: it fires the low-battery alert
|
|
24231
|
+
* every time the hub restarts. Unknown is not empty (D315), and on a battery
|
|
24232
|
+
* reading the difference is an alarm.
|
|
24233
|
+
*
|
|
24234
|
+
* `vacuum-control` and `lawn-mower-control` already model it this way.
|
|
24235
|
+
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
24236
|
+
* declines to band a non-finite reading, which is the correct shape.
|
|
24237
|
+
*/
|
|
24238
|
+
percentage: zod.z.number().min(0).max(100).nullable(),
|
|
24069
24239
|
/**
|
|
24070
24240
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
24071
24241
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -33748,6 +33918,15 @@ var RUNTIME_DEFAULTS = {
|
|
|
33748
33918
|
* Each carried the same list and a comment asking the others to stay in sync.
|
|
33749
33919
|
* This is that list, in the one package all of them already depend on.
|
|
33750
33920
|
*
|
|
33921
|
+
* `ui-library` and the server's linked-devices expansion IMPORT it. Two
|
|
33922
|
+
* consumers cannot, and keep a checked copy instead: the viewer resolves
|
|
33923
|
+
* `@camstack/types` from its own `node_modules` (an installed release, where a
|
|
33924
|
+
* newly added export simply is not there), and the Home Assistant provider
|
|
33925
|
+
* expresses the same precedence over the `DeviceType` enum because it answers
|
|
33926
|
+
* a different question from the same ordering. `scripts/check-container-
|
|
33927
|
+
* priority-in-sync.ts` fails the build when either drifts — the comment that
|
|
33928
|
+
* used to ask for this could not.
|
|
33929
|
+
*
|
|
33751
33930
|
* The rule has two halves and the ORDER matters: an operator's explicit pick
|
|
33752
33931
|
* wins outright, and only in its absence does type priority decide. The pick is
|
|
33753
33932
|
* keyed on the child's re-sync-stable `entityId`, not its numeric id, so it
|
|
@@ -33803,7 +33982,7 @@ function rank(type) {
|
|
|
33803
33982
|
*/
|
|
33804
33983
|
function resolveContainerPrimaryChild(children, overrideEntityId) {
|
|
33805
33984
|
if (overrideEntityId !== void 0 && overrideEntityId !== null) {
|
|
33806
|
-
const picked = children.find((c) => c.entityId === overrideEntityId);
|
|
33985
|
+
const picked = children.find((c) => c.stableId === overrideEntityId) ?? children.find((c) => c.entityId !== void 0 && c.entityId === overrideEntityId);
|
|
33807
33986
|
if (picked !== void 0) return picked;
|
|
33808
33987
|
}
|
|
33809
33988
|
return [...children].toSorted((a, b) => rank(a.type) - rank(b.type))[0] ?? null;
|
|
@@ -34897,6 +35076,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
34897
35076
|
addonId: null,
|
|
34898
35077
|
access: "create"
|
|
34899
35078
|
},
|
|
35079
|
+
"backup.cancel": {
|
|
35080
|
+
capName: "backup",
|
|
35081
|
+
capScope: "system",
|
|
35082
|
+
addonId: null,
|
|
35083
|
+
access: "create"
|
|
35084
|
+
},
|
|
34900
35085
|
"backup.delete": {
|
|
34901
35086
|
capName: "backup",
|
|
34902
35087
|
capScope: "system",
|
|
@@ -34939,6 +35124,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
34939
35124
|
addonId: null,
|
|
34940
35125
|
access: "view"
|
|
34941
35126
|
},
|
|
35127
|
+
"backup.listRuns": {
|
|
35128
|
+
capName: "backup",
|
|
35129
|
+
capScope: "system",
|
|
35130
|
+
addonId: null,
|
|
35131
|
+
access: "view"
|
|
35132
|
+
},
|
|
34942
35133
|
"backup.listSchedules": {
|
|
34943
35134
|
capName: "backup",
|
|
34944
35135
|
capScope: "system",
|
|
@@ -39505,6 +39696,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
39505
39696
|
addonId: null,
|
|
39506
39697
|
access: "create"
|
|
39507
39698
|
},
|
|
39699
|
+
"streamBroker.forgetDeviceHardware": {
|
|
39700
|
+
capName: "stream-broker",
|
|
39701
|
+
capScope: "system",
|
|
39702
|
+
addonId: null,
|
|
39703
|
+
access: "delete"
|
|
39704
|
+
},
|
|
39508
39705
|
"streamBroker.getAllRtspEntries": {
|
|
39509
39706
|
capName: "stream-broker",
|
|
39510
39707
|
capScope: "system",
|
|
@@ -41963,6 +42160,11 @@ Object.freeze({
|
|
|
41963
42160
|
form: "single",
|
|
41964
42161
|
optional: false
|
|
41965
42162
|
}],
|
|
42163
|
+
"streamBroker.forgetDeviceHardware": [{
|
|
42164
|
+
name: "deviceId",
|
|
42165
|
+
form: "single",
|
|
42166
|
+
optional: false
|
|
42167
|
+
}],
|
|
41966
42168
|
"streamBroker.getDeviceAudioMute": [{
|
|
41967
42169
|
name: "deviceId",
|
|
41968
42170
|
form: "single",
|