@camstack/addon-pipeline-orchestrator 1.2.324 → 1.2.326
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/_stub.js +509 -359
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-C-txzh2m.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-raB6EbO9.mjs} +3 -3
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-6CTaeZSL.mjs +26 -0
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-y5F27gMS.mjs +26 -0
- package/dist/{hostInit-C689lJpv.mjs → hostInit-DPLyefT-.mjs} +3 -3
- package/dist/index.js +398 -138
- package/dist/index.mjs +398 -138
- package/dist/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CdOR1t-0.mjs +0 -26
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-iNnhcbuC.mjs +0 -26
package/dist/index.mjs
CHANGED
|
@@ -5390,7 +5390,7 @@ var ZodIssueCode = {
|
|
|
5390
5390
|
var ZodFirstPartyTypeKind;
|
|
5391
5391
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5392
5392
|
//#endregion
|
|
5393
|
-
//#region ../types/dist/sleep-
|
|
5393
|
+
//#region ../types/dist/sleep-DRqxtc0C.mjs
|
|
5394
5394
|
/**
|
|
5395
5395
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5396
5396
|
* window to float samples (D455).
|
|
@@ -8730,6 +8730,10 @@ method(object({ deviceId: number() }), array(StreamSourceEntrySchema)), method(o
|
|
|
8730
8730
|
kind: "mutation",
|
|
8731
8731
|
auth: "admin"
|
|
8732
8732
|
}), method(object({ deviceId: number() }), unknown().nullable()), method(object({ deviceId: number() }), RawStateResultSchema.nullable(), { auth: "protected" });
|
|
8733
|
+
object({
|
|
8734
|
+
code: string().regex(/^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/),
|
|
8735
|
+
params: record(string(), union([string(), number()])).optional()
|
|
8736
|
+
});
|
|
8733
8737
|
/**
|
|
8734
8738
|
* Promise-based timer helpers — used everywhere the codebase needs to
|
|
8735
8739
|
* wait, back off, or schedule a retry. Before these helpers landed, each
|
|
@@ -8755,7 +8759,7 @@ function sleep(ms) {
|
|
|
8755
8759
|
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
8756
8760
|
}
|
|
8757
8761
|
//#endregion
|
|
8758
|
-
//#region ../types/dist/err-msg-
|
|
8762
|
+
//#region ../types/dist/err-msg-DLerKiVO.mjs
|
|
8759
8763
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
8760
8764
|
DeviceType["Camera"] = "camera";
|
|
8761
8765
|
DeviceType["Hub"] = "hub";
|
|
@@ -9005,6 +9009,22 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
9005
9009
|
DeviceRole["Nightvision"] = "nightvision";
|
|
9006
9010
|
DeviceRole["PrivacyMask"] = "privacy-mask";
|
|
9007
9011
|
DeviceRole["Doorbell"] = "doorbell";
|
|
9012
|
+
/** Interior blind / shade / roller blind. HA `device_class: blind|shade`. */
|
|
9013
|
+
DeviceRole["CoverBlind"] = "cover-blind";
|
|
9014
|
+
/** Exterior roller shutter. HA `device_class: shutter`; Homematic BROLL. */
|
|
9015
|
+
DeviceRole["CoverShutter"] = "cover-shutter";
|
|
9016
|
+
/** Exterior awning. HA `device_class: awning`. */
|
|
9017
|
+
DeviceRole["CoverAwning"] = "cover-awning";
|
|
9018
|
+
/** Curtain / drape. HA `device_class: curtain`. */
|
|
9019
|
+
DeviceRole["CoverCurtain"] = "cover-curtain";
|
|
9020
|
+
/** Garage door. HA `device_class: garage`. A security actuator: exports gate it. */
|
|
9021
|
+
DeviceRole["CoverGarage"] = "cover-garage";
|
|
9022
|
+
/** Gate. HA `device_class: gate`. A security actuator: exports gate it. */
|
|
9023
|
+
DeviceRole["CoverGate"] = "cover-gate";
|
|
9024
|
+
/** Door. HA `device_class: door`. A security actuator: exports gate it. */
|
|
9025
|
+
DeviceRole["CoverDoor"] = "cover-door";
|
|
9026
|
+
/** Window. HA `device_class: window`. */
|
|
9027
|
+
DeviceRole["CoverWindow"] = "cover-window";
|
|
9008
9028
|
/** Virtual HA toggle (input_boolean.*) — distinguishable from a
|
|
9009
9029
|
* real Switch device for UI rendering / export adapters. */
|
|
9010
9030
|
DeviceRole["BinaryHelper"] = "binary-helper";
|
|
@@ -9085,7 +9105,7 @@ function errMsg$2(err) {
|
|
|
9085
9105
|
return String(err);
|
|
9086
9106
|
}
|
|
9087
9107
|
//#endregion
|
|
9088
|
-
//#region ../types/dist/composition-
|
|
9108
|
+
//#region ../types/dist/composition-EQ9Ok0Ee.mjs
|
|
9089
9109
|
/**
|
|
9090
9110
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
9091
9111
|
*
|
|
@@ -12750,11 +12770,10 @@ var ClipModelMetaSchema = object({
|
|
|
12750
12770
|
/**
|
|
12751
12771
|
* The `vector-store` index this model's vectors live in. ONE feature space
|
|
12752
12772
|
* per index: two models share an index only when their vectors are ranked
|
|
12753
|
-
* against each other, which for CLIP means never across families.
|
|
12754
|
-
*
|
|
12755
|
-
* `modelId`); any other model gets its own.
|
|
12773
|
+
* against each other, which for CLIP means never across families. Every
|
|
12774
|
+
* model gets its own.
|
|
12756
12775
|
*/
|
|
12757
|
-
vectorIndex: string().regex(/^object-clip
|
|
12776
|
+
vectorIndex: string().regex(/^object-clip-[a-z0-9][a-z0-9-]*$/),
|
|
12758
12777
|
/**
|
|
12759
12778
|
* The text encoder's tokenizer, a declared SIBLING file of its onnx. The name
|
|
12760
12779
|
* must be unique per tokenizer: every model's siblings land in ONE flat
|
|
@@ -13223,7 +13242,8 @@ var ConvertResultSchema = object({
|
|
|
13223
13242
|
})).readonly()
|
|
13224
13243
|
});
|
|
13225
13244
|
_enum(["new-session", "step-up"]);
|
|
13226
|
-
|
|
13245
|
+
/** The languages the hub's app landing pages are written in (D858). */
|
|
13246
|
+
var AppPageLangSchema = _enum(["en", "it"]);
|
|
13227
13247
|
/** How a native app starts one login method (D858), as `auth.listLoginMethods` reports it. */
|
|
13228
13248
|
var AppLoginPathSchema = discriminatedUnion("kind", [object({ kind: literal("native") }), object({
|
|
13229
13249
|
kind: literal("browser"),
|
|
@@ -13720,7 +13740,9 @@ var ConfigTabDeclarationSchema = object({
|
|
|
13720
13740
|
var ConfigSectionWithValuesSchema = object({
|
|
13721
13741
|
id: string(),
|
|
13722
13742
|
title: string(),
|
|
13743
|
+
titleKey: string().optional(),
|
|
13723
13744
|
description: string().optional(),
|
|
13745
|
+
descriptionKey: string().optional(),
|
|
13724
13746
|
style: _enum(["card", "accordion"]).optional(),
|
|
13725
13747
|
defaultCollapsed: boolean().optional(),
|
|
13726
13748
|
columns: union([
|
|
@@ -17003,6 +17025,9 @@ method(object({
|
|
|
17003
17025
|
}), method(object({ refs: RefListSchema }), KeychainResolveResultSchema, { auth: "admin" }), method(object({ refs: RefListSchema }), object({ deleted: number().int() }), {
|
|
17004
17026
|
kind: "mutation",
|
|
17005
17027
|
auth: "admin"
|
|
17028
|
+
}), method(object({ deviceId: number().int() }), object({ refs: array(string()).readonly() }), {
|
|
17029
|
+
kind: "mutation",
|
|
17030
|
+
auth: "admin"
|
|
17006
17031
|
}), method(object({}), array(KeychainSecretMetaSchema).readonly(), { auth: "admin" }), method(object({}), KeychainStateSchema, { auth: "admin" });
|
|
17007
17032
|
/**
|
|
17008
17033
|
* device-history — the ONE mechanism for numeric history (D850).
|
|
@@ -38406,6 +38431,38 @@ var DeviceRegisterPayloadSchema = object({
|
|
|
38406
38431
|
* omits it and the keyword backstop applies. */
|
|
38407
38432
|
secretConfigPaths: array(string()).optional()
|
|
38408
38433
|
});
|
|
38434
|
+
/** One deleted device, kept by device-manager so an addon that missed the
|
|
38435
|
+
* deletion can still learn of it (D865). Written by the delete path ONLY. */
|
|
38436
|
+
var DeviceTombstoneSchema = object({
|
|
38437
|
+
deviceId: number().int().positive(),
|
|
38438
|
+
/** Epoch ms the device was deleted. */
|
|
38439
|
+
deletedAt: number(),
|
|
38440
|
+
/** The addon that owned the device when it was deleted. */
|
|
38441
|
+
providerAddonId: string()
|
|
38442
|
+
});
|
|
38443
|
+
/** Data held for one device that device-manager no longer knows. */
|
|
38444
|
+
var OrphanDeviceDataSchema = object({
|
|
38445
|
+
deviceId: number().int().positive(),
|
|
38446
|
+
/** Rows/files per store (`<addonId>:<store>`). */
|
|
38447
|
+
stores: record(string(), number().int().nonnegative())
|
|
38448
|
+
});
|
|
38449
|
+
var CleanupDeletedDevicesResultSchema = object({
|
|
38450
|
+
/**
|
|
38451
|
+
* `skipped` — the absence check could not be trusted (a device-list read
|
|
38452
|
+
* failed or the two reads disagreed); nothing was listed or deleted.
|
|
38453
|
+
* `dry-run` — `devices` is what a confirmed run would delete.
|
|
38454
|
+
* `cleaned` — `devices` is what was deleted.
|
|
38455
|
+
*/
|
|
38456
|
+
status: _enum([
|
|
38457
|
+
"skipped",
|
|
38458
|
+
"dry-run",
|
|
38459
|
+
"cleaned"
|
|
38460
|
+
]),
|
|
38461
|
+
reason: string().nullable(),
|
|
38462
|
+
devices: array(OrphanDeviceDataSchema),
|
|
38463
|
+
/** Implementers that could not be asked (their stores are not in `devices`). */
|
|
38464
|
+
unreachable: array(string())
|
|
38465
|
+
});
|
|
38409
38466
|
var DeviceRemovePayloadSchema = object({ deviceId: number() });
|
|
38410
38467
|
var DevicePersistConfigPayloadSchema = object({
|
|
38411
38468
|
deviceId: number(),
|
|
@@ -38644,6 +38701,9 @@ method(object({
|
|
|
38644
38701
|
}), method(object({ deviceId: number() }), object({ success: literal(true) }), {
|
|
38645
38702
|
kind: "mutation",
|
|
38646
38703
|
auth: "admin"
|
|
38704
|
+
}), method(object({ sinceMs: number() }), array(DeviceTombstoneSchema).readonly(), { auth: "admin" }), method(object({ confirm: boolean() }), CleanupDeletedDevicesResultSchema, {
|
|
38705
|
+
kind: "mutation",
|
|
38706
|
+
auth: "admin"
|
|
38647
38707
|
}), method(object({ integrationId: string() }), object({ removed: number() }), {
|
|
38648
38708
|
kind: "mutation",
|
|
38649
38709
|
auth: "admin"
|
|
@@ -39106,6 +39166,72 @@ method(object({
|
|
|
39106
39166
|
auth: "admin"
|
|
39107
39167
|
});
|
|
39108
39168
|
/**
|
|
39169
|
+
* `device-removal-hook` — the reliable way an addon learns a device was DELETED,
|
|
39170
|
+
* so it can delete what it keeps for that device (D865).
|
|
39171
|
+
*
|
|
39172
|
+
* ## Why an event was not enough
|
|
39173
|
+
*
|
|
39174
|
+
* `DeviceUnregistered` is telemetry and may be dropped (D8, D11). An addon that
|
|
39175
|
+
* hung its per-device clean-up on it left durable garbage whenever the event was
|
|
39176
|
+
* lost, the addon was down, or it was restarting at that moment — footage,
|
|
39177
|
+
* analytics rows and settings of device 4483 outlived the device. Loss of this
|
|
39178
|
+
* signal corrupts durable state, so it travels by RPC with a recorded fact
|
|
39179
|
+
* behind it (D8: "if loss would corrupt durable state, use RPC plus a reconcile").
|
|
39180
|
+
*
|
|
39181
|
+
* ## The three pieces
|
|
39182
|
+
*
|
|
39183
|
+
* 1. device-manager records a TOMBSTONE per deleted device
|
|
39184
|
+
* (`deviceManager.listDeletedSince`). The tombstone is written by the delete
|
|
39185
|
+
* path only — a device that is unregistered, offline or whose provider is
|
|
39186
|
+
* restarting is NOT deleted and gets none.
|
|
39187
|
+
* 2. After a delete, device-manager calls `onDeviceRemoved` on EVERY provider of
|
|
39188
|
+
* this collection, one by one, with bounded retries, logged.
|
|
39189
|
+
* 3. An implementer that was down asks `deviceManager.listDeletedSince` at start
|
|
39190
|
+
* (and periodically) and runs its own `onDeviceRemoved` for each tombstone it
|
|
39191
|
+
* may have missed. Reconcile is driven by tombstones, never by "the device is
|
|
39192
|
+
* not in the current list": a failed or partial list read must not destroy data.
|
|
39193
|
+
*
|
|
39194
|
+
* ## Contract for an implementer
|
|
39195
|
+
*
|
|
39196
|
+
* - **Idempotent.** It is called again by a retry, by a reconcile and by the
|
|
39197
|
+
* admin cleanup of data that predates tombstones. A device with nothing left
|
|
39198
|
+
* answers zero counts.
|
|
39199
|
+
* - **One key per store** in `counts`, deleted rows/files per store, so the log
|
|
39200
|
+
* line and the admin dry run can say where the data was.
|
|
39201
|
+
* - **Training datasets are NOT device data** and are never deleted by a hook.
|
|
39202
|
+
* - **Per-device SETTINGS are not hooked.** Configuration lives in the standard
|
|
39203
|
+
* per-device store (`ctx.settings.*DeviceStore`), which device-manager clears
|
|
39204
|
+
* for every addon centrally at delete time. This hook is for device DATA
|
|
39205
|
+
* that is not settings: footage, event media, analytics rows, history.
|
|
39206
|
+
* - A provider that cannot tell is allowed to throw; the caller retries it.
|
|
39207
|
+
*
|
|
39208
|
+
* `listDeviceData` is the read side of the admin "Clean data of deleted devices"
|
|
39209
|
+
* action: which device ids this addon holds data for, and how much per store. It
|
|
39210
|
+
* never deletes. device-manager decides which of those ids are absent from the
|
|
39211
|
+
* fleet, from TWO consistent reads of the device list (D49).
|
|
39212
|
+
*/
|
|
39213
|
+
/** What a hook is told about the deletion. */
|
|
39214
|
+
var DeviceRemovedInputSchema = object({
|
|
39215
|
+
deviceId: number().int().positive(),
|
|
39216
|
+
/** Epoch ms the device was deleted (the tombstone's `deletedAt`). */
|
|
39217
|
+
deletedAt: number(),
|
|
39218
|
+
/** The addon that owned the device, as device-manager last knew it. */
|
|
39219
|
+
providerAddonId: string()
|
|
39220
|
+
});
|
|
39221
|
+
/** Rows/files/refs deleted, per store this implementer owns. */
|
|
39222
|
+
var DeviceRemovedResultSchema = object({ counts: record(string(), number().int().nonnegative()) });
|
|
39223
|
+
/** One (store, device) pair an implementer holds data for. */
|
|
39224
|
+
var DeviceDataEntrySchema = object({
|
|
39225
|
+
store: string(),
|
|
39226
|
+
deviceId: number().int().positive(),
|
|
39227
|
+
/** Rows (or files) held for the device in the store. */
|
|
39228
|
+
count: number().int().nonnegative()
|
|
39229
|
+
});
|
|
39230
|
+
method(DeviceRemovedInputSchema, DeviceRemovedResultSchema, {
|
|
39231
|
+
kind: "mutation",
|
|
39232
|
+
auth: "admin"
|
|
39233
|
+
}), method(_void(), array(DeviceDataEntrySchema).readonly(), { auth: "admin" });
|
|
39234
|
+
/**
|
|
39109
39235
|
* `failure-contribution` — the capability an addon reports its OWN losses
|
|
39110
39236
|
* through, per camera, with the denominator attached. It stores nothing.
|
|
39111
39237
|
*
|
|
@@ -56575,7 +56701,19 @@ var TlsStatusSchema = object({
|
|
|
56575
56701
|
sans: array(string()),
|
|
56576
56702
|
caCertPem: string().nullable(),
|
|
56577
56703
|
reissueError: string().nullable(),
|
|
56578
|
-
restartRequired: boolean()
|
|
56704
|
+
restartRequired: boolean(),
|
|
56705
|
+
/** An uploaded certificate is served but no CA is kept for it and it is not publicly signed (D873). */
|
|
56706
|
+
caMissing: boolean(),
|
|
56707
|
+
/** Does the hub's own generated CA carry a critical name constraint? `null` while it has none. */
|
|
56708
|
+
localCaConstrained: boolean().nullable(),
|
|
56709
|
+
/** ISO time the generated CA was replaced by a constrained one; devices must re-download it. */
|
|
56710
|
+
caReplacedAt: string().nullable(),
|
|
56711
|
+
/** The uploaded certificate kept on disk, selectable again after "Revert to generated". */
|
|
56712
|
+
storedUpload: object({
|
|
56713
|
+
leafFingerprintSha256: string(),
|
|
56714
|
+
validTo: string(),
|
|
56715
|
+
hasCa: boolean()
|
|
56716
|
+
}).nullable()
|
|
56579
56717
|
});
|
|
56580
56718
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
56581
56719
|
/**
|
|
@@ -56631,6 +56769,9 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
56631
56769
|
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
56632
56770
|
kind: "mutation",
|
|
56633
56771
|
auth: "admin"
|
|
56772
|
+
}), method(_void(), TlsStatusSchema, {
|
|
56773
|
+
kind: "mutation",
|
|
56774
|
+
auth: "admin"
|
|
56634
56775
|
});
|
|
56635
56776
|
object({
|
|
56636
56777
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
@@ -64674,6 +64815,12 @@ Object.freeze({
|
|
|
64674
64815
|
addonId: null,
|
|
64675
64816
|
access: "create"
|
|
64676
64817
|
},
|
|
64818
|
+
"deviceManager.cleanupDeletedDevicesData": {
|
|
64819
|
+
capName: "device-manager",
|
|
64820
|
+
capScope: "system",
|
|
64821
|
+
addonId: null,
|
|
64822
|
+
access: "create"
|
|
64823
|
+
},
|
|
64677
64824
|
"deviceManager.createDevice": {
|
|
64678
64825
|
capName: "device-manager",
|
|
64679
64826
|
capScope: "system",
|
|
@@ -64854,6 +65001,12 @@ Object.freeze({
|
|
|
64854
65001
|
addonId: null,
|
|
64855
65002
|
access: "view"
|
|
64856
65003
|
},
|
|
65004
|
+
"deviceManager.listDeletedSince": {
|
|
65005
|
+
capName: "device-manager",
|
|
65006
|
+
capScope: "system",
|
|
65007
|
+
addonId: null,
|
|
65008
|
+
access: "view"
|
|
65009
|
+
},
|
|
64857
65010
|
"deviceManager.listDeviceIdsByCaps": {
|
|
64858
65011
|
capName: "device-manager",
|
|
64859
65012
|
capScope: "system",
|
|
@@ -65262,6 +65415,18 @@ Object.freeze({
|
|
|
65262
65415
|
addonId: null,
|
|
65263
65416
|
access: "create"
|
|
65264
65417
|
},
|
|
65418
|
+
"deviceRemovalHook.listDeviceData": {
|
|
65419
|
+
capName: "device-removal-hook",
|
|
65420
|
+
capScope: "system",
|
|
65421
|
+
addonId: null,
|
|
65422
|
+
access: "view"
|
|
65423
|
+
},
|
|
65424
|
+
"deviceRemovalHook.onDeviceRemoved": {
|
|
65425
|
+
capName: "device-removal-hook",
|
|
65426
|
+
capScope: "system",
|
|
65427
|
+
addonId: null,
|
|
65428
|
+
access: "create"
|
|
65429
|
+
},
|
|
65265
65430
|
"deviceState.claimFields": {
|
|
65266
65431
|
capName: "device-state",
|
|
65267
65432
|
capScope: "system",
|
|
@@ -65694,6 +65859,12 @@ Object.freeze({
|
|
|
65694
65859
|
addonId: null,
|
|
65695
65860
|
access: "delete"
|
|
65696
65861
|
},
|
|
65862
|
+
"keychain.deleteDeviceScope": {
|
|
65863
|
+
capName: "keychain",
|
|
65864
|
+
capScope: "system",
|
|
65865
|
+
addonId: null,
|
|
65866
|
+
access: "delete"
|
|
65867
|
+
},
|
|
65697
65868
|
"keychain.getState": {
|
|
65698
65869
|
capName: "keychain",
|
|
65699
65870
|
capScope: "system",
|
|
@@ -66132,6 +66303,12 @@ Object.freeze({
|
|
|
66132
66303
|
addonId: null,
|
|
66133
66304
|
access: "create"
|
|
66134
66305
|
},
|
|
66306
|
+
"localNetwork.useStoredUploadedCertificate": {
|
|
66307
|
+
capName: "local-network",
|
|
66308
|
+
capScope: "system",
|
|
66309
|
+
addonId: null,
|
|
66310
|
+
access: "create"
|
|
66311
|
+
},
|
|
66135
66312
|
"lockControl.getStatus": {
|
|
66136
66313
|
capName: "lock-control",
|
|
66137
66314
|
capScope: "device",
|
|
@@ -71734,6 +71911,11 @@ Object.freeze({
|
|
|
71734
71911
|
form: "single",
|
|
71735
71912
|
optional: false
|
|
71736
71913
|
}],
|
|
71914
|
+
"deviceRemovalHook.onDeviceRemoved": [{
|
|
71915
|
+
name: "deviceId",
|
|
71916
|
+
form: "single",
|
|
71917
|
+
optional: false
|
|
71918
|
+
}],
|
|
71737
71919
|
"deviceState.claimFields": [{
|
|
71738
71920
|
name: "deviceId",
|
|
71739
71921
|
form: "single",
|
|
@@ -71923,6 +72105,11 @@ Object.freeze({
|
|
|
71923
72105
|
form: "single",
|
|
71924
72106
|
optional: false
|
|
71925
72107
|
}],
|
|
72108
|
+
"keychain.deleteDeviceScope": [{
|
|
72109
|
+
name: "deviceId",
|
|
72110
|
+
form: "single",
|
|
72111
|
+
optional: false
|
|
72112
|
+
}],
|
|
71926
72113
|
"lawnMowerControl.dock": [{
|
|
71927
72114
|
name: "deviceId",
|
|
71928
72115
|
form: "single",
|
|
@@ -74271,6 +74458,8 @@ new Set([
|
|
|
74271
74458
|
"audio-tools",
|
|
74272
74459
|
"audio-silence"
|
|
74273
74460
|
]);
|
|
74461
|
+
AppPageLangSchema.options;
|
|
74462
|
+
record(string().min(1), string());
|
|
74274
74463
|
/**
|
|
74275
74464
|
* TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
|
|
74276
74465
|
*
|
|
@@ -74549,7 +74738,11 @@ var REMOVED_MODEL_IDS = {
|
|
|
74549
74738
|
"face-embedding": ["arcface-r100", "inception-resnet-v1"],
|
|
74550
74739
|
"plate-detection": ["yolov8n-plate"],
|
|
74551
74740
|
"plate-ocr": ["vgg-english-g2"],
|
|
74552
|
-
"clip-embedding": [
|
|
74741
|
+
"clip-embedding": [
|
|
74742
|
+
"mobileclip-s0",
|
|
74743
|
+
"mobileclip-s1",
|
|
74744
|
+
"mobileclip-s2"
|
|
74745
|
+
],
|
|
74553
74746
|
"package-detection": ["yolov8n-package"],
|
|
74554
74747
|
"bird-classifier": ["bird-classifier-aiy-edgetpu", "bird-nabirds-404"],
|
|
74555
74748
|
"animal-classifier": ["animals-10"],
|
|
@@ -75972,6 +76165,25 @@ var DETAIL_CROP_PADDING_FIELD = {
|
|
|
75972
76165
|
default: DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio
|
|
75973
76166
|
};
|
|
75974
76167
|
/**
|
|
76168
|
+
* The cadence of the automatic whole-archive face rescore (D866).
|
|
76169
|
+
*
|
|
76170
|
+
* One cluster-wide knob in `pipeline-orchestrator`'s GLOBAL settings, read by
|
|
76171
|
+
* `pipeline-analytics` over the hub-routed `addon-settings` cap on every tick.
|
|
76172
|
+
* It lives here for the same reason the detail-crop convention does: the
|
|
76173
|
+
* writer (orchestrator) and the reader (analytics) are different addons, they
|
|
76174
|
+
* never import each other, and a key copied by hand is how a setting silently
|
|
76175
|
+
* stops arriving.
|
|
76176
|
+
*/
|
|
76177
|
+
var FACE_RESCORE_SECTION_ID = "face-rescore";
|
|
76178
|
+
var FACE_RESCORE_INTERVAL_KEY = "faceRescoreIntervalMinutes";
|
|
76179
|
+
/** Slider bounds. `0` is "disabled"; anything in 1..9 is raised to `min`. */
|
|
76180
|
+
var FACE_RESCORE_INTERVAL_FIELD = {
|
|
76181
|
+
min: 0,
|
|
76182
|
+
max: 1440,
|
|
76183
|
+
step: 5,
|
|
76184
|
+
default: 60
|
|
76185
|
+
};
|
|
76186
|
+
/**
|
|
75975
76187
|
* THE native-frame **lease** knobs — hold depth, RAM budget, demand window and
|
|
75976
76188
|
* subject-tile budget for the decode worker's native-resolution retention.
|
|
75977
76189
|
*
|
|
@@ -76427,6 +76639,22 @@ var DurableLedger = class DurableLedger {
|
|
|
76427
76639
|
}
|
|
76428
76640
|
}
|
|
76429
76641
|
/**
|
|
76642
|
+
* Drop every MIRRORED row about `deviceId` (per the spec's `deviceIdOf`),
|
|
76643
|
+
* leaving the durable rows to the caller's `deleteWhere` (D871). Returns how
|
|
76644
|
+
* many mirror entries went; a spec without `deviceIdOf` evicts nothing.
|
|
76645
|
+
*/
|
|
76646
|
+
evictDevice(deviceId) {
|
|
76647
|
+
const of = this.spec.deviceIdOf;
|
|
76648
|
+
if (of === void 0) return 0;
|
|
76649
|
+
let evicted = 0;
|
|
76650
|
+
for (const [key, row] of [...this.mirror.entries()]) {
|
|
76651
|
+
if (of(row) !== deviceId) continue;
|
|
76652
|
+
this.mirror.delete(key);
|
|
76653
|
+
evicted += 1;
|
|
76654
|
+
}
|
|
76655
|
+
return evicted;
|
|
76656
|
+
}
|
|
76657
|
+
/**
|
|
76430
76658
|
* Drop every mirrored key NOT in `activeKeys`. Returns how many rows went.
|
|
76431
76659
|
*
|
|
76432
76660
|
* **The caller must hold an AUTHORITATIVE active set.** A prune driven by a
|
|
@@ -83735,67 +83963,20 @@ function buildRunnerConfig(base, overrides) {
|
|
|
83735
83963
|
preRollInferenceFrames: config.preRollInferenceFrames
|
|
83736
83964
|
};
|
|
83737
83965
|
}
|
|
83738
|
-
|
|
83739
|
-
|
|
83740
|
-
|
|
83741
|
-
|
|
83742
|
-
name: "deviceId",
|
|
83743
|
-
type: "TEXT",
|
|
83744
|
-
primaryKey: true,
|
|
83745
|
-
notNull: true
|
|
83746
|
-
},
|
|
83747
|
-
(
|
|
83748
|
-
/** The feature name list, verbatim. */
|
|
83749
|
-
{
|
|
83750
|
-
name: "features",
|
|
83751
|
-
type: "JSON",
|
|
83752
|
-
notNull: true
|
|
83753
|
-
}),
|
|
83754
|
-
{
|
|
83755
|
-
name: "updatedAt",
|
|
83756
|
-
type: "INTEGER",
|
|
83757
|
-
notNull: true
|
|
83758
|
-
}
|
|
83759
|
-
],
|
|
83760
|
-
writeMode: "write-behind",
|
|
83761
|
-
keyOf: (row) => String(row.deviceId),
|
|
83762
|
-
toValue: (row) => ({
|
|
83763
|
-
features: [...row.features],
|
|
83764
|
-
updatedAt: row.updatedAt
|
|
83765
|
-
}),
|
|
83766
|
-
fromRecord: (key, data) => {
|
|
83767
|
-
const deviceId = Number(key);
|
|
83768
|
-
const raw = data["features"];
|
|
83769
|
-
if (!Number.isFinite(deviceId) || !Array.isArray(raw)) return null;
|
|
83770
|
-
const features = raw.filter((f) => typeof f === "string");
|
|
83771
|
-
if (features.length === 0) return null;
|
|
83772
|
-
const updatedAt = Number(data["updatedAt"]);
|
|
83773
|
-
return {
|
|
83774
|
-
deviceId,
|
|
83775
|
-
features,
|
|
83776
|
-
updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
|
|
83777
|
-
restored: true
|
|
83778
|
-
};
|
|
83779
|
-
},
|
|
83780
|
-
deviceIdOf: (row) => row.deviceId
|
|
83781
|
-
};
|
|
83966
|
+
//#endregion
|
|
83967
|
+
//#region src/device-features-mirror.ts
|
|
83968
|
+
/** The per-device settings key a device's mirrored features live under. */
|
|
83969
|
+
var DEVICE_FEATURES_KEY = "deviceFeatures";
|
|
83782
83970
|
var DeviceFeaturesMirror = class {
|
|
83783
83971
|
/** Process-local fallback, used only when no store was supplied. */
|
|
83784
83972
|
local = /* @__PURE__ */ new Map();
|
|
83785
|
-
|
|
83973
|
+
settings;
|
|
83786
83974
|
logger;
|
|
83787
83975
|
now;
|
|
83788
83976
|
constructor(deps) {
|
|
83789
83977
|
this.logger = deps.logger;
|
|
83790
83978
|
this.now = deps.now ?? (() => Date.now());
|
|
83791
|
-
this.
|
|
83792
|
-
spec: DEVICE_FEATURES_SPEC,
|
|
83793
|
-
store: deps.store,
|
|
83794
|
-
logger: deps.logger
|
|
83795
|
-
});
|
|
83796
|
-
}
|
|
83797
|
-
static declare(store) {
|
|
83798
|
-
return DurableLedger.declare(store, DEVICE_FEATURES_SPEC);
|
|
83979
|
+
this.settings = deps.settings ?? null;
|
|
83799
83980
|
}
|
|
83800
83981
|
/**
|
|
83801
83982
|
* Seed the mirror from the last session. Call once at boot, after `declare`
|
|
@@ -83803,13 +83984,29 @@ var DeviceFeaturesMirror = class {
|
|
|
83803
83984
|
* already there when the first read fails.
|
|
83804
83985
|
*/
|
|
83805
83986
|
async hydrate() {
|
|
83806
|
-
|
|
83807
|
-
|
|
83808
|
-
|
|
83809
|
-
|
|
83987
|
+
const settings = this.settings;
|
|
83988
|
+
if (settings === null) return 0;
|
|
83989
|
+
const list = settings.listDeviceStoreIds;
|
|
83990
|
+
if (list !== void 0) {
|
|
83991
|
+
const ids = await list.call(settings);
|
|
83992
|
+
const batch = settings.readDeviceStoreBatch;
|
|
83993
|
+
const blobs = batch === void 0 ? new Map(await Promise.all(ids.map(async (id) => [id, await settings.readDeviceStore(id)]))) : await batch.call(settings, ids);
|
|
83994
|
+
for (const [deviceId, blob] of blobs) {
|
|
83995
|
+
const row = rowOf(deviceId, blob[DEVICE_FEATURES_KEY]);
|
|
83996
|
+
if (row !== null) this.local.set(deviceId, row);
|
|
83997
|
+
}
|
|
83998
|
+
}
|
|
83999
|
+
this.logger.info("device-features mirror restored", { meta: { devices: this.local.size } });
|
|
84000
|
+
return this.local.size;
|
|
83810
84001
|
}
|
|
83811
84002
|
held(deviceId) {
|
|
83812
|
-
return this.
|
|
84003
|
+
return this.local.get(deviceId);
|
|
84004
|
+
}
|
|
84005
|
+
async persist(deviceId, features, updatedAt) {
|
|
84006
|
+
await this.settings?.writeDeviceStore(deviceId, { [DEVICE_FEATURES_KEY]: {
|
|
84007
|
+
features: [...features],
|
|
84008
|
+
updatedAt
|
|
84009
|
+
} });
|
|
83813
84010
|
}
|
|
83814
84011
|
remember(deviceId, features) {
|
|
83815
84012
|
const row = {
|
|
@@ -83818,20 +84015,19 @@ var DeviceFeaturesMirror = class {
|
|
|
83818
84015
|
updatedAt: this.now(),
|
|
83819
84016
|
restored: false
|
|
83820
84017
|
};
|
|
83821
|
-
|
|
83822
|
-
|
|
83823
|
-
return;
|
|
83824
|
-
}
|
|
83825
|
-
const held = this.durable.get(String(deviceId));
|
|
84018
|
+
const held = this.local.get(deviceId);
|
|
84019
|
+
this.local.set(deviceId, row);
|
|
83826
84020
|
if (held !== void 0 && !held.restored && sameFeatures(held.features, features)) return;
|
|
83827
|
-
this.
|
|
84021
|
+
this.persist(deviceId, features, row.updatedAt).catch((err) => {
|
|
84022
|
+
this.logger.warn("device-features mirror not persisted", {
|
|
84023
|
+
tags: { deviceId },
|
|
84024
|
+
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
84025
|
+
});
|
|
84026
|
+
});
|
|
83828
84027
|
}
|
|
83829
84028
|
drop(deviceId) {
|
|
83830
|
-
|
|
83831
|
-
|
|
83832
|
-
return;
|
|
83833
|
-
}
|
|
83834
|
-
this.durable.forget(String(deviceId));
|
|
84029
|
+
this.local.delete(deviceId);
|
|
84030
|
+
this.settings?.writeDeviceStore(deviceId, { [DEVICE_FEATURES_KEY]: void 0 });
|
|
83835
84031
|
}
|
|
83836
84032
|
/**
|
|
83837
84033
|
* Resolve a device's features, preferring a fresh read but never letting a
|
|
@@ -83880,7 +84076,7 @@ var DeviceFeaturesMirror = class {
|
|
|
83880
84076
|
this.drop(deviceId);
|
|
83881
84077
|
return [];
|
|
83882
84078
|
}
|
|
83883
|
-
/** Drop a device's mirror
|
|
84079
|
+
/** Drop a device's mirror. (A removed device's settings are cleared centrally, D865.) */
|
|
83884
84080
|
forget(deviceId) {
|
|
83885
84081
|
this.drop(deviceId);
|
|
83886
84082
|
}
|
|
@@ -83891,6 +84087,20 @@ function sameFeatures(a, b) {
|
|
|
83891
84087
|
const held = new Set(a);
|
|
83892
84088
|
return b.every((f) => held.has(f));
|
|
83893
84089
|
}
|
|
84090
|
+
function rowOf(deviceId, raw) {
|
|
84091
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
84092
|
+
const features = Reflect.get(raw, "features");
|
|
84093
|
+
if (!Array.isArray(features)) return null;
|
|
84094
|
+
const kept = features.filter((f) => typeof f === "string");
|
|
84095
|
+
if (kept.length === 0) return null;
|
|
84096
|
+
const updatedAt = Number(Reflect.get(raw, "updatedAt"));
|
|
84097
|
+
return {
|
|
84098
|
+
deviceId,
|
|
84099
|
+
features: kept,
|
|
84100
|
+
updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
|
|
84101
|
+
restored: true
|
|
84102
|
+
};
|
|
84103
|
+
}
|
|
83894
84104
|
//#endregion
|
|
83895
84105
|
//#region src/onboard-forwarding-reconcile.ts
|
|
83896
84106
|
/** The `detectionSources` member that means "this camera's own firmware". */
|
|
@@ -84049,9 +84259,10 @@ var DetectionWiringController = class {
|
|
|
84049
84259
|
featuresMirror;
|
|
84050
84260
|
constructor(deps) {
|
|
84051
84261
|
this.deps = deps;
|
|
84262
|
+
const settings = deps.persistFeatures === true ? deps.ctx().settings : void 0;
|
|
84052
84263
|
this.featuresMirror = new DeviceFeaturesMirror({
|
|
84053
84264
|
logger: deps.logger,
|
|
84054
|
-
...
|
|
84265
|
+
...settings !== void 0 ? { settings } : {}
|
|
84055
84266
|
});
|
|
84056
84267
|
}
|
|
84057
84268
|
/**
|
|
@@ -84060,8 +84271,7 @@ var DetectionWiringController = class {
|
|
|
84060
84271
|
* that would have needed it.
|
|
84061
84272
|
*/
|
|
84062
84273
|
async hydrateFeaturesMirror() {
|
|
84063
|
-
if (this.deps.
|
|
84064
|
-
await DeviceFeaturesMirror.declare(this.deps.featuresStore);
|
|
84274
|
+
if (this.deps.persistFeatures !== true) return;
|
|
84065
84275
|
await this.featuresMirror.hydrate();
|
|
84066
84276
|
}
|
|
84067
84277
|
/** `activeDetections.get(deviceId)`. */
|
|
@@ -87442,25 +87652,33 @@ var NodeStressController = class NodeStressController {
|
|
|
87442
87652
|
//#region src/durable/lts-aggregator.ts
|
|
87443
87653
|
/** Wall-clock bucket width. Five minutes, matching HA's statistics tier. */
|
|
87444
87654
|
var LTS_BUCKET_MS = 5 * 6e4;
|
|
87655
|
+
/** Buckets written per transaction — bounds one statement, not the flush. */
|
|
87656
|
+
var FLUSH_BATCH = 200;
|
|
87445
87657
|
/** How often the row cap is enforced. Rarely: it is a bound, not a deadline. */
|
|
87446
87658
|
var CAP_SWEEP_INTERVAL_MS = 6 * 36e5;
|
|
87659
|
+
/**
|
|
87660
|
+
* The row's identity IS its four key columns (D871): a composite
|
|
87661
|
+
* `primaryKey` + `WITHOUT ROWID`, so the table is its own index and there is no
|
|
87662
|
+
* TEXT `id` repeating what the row already says. The old layout kept
|
|
87663
|
+
* `<subject>|<series>|<scope>|<bucketStart>` as a TEXT primary key beside the
|
|
87664
|
+
* four columns — 85 MB of rows plus a 44 MB autoindex for 1.07 M rows. A
|
|
87665
|
+
* composite-key collection is written with `upsertMany` (a bucket flushed twice
|
|
87666
|
+
* replaces itself) and read with `query` / `count` / `deleteWhere`; the calls
|
|
87667
|
+
* that address one row by id are refused by the engine.
|
|
87668
|
+
*/
|
|
87669
|
+
var LTS_KEY_COLUMNS = [
|
|
87670
|
+
"subject",
|
|
87671
|
+
"series",
|
|
87672
|
+
"scope",
|
|
87673
|
+
"bucketStart"
|
|
87674
|
+
];
|
|
87447
87675
|
var LTS_COLUMNS = [
|
|
87448
|
-
(
|
|
87449
|
-
/** `<subject>|<series>|<scope>|<bucketStart>` — deterministic, so a bucket
|
|
87450
|
-
* flushed twice replaces itself rather than doubling. */
|
|
87451
|
-
{
|
|
87452
|
-
name: "id",
|
|
87453
|
-
type: "TEXT",
|
|
87454
|
-
primaryKey: true,
|
|
87455
|
-
notNull: true
|
|
87456
|
-
}),
|
|
87457
87676
|
(
|
|
87458
87677
|
/** The camera id, or the node id. One column, because every query is
|
|
87459
87678
|
* "this thing over time" and the thing is one or the other. */
|
|
87460
87679
|
{
|
|
87461
87680
|
name: "subject",
|
|
87462
|
-
type: "TEXT"
|
|
87463
|
-
notNull: true
|
|
87681
|
+
type: "TEXT"
|
|
87464
87682
|
}),
|
|
87465
87683
|
(
|
|
87466
87684
|
/** Numeric mirror of `subject` for camera rows, so a per-camera question is
|
|
@@ -87472,22 +87690,19 @@ var LTS_COLUMNS = [
|
|
|
87472
87690
|
}),
|
|
87473
87691
|
{
|
|
87474
87692
|
name: "series",
|
|
87475
|
-
type: "TEXT"
|
|
87476
|
-
notNull: true
|
|
87693
|
+
type: "TEXT"
|
|
87477
87694
|
},
|
|
87478
87695
|
(
|
|
87479
87696
|
/** Sub-scope within a series: a zoneId for occupancy, `''` otherwise. */
|
|
87480
87697
|
{
|
|
87481
87698
|
name: "scope",
|
|
87482
|
-
type: "TEXT"
|
|
87483
|
-
notNull: true
|
|
87699
|
+
type: "TEXT"
|
|
87484
87700
|
}),
|
|
87485
87701
|
(
|
|
87486
87702
|
/** Bucket start, wall-clock aligned to {@link LTS_BUCKET_MS}. */
|
|
87487
87703
|
{
|
|
87488
87704
|
name: "bucketStart",
|
|
87489
|
-
type: "INTEGER"
|
|
87490
|
-
notNull: true
|
|
87705
|
+
type: "INTEGER"
|
|
87491
87706
|
}),
|
|
87492
87707
|
{
|
|
87493
87708
|
name: "samples",
|
|
@@ -87510,12 +87725,15 @@ var LTS_COLUMNS = [
|
|
|
87510
87725
|
notNull: true
|
|
87511
87726
|
}
|
|
87512
87727
|
];
|
|
87728
|
+
/**
|
|
87729
|
+
* The primary key serves every per-subject read (`subject, series, scope` then a
|
|
87730
|
+
* `bucketStart` range, already in bucket order); this index serves the read that
|
|
87731
|
+
* names a series without a subject. The old `deviceId` index went with the TEXT
|
|
87732
|
+
* id: nothing queries a series by the numeric mirror.
|
|
87733
|
+
*/
|
|
87513
87734
|
var LTS_INDEXES = [{
|
|
87514
87735
|
name: "idx_lts_series_bucket",
|
|
87515
87736
|
columns: ["series", "bucketStart"]
|
|
87516
|
-
}, {
|
|
87517
|
-
name: "idx_lts_device",
|
|
87518
|
-
columns: ["deviceId"]
|
|
87519
87737
|
}];
|
|
87520
87738
|
function ltsBucketStart(at, bucketMs = LTS_BUCKET_MS) {
|
|
87521
87739
|
return Math.floor(at / bucketMs) * bucketMs;
|
|
@@ -87544,6 +87762,8 @@ var LtsAggregator = class {
|
|
|
87544
87762
|
return store.declareCollection.mutate({
|
|
87545
87763
|
collection,
|
|
87546
87764
|
columns: [...LTS_COLUMNS],
|
|
87765
|
+
primaryKey: [...LTS_KEY_COLUMNS],
|
|
87766
|
+
withoutRowid: true,
|
|
87547
87767
|
indexes: [...LTS_INDEXES]
|
|
87548
87768
|
});
|
|
87549
87769
|
}
|
|
@@ -87603,43 +87823,60 @@ var LtsAggregator = class {
|
|
|
87603
87823
|
return 0;
|
|
87604
87824
|
}
|
|
87605
87825
|
let written = 0;
|
|
87606
|
-
for (const
|
|
87607
|
-
|
|
87608
|
-
|
|
87609
|
-
await this.store.set.mutate({
|
|
87610
|
-
collection: this.collection,
|
|
87611
|
-
key,
|
|
87612
|
-
value: {
|
|
87613
|
-
subject: bucket.subject,
|
|
87614
|
-
...bucket.deviceId !== void 0 ? { deviceId: bucket.deviceId } : {},
|
|
87615
|
-
series: bucket.series,
|
|
87616
|
-
scope: bucket.scope,
|
|
87617
|
-
bucketStart: bucket.bucketStart,
|
|
87618
|
-
samples: bucket.samples,
|
|
87619
|
-
sum: bucket.sum,
|
|
87620
|
-
min: bucket.min,
|
|
87621
|
-
max: bucket.max
|
|
87622
|
-
}
|
|
87623
|
-
});
|
|
87624
|
-
written += 1;
|
|
87625
|
-
this.groups.add(groupKey(bucket.subject, bucket.series, bucket.scope));
|
|
87626
|
-
} catch (err) {
|
|
87627
|
-
this.logger.warn("lts bucket write failed — this interval will be missing", {
|
|
87628
|
-
...bucket.deviceId !== void 0 ? { tags: { deviceId: bucket.deviceId } } : {},
|
|
87629
|
-
meta: {
|
|
87630
|
-
collection: this.collection,
|
|
87631
|
-
series: bucket.series,
|
|
87632
|
-
subject: bucket.subject,
|
|
87633
|
-
bucketStart: bucket.bucketStart,
|
|
87634
|
-
error: String(err)
|
|
87635
|
-
}
|
|
87636
|
-
});
|
|
87637
|
-
}
|
|
87638
|
-
this.open.delete(key);
|
|
87826
|
+
for (const batch of chunk(due, FLUSH_BATCH)) {
|
|
87827
|
+
written += await this.writeBatch(batch);
|
|
87828
|
+
for (const bucket of batch) this.open.delete(rowId(bucket.subject, bucket.series, bucket.scope, bucket.bucketStart));
|
|
87639
87829
|
}
|
|
87640
87830
|
await this.maybeSweepCap(now);
|
|
87641
87831
|
return written;
|
|
87642
87832
|
}
|
|
87833
|
+
/**
|
|
87834
|
+
* Upsert one batch in a single transaction. If the batch is refused as a
|
|
87835
|
+
* whole (all-or-nothing), each bucket is retried alone so one bad row costs
|
|
87836
|
+
* one interval, not forty. Returns the number of rows written.
|
|
87837
|
+
*/
|
|
87838
|
+
async writeBatch(batch) {
|
|
87839
|
+
try {
|
|
87840
|
+
await this.upsert(batch);
|
|
87841
|
+
for (const bucket of batch) this.groups.add(groupKey(bucket.subject, bucket.series, bucket.scope));
|
|
87842
|
+
return batch.length;
|
|
87843
|
+
} catch (err) {
|
|
87844
|
+
if (batch.length > 1) {
|
|
87845
|
+
let written = 0;
|
|
87846
|
+
for (const bucket of batch) written += await this.writeBatch([bucket]);
|
|
87847
|
+
return written;
|
|
87848
|
+
}
|
|
87849
|
+
const bucket = batch[0];
|
|
87850
|
+
this.logger.warn("lts bucket write failed — this interval will be missing", {
|
|
87851
|
+
...bucket?.deviceId !== void 0 ? { tags: { deviceId: bucket.deviceId } } : {},
|
|
87852
|
+
meta: {
|
|
87853
|
+
collection: this.collection,
|
|
87854
|
+
series: bucket?.series,
|
|
87855
|
+
subject: bucket?.subject,
|
|
87856
|
+
bucketStart: bucket?.bucketStart,
|
|
87857
|
+
error: String(err)
|
|
87858
|
+
}
|
|
87859
|
+
});
|
|
87860
|
+
return 0;
|
|
87861
|
+
}
|
|
87862
|
+
}
|
|
87863
|
+
async upsert(batch) {
|
|
87864
|
+
await this.store.upsertMany.mutate({
|
|
87865
|
+
collection: this.collection,
|
|
87866
|
+
conflict: [...LTS_KEY_COLUMNS],
|
|
87867
|
+
records: batch.map((bucket) => ({ data: {
|
|
87868
|
+
subject: bucket.subject,
|
|
87869
|
+
deviceId: bucket.deviceId ?? null,
|
|
87870
|
+
series: bucket.series,
|
|
87871
|
+
scope: bucket.scope,
|
|
87872
|
+
bucketStart: bucket.bucketStart,
|
|
87873
|
+
samples: bucket.samples,
|
|
87874
|
+
sum: bucket.sum,
|
|
87875
|
+
min: bucket.min,
|
|
87876
|
+
max: bucket.max
|
|
87877
|
+
} }))
|
|
87878
|
+
});
|
|
87879
|
+
}
|
|
87643
87880
|
/** Read closed buckets back. The read surface every chart will use. */
|
|
87644
87881
|
async read(query = {}) {
|
|
87645
87882
|
const where = {};
|
|
@@ -87724,6 +87961,11 @@ var LtsAggregator = class {
|
|
|
87724
87961
|
}
|
|
87725
87962
|
}
|
|
87726
87963
|
};
|
|
87964
|
+
function chunk(items, size) {
|
|
87965
|
+
const out = [];
|
|
87966
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
87967
|
+
return out;
|
|
87968
|
+
}
|
|
87727
87969
|
function groupKey(subject, series, scope) {
|
|
87728
87970
|
return `${subject}${series}${scope}`;
|
|
87729
87971
|
}
|
|
@@ -87782,7 +88024,7 @@ function recordToRow(data) {
|
|
|
87782
88024
|
* write="one row per (node, series, 5-min bucket), written ONCE when the bucket closes; a node reporting no samples writes nothing"
|
|
87783
88025
|
* retention="row CAP per (subject, series, scope) — 105,120 rows ≈ one year of 5-minute buckets. Not an age sweep: an LTS row summarises an interval and has no owner to be orphaned from."
|
|
87784
88026
|
*/
|
|
87785
|
-
var NODE_STRESS_LTS_COLLECTION = "pipeline-orchestrator:stats-5m";
|
|
88027
|
+
var NODE_STRESS_LTS_COLLECTION = "pipeline-orchestrator:stats-5m-v2";
|
|
87786
88028
|
var NodeStressLts = class {
|
|
87787
88029
|
lts;
|
|
87788
88030
|
constructor(deps) {
|
|
@@ -94359,7 +94601,7 @@ async function buildOrchestratorControllers(deps) {
|
|
|
94359
94601
|
reconcile.scheduleReconcile();
|
|
94360
94602
|
const detectionWiring = new DetectionWiringController({
|
|
94361
94603
|
ctx: () => deps.ctx(),
|
|
94362
|
-
|
|
94604
|
+
persistFeatures: true,
|
|
94363
94605
|
ledger,
|
|
94364
94606
|
placement,
|
|
94365
94607
|
audio,
|
|
@@ -94579,6 +94821,24 @@ function buildGlobalSettingsSections(options) {
|
|
|
94579
94821
|
default: DEFAULT_DETAIL_CROP_CONVENTION.square
|
|
94580
94822
|
}]
|
|
94581
94823
|
},
|
|
94824
|
+
{
|
|
94825
|
+
id: FACE_RESCORE_SECTION_ID,
|
|
94826
|
+
title: "Face suggestions",
|
|
94827
|
+
tab: "pipeline",
|
|
94828
|
+
description: "How often the people suggestions on unassigned faces are refreshed against the current gallery. The job only runs when the gallery or the set of unassigned faces changed since the last run; otherwise it does nothing.",
|
|
94829
|
+
fields: [{
|
|
94830
|
+
key: FACE_RESCORE_INTERVAL_KEY,
|
|
94831
|
+
type: "slider",
|
|
94832
|
+
label: "Suggestion refresh interval",
|
|
94833
|
+
description: "0 = off (use the Recompute suggestions button). Values from 1 to 9 run every 10 minutes. The first run happens one interval after the addon starts.",
|
|
94834
|
+
min: FACE_RESCORE_INTERVAL_FIELD.min,
|
|
94835
|
+
max: FACE_RESCORE_INTERVAL_FIELD.max,
|
|
94836
|
+
step: FACE_RESCORE_INTERVAL_FIELD.step,
|
|
94837
|
+
default: FACE_RESCORE_INTERVAL_FIELD.default,
|
|
94838
|
+
showValue: true,
|
|
94839
|
+
unit: "min"
|
|
94840
|
+
}]
|
|
94841
|
+
},
|
|
94582
94842
|
{
|
|
94583
94843
|
id: NATIVE_LEASE_SECTION_ID,
|
|
94584
94844
|
title: "Native frame lease",
|