@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.js
CHANGED
|
@@ -5418,7 +5418,7 @@ var ZodIssueCode = {
|
|
|
5418
5418
|
var ZodFirstPartyTypeKind;
|
|
5419
5419
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5420
5420
|
//#endregion
|
|
5421
|
-
//#region ../types/dist/sleep-
|
|
5421
|
+
//#region ../types/dist/sleep-DRqxtc0C.mjs
|
|
5422
5422
|
/**
|
|
5423
5423
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5424
5424
|
* window to float samples (D455).
|
|
@@ -8758,6 +8758,10 @@ method(object({ deviceId: number() }), array(StreamSourceEntrySchema)), method(o
|
|
|
8758
8758
|
kind: "mutation",
|
|
8759
8759
|
auth: "admin"
|
|
8760
8760
|
}), method(object({ deviceId: number() }), unknown().nullable()), method(object({ deviceId: number() }), RawStateResultSchema.nullable(), { auth: "protected" });
|
|
8761
|
+
object({
|
|
8762
|
+
code: string().regex(/^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/),
|
|
8763
|
+
params: record(string(), union([string(), number()])).optional()
|
|
8764
|
+
});
|
|
8761
8765
|
/**
|
|
8762
8766
|
* Promise-based timer helpers — used everywhere the codebase needs to
|
|
8763
8767
|
* wait, back off, or schedule a retry. Before these helpers landed, each
|
|
@@ -8783,7 +8787,7 @@ function sleep(ms) {
|
|
|
8783
8787
|
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
8784
8788
|
}
|
|
8785
8789
|
//#endregion
|
|
8786
|
-
//#region ../types/dist/err-msg-
|
|
8790
|
+
//#region ../types/dist/err-msg-DLerKiVO.mjs
|
|
8787
8791
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
8788
8792
|
DeviceType["Camera"] = "camera";
|
|
8789
8793
|
DeviceType["Hub"] = "hub";
|
|
@@ -9033,6 +9037,22 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
9033
9037
|
DeviceRole["Nightvision"] = "nightvision";
|
|
9034
9038
|
DeviceRole["PrivacyMask"] = "privacy-mask";
|
|
9035
9039
|
DeviceRole["Doorbell"] = "doorbell";
|
|
9040
|
+
/** Interior blind / shade / roller blind. HA `device_class: blind|shade`. */
|
|
9041
|
+
DeviceRole["CoverBlind"] = "cover-blind";
|
|
9042
|
+
/** Exterior roller shutter. HA `device_class: shutter`; Homematic BROLL. */
|
|
9043
|
+
DeviceRole["CoverShutter"] = "cover-shutter";
|
|
9044
|
+
/** Exterior awning. HA `device_class: awning`. */
|
|
9045
|
+
DeviceRole["CoverAwning"] = "cover-awning";
|
|
9046
|
+
/** Curtain / drape. HA `device_class: curtain`. */
|
|
9047
|
+
DeviceRole["CoverCurtain"] = "cover-curtain";
|
|
9048
|
+
/** Garage door. HA `device_class: garage`. A security actuator: exports gate it. */
|
|
9049
|
+
DeviceRole["CoverGarage"] = "cover-garage";
|
|
9050
|
+
/** Gate. HA `device_class: gate`. A security actuator: exports gate it. */
|
|
9051
|
+
DeviceRole["CoverGate"] = "cover-gate";
|
|
9052
|
+
/** Door. HA `device_class: door`. A security actuator: exports gate it. */
|
|
9053
|
+
DeviceRole["CoverDoor"] = "cover-door";
|
|
9054
|
+
/** Window. HA `device_class: window`. */
|
|
9055
|
+
DeviceRole["CoverWindow"] = "cover-window";
|
|
9036
9056
|
/** Virtual HA toggle (input_boolean.*) — distinguishable from a
|
|
9037
9057
|
* real Switch device for UI rendering / export adapters. */
|
|
9038
9058
|
DeviceRole["BinaryHelper"] = "binary-helper";
|
|
@@ -9113,7 +9133,7 @@ function errMsg$2(err) {
|
|
|
9113
9133
|
return String(err);
|
|
9114
9134
|
}
|
|
9115
9135
|
//#endregion
|
|
9116
|
-
//#region ../types/dist/composition-
|
|
9136
|
+
//#region ../types/dist/composition-EQ9Ok0Ee.mjs
|
|
9117
9137
|
/**
|
|
9118
9138
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
9119
9139
|
*
|
|
@@ -12778,11 +12798,10 @@ var ClipModelMetaSchema = object({
|
|
|
12778
12798
|
/**
|
|
12779
12799
|
* The `vector-store` index this model's vectors live in. ONE feature space
|
|
12780
12800
|
* per index: two models share an index only when their vectors are ranked
|
|
12781
|
-
* against each other, which for CLIP means never across families.
|
|
12782
|
-
*
|
|
12783
|
-
* `modelId`); any other model gets its own.
|
|
12801
|
+
* against each other, which for CLIP means never across families. Every
|
|
12802
|
+
* model gets its own.
|
|
12784
12803
|
*/
|
|
12785
|
-
vectorIndex: string().regex(/^object-clip
|
|
12804
|
+
vectorIndex: string().regex(/^object-clip-[a-z0-9][a-z0-9-]*$/),
|
|
12786
12805
|
/**
|
|
12787
12806
|
* The text encoder's tokenizer, a declared SIBLING file of its onnx. The name
|
|
12788
12807
|
* must be unique per tokenizer: every model's siblings land in ONE flat
|
|
@@ -13251,7 +13270,8 @@ var ConvertResultSchema = object({
|
|
|
13251
13270
|
})).readonly()
|
|
13252
13271
|
});
|
|
13253
13272
|
_enum(["new-session", "step-up"]);
|
|
13254
|
-
|
|
13273
|
+
/** The languages the hub's app landing pages are written in (D858). */
|
|
13274
|
+
var AppPageLangSchema = _enum(["en", "it"]);
|
|
13255
13275
|
/** How a native app starts one login method (D858), as `auth.listLoginMethods` reports it. */
|
|
13256
13276
|
var AppLoginPathSchema = discriminatedUnion("kind", [object({ kind: literal("native") }), object({
|
|
13257
13277
|
kind: literal("browser"),
|
|
@@ -13748,7 +13768,9 @@ var ConfigTabDeclarationSchema = object({
|
|
|
13748
13768
|
var ConfigSectionWithValuesSchema = object({
|
|
13749
13769
|
id: string(),
|
|
13750
13770
|
title: string(),
|
|
13771
|
+
titleKey: string().optional(),
|
|
13751
13772
|
description: string().optional(),
|
|
13773
|
+
descriptionKey: string().optional(),
|
|
13752
13774
|
style: _enum(["card", "accordion"]).optional(),
|
|
13753
13775
|
defaultCollapsed: boolean().optional(),
|
|
13754
13776
|
columns: union([
|
|
@@ -17031,6 +17053,9 @@ method(object({
|
|
|
17031
17053
|
}), method(object({ refs: RefListSchema }), KeychainResolveResultSchema, { auth: "admin" }), method(object({ refs: RefListSchema }), object({ deleted: number().int() }), {
|
|
17032
17054
|
kind: "mutation",
|
|
17033
17055
|
auth: "admin"
|
|
17056
|
+
}), method(object({ deviceId: number().int() }), object({ refs: array(string()).readonly() }), {
|
|
17057
|
+
kind: "mutation",
|
|
17058
|
+
auth: "admin"
|
|
17034
17059
|
}), method(object({}), array(KeychainSecretMetaSchema).readonly(), { auth: "admin" }), method(object({}), KeychainStateSchema, { auth: "admin" });
|
|
17035
17060
|
/**
|
|
17036
17061
|
* device-history — the ONE mechanism for numeric history (D850).
|
|
@@ -38434,6 +38459,38 @@ var DeviceRegisterPayloadSchema = object({
|
|
|
38434
38459
|
* omits it and the keyword backstop applies. */
|
|
38435
38460
|
secretConfigPaths: array(string()).optional()
|
|
38436
38461
|
});
|
|
38462
|
+
/** One deleted device, kept by device-manager so an addon that missed the
|
|
38463
|
+
* deletion can still learn of it (D865). Written by the delete path ONLY. */
|
|
38464
|
+
var DeviceTombstoneSchema = object({
|
|
38465
|
+
deviceId: number().int().positive(),
|
|
38466
|
+
/** Epoch ms the device was deleted. */
|
|
38467
|
+
deletedAt: number(),
|
|
38468
|
+
/** The addon that owned the device when it was deleted. */
|
|
38469
|
+
providerAddonId: string()
|
|
38470
|
+
});
|
|
38471
|
+
/** Data held for one device that device-manager no longer knows. */
|
|
38472
|
+
var OrphanDeviceDataSchema = object({
|
|
38473
|
+
deviceId: number().int().positive(),
|
|
38474
|
+
/** Rows/files per store (`<addonId>:<store>`). */
|
|
38475
|
+
stores: record(string(), number().int().nonnegative())
|
|
38476
|
+
});
|
|
38477
|
+
var CleanupDeletedDevicesResultSchema = object({
|
|
38478
|
+
/**
|
|
38479
|
+
* `skipped` — the absence check could not be trusted (a device-list read
|
|
38480
|
+
* failed or the two reads disagreed); nothing was listed or deleted.
|
|
38481
|
+
* `dry-run` — `devices` is what a confirmed run would delete.
|
|
38482
|
+
* `cleaned` — `devices` is what was deleted.
|
|
38483
|
+
*/
|
|
38484
|
+
status: _enum([
|
|
38485
|
+
"skipped",
|
|
38486
|
+
"dry-run",
|
|
38487
|
+
"cleaned"
|
|
38488
|
+
]),
|
|
38489
|
+
reason: string().nullable(),
|
|
38490
|
+
devices: array(OrphanDeviceDataSchema),
|
|
38491
|
+
/** Implementers that could not be asked (their stores are not in `devices`). */
|
|
38492
|
+
unreachable: array(string())
|
|
38493
|
+
});
|
|
38437
38494
|
var DeviceRemovePayloadSchema = object({ deviceId: number() });
|
|
38438
38495
|
var DevicePersistConfigPayloadSchema = object({
|
|
38439
38496
|
deviceId: number(),
|
|
@@ -38672,6 +38729,9 @@ method(object({
|
|
|
38672
38729
|
}), method(object({ deviceId: number() }), object({ success: literal(true) }), {
|
|
38673
38730
|
kind: "mutation",
|
|
38674
38731
|
auth: "admin"
|
|
38732
|
+
}), method(object({ sinceMs: number() }), array(DeviceTombstoneSchema).readonly(), { auth: "admin" }), method(object({ confirm: boolean() }), CleanupDeletedDevicesResultSchema, {
|
|
38733
|
+
kind: "mutation",
|
|
38734
|
+
auth: "admin"
|
|
38675
38735
|
}), method(object({ integrationId: string() }), object({ removed: number() }), {
|
|
38676
38736
|
kind: "mutation",
|
|
38677
38737
|
auth: "admin"
|
|
@@ -39134,6 +39194,72 @@ method(object({
|
|
|
39134
39194
|
auth: "admin"
|
|
39135
39195
|
});
|
|
39136
39196
|
/**
|
|
39197
|
+
* `device-removal-hook` — the reliable way an addon learns a device was DELETED,
|
|
39198
|
+
* so it can delete what it keeps for that device (D865).
|
|
39199
|
+
*
|
|
39200
|
+
* ## Why an event was not enough
|
|
39201
|
+
*
|
|
39202
|
+
* `DeviceUnregistered` is telemetry and may be dropped (D8, D11). An addon that
|
|
39203
|
+
* hung its per-device clean-up on it left durable garbage whenever the event was
|
|
39204
|
+
* lost, the addon was down, or it was restarting at that moment — footage,
|
|
39205
|
+
* analytics rows and settings of device 4483 outlived the device. Loss of this
|
|
39206
|
+
* signal corrupts durable state, so it travels by RPC with a recorded fact
|
|
39207
|
+
* behind it (D8: "if loss would corrupt durable state, use RPC plus a reconcile").
|
|
39208
|
+
*
|
|
39209
|
+
* ## The three pieces
|
|
39210
|
+
*
|
|
39211
|
+
* 1. device-manager records a TOMBSTONE per deleted device
|
|
39212
|
+
* (`deviceManager.listDeletedSince`). The tombstone is written by the delete
|
|
39213
|
+
* path only — a device that is unregistered, offline or whose provider is
|
|
39214
|
+
* restarting is NOT deleted and gets none.
|
|
39215
|
+
* 2. After a delete, device-manager calls `onDeviceRemoved` on EVERY provider of
|
|
39216
|
+
* this collection, one by one, with bounded retries, logged.
|
|
39217
|
+
* 3. An implementer that was down asks `deviceManager.listDeletedSince` at start
|
|
39218
|
+
* (and periodically) and runs its own `onDeviceRemoved` for each tombstone it
|
|
39219
|
+
* may have missed. Reconcile is driven by tombstones, never by "the device is
|
|
39220
|
+
* not in the current list": a failed or partial list read must not destroy data.
|
|
39221
|
+
*
|
|
39222
|
+
* ## Contract for an implementer
|
|
39223
|
+
*
|
|
39224
|
+
* - **Idempotent.** It is called again by a retry, by a reconcile and by the
|
|
39225
|
+
* admin cleanup of data that predates tombstones. A device with nothing left
|
|
39226
|
+
* answers zero counts.
|
|
39227
|
+
* - **One key per store** in `counts`, deleted rows/files per store, so the log
|
|
39228
|
+
* line and the admin dry run can say where the data was.
|
|
39229
|
+
* - **Training datasets are NOT device data** and are never deleted by a hook.
|
|
39230
|
+
* - **Per-device SETTINGS are not hooked.** Configuration lives in the standard
|
|
39231
|
+
* per-device store (`ctx.settings.*DeviceStore`), which device-manager clears
|
|
39232
|
+
* for every addon centrally at delete time. This hook is for device DATA
|
|
39233
|
+
* that is not settings: footage, event media, analytics rows, history.
|
|
39234
|
+
* - A provider that cannot tell is allowed to throw; the caller retries it.
|
|
39235
|
+
*
|
|
39236
|
+
* `listDeviceData` is the read side of the admin "Clean data of deleted devices"
|
|
39237
|
+
* action: which device ids this addon holds data for, and how much per store. It
|
|
39238
|
+
* never deletes. device-manager decides which of those ids are absent from the
|
|
39239
|
+
* fleet, from TWO consistent reads of the device list (D49).
|
|
39240
|
+
*/
|
|
39241
|
+
/** What a hook is told about the deletion. */
|
|
39242
|
+
var DeviceRemovedInputSchema = object({
|
|
39243
|
+
deviceId: number().int().positive(),
|
|
39244
|
+
/** Epoch ms the device was deleted (the tombstone's `deletedAt`). */
|
|
39245
|
+
deletedAt: number(),
|
|
39246
|
+
/** The addon that owned the device, as device-manager last knew it. */
|
|
39247
|
+
providerAddonId: string()
|
|
39248
|
+
});
|
|
39249
|
+
/** Rows/files/refs deleted, per store this implementer owns. */
|
|
39250
|
+
var DeviceRemovedResultSchema = object({ counts: record(string(), number().int().nonnegative()) });
|
|
39251
|
+
/** One (store, device) pair an implementer holds data for. */
|
|
39252
|
+
var DeviceDataEntrySchema = object({
|
|
39253
|
+
store: string(),
|
|
39254
|
+
deviceId: number().int().positive(),
|
|
39255
|
+
/** Rows (or files) held for the device in the store. */
|
|
39256
|
+
count: number().int().nonnegative()
|
|
39257
|
+
});
|
|
39258
|
+
method(DeviceRemovedInputSchema, DeviceRemovedResultSchema, {
|
|
39259
|
+
kind: "mutation",
|
|
39260
|
+
auth: "admin"
|
|
39261
|
+
}), method(_void(), array(DeviceDataEntrySchema).readonly(), { auth: "admin" });
|
|
39262
|
+
/**
|
|
39137
39263
|
* `failure-contribution` — the capability an addon reports its OWN losses
|
|
39138
39264
|
* through, per camera, with the denominator attached. It stores nothing.
|
|
39139
39265
|
*
|
|
@@ -56603,7 +56729,19 @@ var TlsStatusSchema = object({
|
|
|
56603
56729
|
sans: array(string()),
|
|
56604
56730
|
caCertPem: string().nullable(),
|
|
56605
56731
|
reissueError: string().nullable(),
|
|
56606
|
-
restartRequired: boolean()
|
|
56732
|
+
restartRequired: boolean(),
|
|
56733
|
+
/** An uploaded certificate is served but no CA is kept for it and it is not publicly signed (D873). */
|
|
56734
|
+
caMissing: boolean(),
|
|
56735
|
+
/** Does the hub's own generated CA carry a critical name constraint? `null` while it has none. */
|
|
56736
|
+
localCaConstrained: boolean().nullable(),
|
|
56737
|
+
/** ISO time the generated CA was replaced by a constrained one; devices must re-download it. */
|
|
56738
|
+
caReplacedAt: string().nullable(),
|
|
56739
|
+
/** The uploaded certificate kept on disk, selectable again after "Revert to generated". */
|
|
56740
|
+
storedUpload: object({
|
|
56741
|
+
leafFingerprintSha256: string(),
|
|
56742
|
+
validTo: string(),
|
|
56743
|
+
hasCa: boolean()
|
|
56744
|
+
}).nullable()
|
|
56607
56745
|
});
|
|
56608
56746
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
56609
56747
|
/**
|
|
@@ -56659,6 +56797,9 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
56659
56797
|
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
56660
56798
|
kind: "mutation",
|
|
56661
56799
|
auth: "admin"
|
|
56800
|
+
}), method(_void(), TlsStatusSchema, {
|
|
56801
|
+
kind: "mutation",
|
|
56802
|
+
auth: "admin"
|
|
56662
56803
|
});
|
|
56663
56804
|
object({
|
|
56664
56805
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
@@ -64702,6 +64843,12 @@ Object.freeze({
|
|
|
64702
64843
|
addonId: null,
|
|
64703
64844
|
access: "create"
|
|
64704
64845
|
},
|
|
64846
|
+
"deviceManager.cleanupDeletedDevicesData": {
|
|
64847
|
+
capName: "device-manager",
|
|
64848
|
+
capScope: "system",
|
|
64849
|
+
addonId: null,
|
|
64850
|
+
access: "create"
|
|
64851
|
+
},
|
|
64705
64852
|
"deviceManager.createDevice": {
|
|
64706
64853
|
capName: "device-manager",
|
|
64707
64854
|
capScope: "system",
|
|
@@ -64882,6 +65029,12 @@ Object.freeze({
|
|
|
64882
65029
|
addonId: null,
|
|
64883
65030
|
access: "view"
|
|
64884
65031
|
},
|
|
65032
|
+
"deviceManager.listDeletedSince": {
|
|
65033
|
+
capName: "device-manager",
|
|
65034
|
+
capScope: "system",
|
|
65035
|
+
addonId: null,
|
|
65036
|
+
access: "view"
|
|
65037
|
+
},
|
|
64885
65038
|
"deviceManager.listDeviceIdsByCaps": {
|
|
64886
65039
|
capName: "device-manager",
|
|
64887
65040
|
capScope: "system",
|
|
@@ -65290,6 +65443,18 @@ Object.freeze({
|
|
|
65290
65443
|
addonId: null,
|
|
65291
65444
|
access: "create"
|
|
65292
65445
|
},
|
|
65446
|
+
"deviceRemovalHook.listDeviceData": {
|
|
65447
|
+
capName: "device-removal-hook",
|
|
65448
|
+
capScope: "system",
|
|
65449
|
+
addonId: null,
|
|
65450
|
+
access: "view"
|
|
65451
|
+
},
|
|
65452
|
+
"deviceRemovalHook.onDeviceRemoved": {
|
|
65453
|
+
capName: "device-removal-hook",
|
|
65454
|
+
capScope: "system",
|
|
65455
|
+
addonId: null,
|
|
65456
|
+
access: "create"
|
|
65457
|
+
},
|
|
65293
65458
|
"deviceState.claimFields": {
|
|
65294
65459
|
capName: "device-state",
|
|
65295
65460
|
capScope: "system",
|
|
@@ -65722,6 +65887,12 @@ Object.freeze({
|
|
|
65722
65887
|
addonId: null,
|
|
65723
65888
|
access: "delete"
|
|
65724
65889
|
},
|
|
65890
|
+
"keychain.deleteDeviceScope": {
|
|
65891
|
+
capName: "keychain",
|
|
65892
|
+
capScope: "system",
|
|
65893
|
+
addonId: null,
|
|
65894
|
+
access: "delete"
|
|
65895
|
+
},
|
|
65725
65896
|
"keychain.getState": {
|
|
65726
65897
|
capName: "keychain",
|
|
65727
65898
|
capScope: "system",
|
|
@@ -66160,6 +66331,12 @@ Object.freeze({
|
|
|
66160
66331
|
addonId: null,
|
|
66161
66332
|
access: "create"
|
|
66162
66333
|
},
|
|
66334
|
+
"localNetwork.useStoredUploadedCertificate": {
|
|
66335
|
+
capName: "local-network",
|
|
66336
|
+
capScope: "system",
|
|
66337
|
+
addonId: null,
|
|
66338
|
+
access: "create"
|
|
66339
|
+
},
|
|
66163
66340
|
"lockControl.getStatus": {
|
|
66164
66341
|
capName: "lock-control",
|
|
66165
66342
|
capScope: "device",
|
|
@@ -71762,6 +71939,11 @@ Object.freeze({
|
|
|
71762
71939
|
form: "single",
|
|
71763
71940
|
optional: false
|
|
71764
71941
|
}],
|
|
71942
|
+
"deviceRemovalHook.onDeviceRemoved": [{
|
|
71943
|
+
name: "deviceId",
|
|
71944
|
+
form: "single",
|
|
71945
|
+
optional: false
|
|
71946
|
+
}],
|
|
71765
71947
|
"deviceState.claimFields": [{
|
|
71766
71948
|
name: "deviceId",
|
|
71767
71949
|
form: "single",
|
|
@@ -71951,6 +72133,11 @@ Object.freeze({
|
|
|
71951
72133
|
form: "single",
|
|
71952
72134
|
optional: false
|
|
71953
72135
|
}],
|
|
72136
|
+
"keychain.deleteDeviceScope": [{
|
|
72137
|
+
name: "deviceId",
|
|
72138
|
+
form: "single",
|
|
72139
|
+
optional: false
|
|
72140
|
+
}],
|
|
71954
72141
|
"lawnMowerControl.dock": [{
|
|
71955
72142
|
name: "deviceId",
|
|
71956
72143
|
form: "single",
|
|
@@ -74299,6 +74486,8 @@ new Set([
|
|
|
74299
74486
|
"audio-tools",
|
|
74300
74487
|
"audio-silence"
|
|
74301
74488
|
]);
|
|
74489
|
+
AppPageLangSchema.options;
|
|
74490
|
+
record(string().min(1), string());
|
|
74302
74491
|
/**
|
|
74303
74492
|
* TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
|
|
74304
74493
|
*
|
|
@@ -74577,7 +74766,11 @@ var REMOVED_MODEL_IDS = {
|
|
|
74577
74766
|
"face-embedding": ["arcface-r100", "inception-resnet-v1"],
|
|
74578
74767
|
"plate-detection": ["yolov8n-plate"],
|
|
74579
74768
|
"plate-ocr": ["vgg-english-g2"],
|
|
74580
|
-
"clip-embedding": [
|
|
74769
|
+
"clip-embedding": [
|
|
74770
|
+
"mobileclip-s0",
|
|
74771
|
+
"mobileclip-s1",
|
|
74772
|
+
"mobileclip-s2"
|
|
74773
|
+
],
|
|
74581
74774
|
"package-detection": ["yolov8n-package"],
|
|
74582
74775
|
"bird-classifier": ["bird-classifier-aiy-edgetpu", "bird-nabirds-404"],
|
|
74583
74776
|
"animal-classifier": ["animals-10"],
|
|
@@ -76000,6 +76193,25 @@ var DETAIL_CROP_PADDING_FIELD = {
|
|
|
76000
76193
|
default: DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio
|
|
76001
76194
|
};
|
|
76002
76195
|
/**
|
|
76196
|
+
* The cadence of the automatic whole-archive face rescore (D866).
|
|
76197
|
+
*
|
|
76198
|
+
* One cluster-wide knob in `pipeline-orchestrator`'s GLOBAL settings, read by
|
|
76199
|
+
* `pipeline-analytics` over the hub-routed `addon-settings` cap on every tick.
|
|
76200
|
+
* It lives here for the same reason the detail-crop convention does: the
|
|
76201
|
+
* writer (orchestrator) and the reader (analytics) are different addons, they
|
|
76202
|
+
* never import each other, and a key copied by hand is how a setting silently
|
|
76203
|
+
* stops arriving.
|
|
76204
|
+
*/
|
|
76205
|
+
var FACE_RESCORE_SECTION_ID = "face-rescore";
|
|
76206
|
+
var FACE_RESCORE_INTERVAL_KEY = "faceRescoreIntervalMinutes";
|
|
76207
|
+
/** Slider bounds. `0` is "disabled"; anything in 1..9 is raised to `min`. */
|
|
76208
|
+
var FACE_RESCORE_INTERVAL_FIELD = {
|
|
76209
|
+
min: 0,
|
|
76210
|
+
max: 1440,
|
|
76211
|
+
step: 5,
|
|
76212
|
+
default: 60
|
|
76213
|
+
};
|
|
76214
|
+
/**
|
|
76003
76215
|
* THE native-frame **lease** knobs — hold depth, RAM budget, demand window and
|
|
76004
76216
|
* subject-tile budget for the decode worker's native-resolution retention.
|
|
76005
76217
|
*
|
|
@@ -76455,6 +76667,22 @@ var DurableLedger = class DurableLedger {
|
|
|
76455
76667
|
}
|
|
76456
76668
|
}
|
|
76457
76669
|
/**
|
|
76670
|
+
* Drop every MIRRORED row about `deviceId` (per the spec's `deviceIdOf`),
|
|
76671
|
+
* leaving the durable rows to the caller's `deleteWhere` (D871). Returns how
|
|
76672
|
+
* many mirror entries went; a spec without `deviceIdOf` evicts nothing.
|
|
76673
|
+
*/
|
|
76674
|
+
evictDevice(deviceId) {
|
|
76675
|
+
const of = this.spec.deviceIdOf;
|
|
76676
|
+
if (of === void 0) return 0;
|
|
76677
|
+
let evicted = 0;
|
|
76678
|
+
for (const [key, row] of [...this.mirror.entries()]) {
|
|
76679
|
+
if (of(row) !== deviceId) continue;
|
|
76680
|
+
this.mirror.delete(key);
|
|
76681
|
+
evicted += 1;
|
|
76682
|
+
}
|
|
76683
|
+
return evicted;
|
|
76684
|
+
}
|
|
76685
|
+
/**
|
|
76458
76686
|
* Drop every mirrored key NOT in `activeKeys`. Returns how many rows went.
|
|
76459
76687
|
*
|
|
76460
76688
|
* **The caller must hold an AUTHORITATIVE active set.** A prune driven by a
|
|
@@ -83763,67 +83991,20 @@ function buildRunnerConfig(base, overrides) {
|
|
|
83763
83991
|
preRollInferenceFrames: config.preRollInferenceFrames
|
|
83764
83992
|
};
|
|
83765
83993
|
}
|
|
83766
|
-
|
|
83767
|
-
|
|
83768
|
-
|
|
83769
|
-
|
|
83770
|
-
name: "deviceId",
|
|
83771
|
-
type: "TEXT",
|
|
83772
|
-
primaryKey: true,
|
|
83773
|
-
notNull: true
|
|
83774
|
-
},
|
|
83775
|
-
(
|
|
83776
|
-
/** The feature name list, verbatim. */
|
|
83777
|
-
{
|
|
83778
|
-
name: "features",
|
|
83779
|
-
type: "JSON",
|
|
83780
|
-
notNull: true
|
|
83781
|
-
}),
|
|
83782
|
-
{
|
|
83783
|
-
name: "updatedAt",
|
|
83784
|
-
type: "INTEGER",
|
|
83785
|
-
notNull: true
|
|
83786
|
-
}
|
|
83787
|
-
],
|
|
83788
|
-
writeMode: "write-behind",
|
|
83789
|
-
keyOf: (row) => String(row.deviceId),
|
|
83790
|
-
toValue: (row) => ({
|
|
83791
|
-
features: [...row.features],
|
|
83792
|
-
updatedAt: row.updatedAt
|
|
83793
|
-
}),
|
|
83794
|
-
fromRecord: (key, data) => {
|
|
83795
|
-
const deviceId = Number(key);
|
|
83796
|
-
const raw = data["features"];
|
|
83797
|
-
if (!Number.isFinite(deviceId) || !Array.isArray(raw)) return null;
|
|
83798
|
-
const features = raw.filter((f) => typeof f === "string");
|
|
83799
|
-
if (features.length === 0) return null;
|
|
83800
|
-
const updatedAt = Number(data["updatedAt"]);
|
|
83801
|
-
return {
|
|
83802
|
-
deviceId,
|
|
83803
|
-
features,
|
|
83804
|
-
updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
|
|
83805
|
-
restored: true
|
|
83806
|
-
};
|
|
83807
|
-
},
|
|
83808
|
-
deviceIdOf: (row) => row.deviceId
|
|
83809
|
-
};
|
|
83994
|
+
//#endregion
|
|
83995
|
+
//#region src/device-features-mirror.ts
|
|
83996
|
+
/** The per-device settings key a device's mirrored features live under. */
|
|
83997
|
+
var DEVICE_FEATURES_KEY = "deviceFeatures";
|
|
83810
83998
|
var DeviceFeaturesMirror = class {
|
|
83811
83999
|
/** Process-local fallback, used only when no store was supplied. */
|
|
83812
84000
|
local = /* @__PURE__ */ new Map();
|
|
83813
|
-
|
|
84001
|
+
settings;
|
|
83814
84002
|
logger;
|
|
83815
84003
|
now;
|
|
83816
84004
|
constructor(deps) {
|
|
83817
84005
|
this.logger = deps.logger;
|
|
83818
84006
|
this.now = deps.now ?? (() => Date.now());
|
|
83819
|
-
this.
|
|
83820
|
-
spec: DEVICE_FEATURES_SPEC,
|
|
83821
|
-
store: deps.store,
|
|
83822
|
-
logger: deps.logger
|
|
83823
|
-
});
|
|
83824
|
-
}
|
|
83825
|
-
static declare(store) {
|
|
83826
|
-
return DurableLedger.declare(store, DEVICE_FEATURES_SPEC);
|
|
84007
|
+
this.settings = deps.settings ?? null;
|
|
83827
84008
|
}
|
|
83828
84009
|
/**
|
|
83829
84010
|
* Seed the mirror from the last session. Call once at boot, after `declare`
|
|
@@ -83831,13 +84012,29 @@ var DeviceFeaturesMirror = class {
|
|
|
83831
84012
|
* already there when the first read fails.
|
|
83832
84013
|
*/
|
|
83833
84014
|
async hydrate() {
|
|
83834
|
-
|
|
83835
|
-
|
|
83836
|
-
|
|
83837
|
-
|
|
84015
|
+
const settings = this.settings;
|
|
84016
|
+
if (settings === null) return 0;
|
|
84017
|
+
const list = settings.listDeviceStoreIds;
|
|
84018
|
+
if (list !== void 0) {
|
|
84019
|
+
const ids = await list.call(settings);
|
|
84020
|
+
const batch = settings.readDeviceStoreBatch;
|
|
84021
|
+
const blobs = batch === void 0 ? new Map(await Promise.all(ids.map(async (id) => [id, await settings.readDeviceStore(id)]))) : await batch.call(settings, ids);
|
|
84022
|
+
for (const [deviceId, blob] of blobs) {
|
|
84023
|
+
const row = rowOf(deviceId, blob[DEVICE_FEATURES_KEY]);
|
|
84024
|
+
if (row !== null) this.local.set(deviceId, row);
|
|
84025
|
+
}
|
|
84026
|
+
}
|
|
84027
|
+
this.logger.info("device-features mirror restored", { meta: { devices: this.local.size } });
|
|
84028
|
+
return this.local.size;
|
|
83838
84029
|
}
|
|
83839
84030
|
held(deviceId) {
|
|
83840
|
-
return this.
|
|
84031
|
+
return this.local.get(deviceId);
|
|
84032
|
+
}
|
|
84033
|
+
async persist(deviceId, features, updatedAt) {
|
|
84034
|
+
await this.settings?.writeDeviceStore(deviceId, { [DEVICE_FEATURES_KEY]: {
|
|
84035
|
+
features: [...features],
|
|
84036
|
+
updatedAt
|
|
84037
|
+
} });
|
|
83841
84038
|
}
|
|
83842
84039
|
remember(deviceId, features) {
|
|
83843
84040
|
const row = {
|
|
@@ -83846,20 +84043,19 @@ var DeviceFeaturesMirror = class {
|
|
|
83846
84043
|
updatedAt: this.now(),
|
|
83847
84044
|
restored: false
|
|
83848
84045
|
};
|
|
83849
|
-
|
|
83850
|
-
|
|
83851
|
-
return;
|
|
83852
|
-
}
|
|
83853
|
-
const held = this.durable.get(String(deviceId));
|
|
84046
|
+
const held = this.local.get(deviceId);
|
|
84047
|
+
this.local.set(deviceId, row);
|
|
83854
84048
|
if (held !== void 0 && !held.restored && sameFeatures(held.features, features)) return;
|
|
83855
|
-
this.
|
|
84049
|
+
this.persist(deviceId, features, row.updatedAt).catch((err) => {
|
|
84050
|
+
this.logger.warn("device-features mirror not persisted", {
|
|
84051
|
+
tags: { deviceId },
|
|
84052
|
+
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
84053
|
+
});
|
|
84054
|
+
});
|
|
83856
84055
|
}
|
|
83857
84056
|
drop(deviceId) {
|
|
83858
|
-
|
|
83859
|
-
|
|
83860
|
-
return;
|
|
83861
|
-
}
|
|
83862
|
-
this.durable.forget(String(deviceId));
|
|
84057
|
+
this.local.delete(deviceId);
|
|
84058
|
+
this.settings?.writeDeviceStore(deviceId, { [DEVICE_FEATURES_KEY]: void 0 });
|
|
83863
84059
|
}
|
|
83864
84060
|
/**
|
|
83865
84061
|
* Resolve a device's features, preferring a fresh read but never letting a
|
|
@@ -83908,7 +84104,7 @@ var DeviceFeaturesMirror = class {
|
|
|
83908
84104
|
this.drop(deviceId);
|
|
83909
84105
|
return [];
|
|
83910
84106
|
}
|
|
83911
|
-
/** Drop a device's mirror
|
|
84107
|
+
/** Drop a device's mirror. (A removed device's settings are cleared centrally, D865.) */
|
|
83912
84108
|
forget(deviceId) {
|
|
83913
84109
|
this.drop(deviceId);
|
|
83914
84110
|
}
|
|
@@ -83919,6 +84115,20 @@ function sameFeatures(a, b) {
|
|
|
83919
84115
|
const held = new Set(a);
|
|
83920
84116
|
return b.every((f) => held.has(f));
|
|
83921
84117
|
}
|
|
84118
|
+
function rowOf(deviceId, raw) {
|
|
84119
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
84120
|
+
const features = Reflect.get(raw, "features");
|
|
84121
|
+
if (!Array.isArray(features)) return null;
|
|
84122
|
+
const kept = features.filter((f) => typeof f === "string");
|
|
84123
|
+
if (kept.length === 0) return null;
|
|
84124
|
+
const updatedAt = Number(Reflect.get(raw, "updatedAt"));
|
|
84125
|
+
return {
|
|
84126
|
+
deviceId,
|
|
84127
|
+
features: kept,
|
|
84128
|
+
updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
|
|
84129
|
+
restored: true
|
|
84130
|
+
};
|
|
84131
|
+
}
|
|
83922
84132
|
//#endregion
|
|
83923
84133
|
//#region src/onboard-forwarding-reconcile.ts
|
|
83924
84134
|
/** The `detectionSources` member that means "this camera's own firmware". */
|
|
@@ -84077,9 +84287,10 @@ var DetectionWiringController = class {
|
|
|
84077
84287
|
featuresMirror;
|
|
84078
84288
|
constructor(deps) {
|
|
84079
84289
|
this.deps = deps;
|
|
84290
|
+
const settings = deps.persistFeatures === true ? deps.ctx().settings : void 0;
|
|
84080
84291
|
this.featuresMirror = new DeviceFeaturesMirror({
|
|
84081
84292
|
logger: deps.logger,
|
|
84082
|
-
...
|
|
84293
|
+
...settings !== void 0 ? { settings } : {}
|
|
84083
84294
|
});
|
|
84084
84295
|
}
|
|
84085
84296
|
/**
|
|
@@ -84088,8 +84299,7 @@ var DetectionWiringController = class {
|
|
|
84088
84299
|
* that would have needed it.
|
|
84089
84300
|
*/
|
|
84090
84301
|
async hydrateFeaturesMirror() {
|
|
84091
|
-
if (this.deps.
|
|
84092
|
-
await DeviceFeaturesMirror.declare(this.deps.featuresStore);
|
|
84302
|
+
if (this.deps.persistFeatures !== true) return;
|
|
84093
84303
|
await this.featuresMirror.hydrate();
|
|
84094
84304
|
}
|
|
84095
84305
|
/** `activeDetections.get(deviceId)`. */
|
|
@@ -87470,25 +87680,33 @@ var NodeStressController = class NodeStressController {
|
|
|
87470
87680
|
//#region src/durable/lts-aggregator.ts
|
|
87471
87681
|
/** Wall-clock bucket width. Five minutes, matching HA's statistics tier. */
|
|
87472
87682
|
var LTS_BUCKET_MS = 5 * 6e4;
|
|
87683
|
+
/** Buckets written per transaction — bounds one statement, not the flush. */
|
|
87684
|
+
var FLUSH_BATCH = 200;
|
|
87473
87685
|
/** How often the row cap is enforced. Rarely: it is a bound, not a deadline. */
|
|
87474
87686
|
var CAP_SWEEP_INTERVAL_MS = 6 * 36e5;
|
|
87687
|
+
/**
|
|
87688
|
+
* The row's identity IS its four key columns (D871): a composite
|
|
87689
|
+
* `primaryKey` + `WITHOUT ROWID`, so the table is its own index and there is no
|
|
87690
|
+
* TEXT `id` repeating what the row already says. The old layout kept
|
|
87691
|
+
* `<subject>|<series>|<scope>|<bucketStart>` as a TEXT primary key beside the
|
|
87692
|
+
* four columns — 85 MB of rows plus a 44 MB autoindex for 1.07 M rows. A
|
|
87693
|
+
* composite-key collection is written with `upsertMany` (a bucket flushed twice
|
|
87694
|
+
* replaces itself) and read with `query` / `count` / `deleteWhere`; the calls
|
|
87695
|
+
* that address one row by id are refused by the engine.
|
|
87696
|
+
*/
|
|
87697
|
+
var LTS_KEY_COLUMNS = [
|
|
87698
|
+
"subject",
|
|
87699
|
+
"series",
|
|
87700
|
+
"scope",
|
|
87701
|
+
"bucketStart"
|
|
87702
|
+
];
|
|
87475
87703
|
var LTS_COLUMNS = [
|
|
87476
|
-
(
|
|
87477
|
-
/** `<subject>|<series>|<scope>|<bucketStart>` — deterministic, so a bucket
|
|
87478
|
-
* flushed twice replaces itself rather than doubling. */
|
|
87479
|
-
{
|
|
87480
|
-
name: "id",
|
|
87481
|
-
type: "TEXT",
|
|
87482
|
-
primaryKey: true,
|
|
87483
|
-
notNull: true
|
|
87484
|
-
}),
|
|
87485
87704
|
(
|
|
87486
87705
|
/** The camera id, or the node id. One column, because every query is
|
|
87487
87706
|
* "this thing over time" and the thing is one or the other. */
|
|
87488
87707
|
{
|
|
87489
87708
|
name: "subject",
|
|
87490
|
-
type: "TEXT"
|
|
87491
|
-
notNull: true
|
|
87709
|
+
type: "TEXT"
|
|
87492
87710
|
}),
|
|
87493
87711
|
(
|
|
87494
87712
|
/** Numeric mirror of `subject` for camera rows, so a per-camera question is
|
|
@@ -87500,22 +87718,19 @@ var LTS_COLUMNS = [
|
|
|
87500
87718
|
}),
|
|
87501
87719
|
{
|
|
87502
87720
|
name: "series",
|
|
87503
|
-
type: "TEXT"
|
|
87504
|
-
notNull: true
|
|
87721
|
+
type: "TEXT"
|
|
87505
87722
|
},
|
|
87506
87723
|
(
|
|
87507
87724
|
/** Sub-scope within a series: a zoneId for occupancy, `''` otherwise. */
|
|
87508
87725
|
{
|
|
87509
87726
|
name: "scope",
|
|
87510
|
-
type: "TEXT"
|
|
87511
|
-
notNull: true
|
|
87727
|
+
type: "TEXT"
|
|
87512
87728
|
}),
|
|
87513
87729
|
(
|
|
87514
87730
|
/** Bucket start, wall-clock aligned to {@link LTS_BUCKET_MS}. */
|
|
87515
87731
|
{
|
|
87516
87732
|
name: "bucketStart",
|
|
87517
|
-
type: "INTEGER"
|
|
87518
|
-
notNull: true
|
|
87733
|
+
type: "INTEGER"
|
|
87519
87734
|
}),
|
|
87520
87735
|
{
|
|
87521
87736
|
name: "samples",
|
|
@@ -87538,12 +87753,15 @@ var LTS_COLUMNS = [
|
|
|
87538
87753
|
notNull: true
|
|
87539
87754
|
}
|
|
87540
87755
|
];
|
|
87756
|
+
/**
|
|
87757
|
+
* The primary key serves every per-subject read (`subject, series, scope` then a
|
|
87758
|
+
* `bucketStart` range, already in bucket order); this index serves the read that
|
|
87759
|
+
* names a series without a subject. The old `deviceId` index went with the TEXT
|
|
87760
|
+
* id: nothing queries a series by the numeric mirror.
|
|
87761
|
+
*/
|
|
87541
87762
|
var LTS_INDEXES = [{
|
|
87542
87763
|
name: "idx_lts_series_bucket",
|
|
87543
87764
|
columns: ["series", "bucketStart"]
|
|
87544
|
-
}, {
|
|
87545
|
-
name: "idx_lts_device",
|
|
87546
|
-
columns: ["deviceId"]
|
|
87547
87765
|
}];
|
|
87548
87766
|
function ltsBucketStart(at, bucketMs = LTS_BUCKET_MS) {
|
|
87549
87767
|
return Math.floor(at / bucketMs) * bucketMs;
|
|
@@ -87572,6 +87790,8 @@ var LtsAggregator = class {
|
|
|
87572
87790
|
return store.declareCollection.mutate({
|
|
87573
87791
|
collection,
|
|
87574
87792
|
columns: [...LTS_COLUMNS],
|
|
87793
|
+
primaryKey: [...LTS_KEY_COLUMNS],
|
|
87794
|
+
withoutRowid: true,
|
|
87575
87795
|
indexes: [...LTS_INDEXES]
|
|
87576
87796
|
});
|
|
87577
87797
|
}
|
|
@@ -87631,43 +87851,60 @@ var LtsAggregator = class {
|
|
|
87631
87851
|
return 0;
|
|
87632
87852
|
}
|
|
87633
87853
|
let written = 0;
|
|
87634
|
-
for (const
|
|
87635
|
-
|
|
87636
|
-
|
|
87637
|
-
await this.store.set.mutate({
|
|
87638
|
-
collection: this.collection,
|
|
87639
|
-
key,
|
|
87640
|
-
value: {
|
|
87641
|
-
subject: bucket.subject,
|
|
87642
|
-
...bucket.deviceId !== void 0 ? { deviceId: bucket.deviceId } : {},
|
|
87643
|
-
series: bucket.series,
|
|
87644
|
-
scope: bucket.scope,
|
|
87645
|
-
bucketStart: bucket.bucketStart,
|
|
87646
|
-
samples: bucket.samples,
|
|
87647
|
-
sum: bucket.sum,
|
|
87648
|
-
min: bucket.min,
|
|
87649
|
-
max: bucket.max
|
|
87650
|
-
}
|
|
87651
|
-
});
|
|
87652
|
-
written += 1;
|
|
87653
|
-
this.groups.add(groupKey(bucket.subject, bucket.series, bucket.scope));
|
|
87654
|
-
} catch (err) {
|
|
87655
|
-
this.logger.warn("lts bucket write failed — this interval will be missing", {
|
|
87656
|
-
...bucket.deviceId !== void 0 ? { tags: { deviceId: bucket.deviceId } } : {},
|
|
87657
|
-
meta: {
|
|
87658
|
-
collection: this.collection,
|
|
87659
|
-
series: bucket.series,
|
|
87660
|
-
subject: bucket.subject,
|
|
87661
|
-
bucketStart: bucket.bucketStart,
|
|
87662
|
-
error: String(err)
|
|
87663
|
-
}
|
|
87664
|
-
});
|
|
87665
|
-
}
|
|
87666
|
-
this.open.delete(key);
|
|
87854
|
+
for (const batch of chunk(due, FLUSH_BATCH)) {
|
|
87855
|
+
written += await this.writeBatch(batch);
|
|
87856
|
+
for (const bucket of batch) this.open.delete(rowId(bucket.subject, bucket.series, bucket.scope, bucket.bucketStart));
|
|
87667
87857
|
}
|
|
87668
87858
|
await this.maybeSweepCap(now);
|
|
87669
87859
|
return written;
|
|
87670
87860
|
}
|
|
87861
|
+
/**
|
|
87862
|
+
* Upsert one batch in a single transaction. If the batch is refused as a
|
|
87863
|
+
* whole (all-or-nothing), each bucket is retried alone so one bad row costs
|
|
87864
|
+
* one interval, not forty. Returns the number of rows written.
|
|
87865
|
+
*/
|
|
87866
|
+
async writeBatch(batch) {
|
|
87867
|
+
try {
|
|
87868
|
+
await this.upsert(batch);
|
|
87869
|
+
for (const bucket of batch) this.groups.add(groupKey(bucket.subject, bucket.series, bucket.scope));
|
|
87870
|
+
return batch.length;
|
|
87871
|
+
} catch (err) {
|
|
87872
|
+
if (batch.length > 1) {
|
|
87873
|
+
let written = 0;
|
|
87874
|
+
for (const bucket of batch) written += await this.writeBatch([bucket]);
|
|
87875
|
+
return written;
|
|
87876
|
+
}
|
|
87877
|
+
const bucket = batch[0];
|
|
87878
|
+
this.logger.warn("lts bucket write failed — this interval will be missing", {
|
|
87879
|
+
...bucket?.deviceId !== void 0 ? { tags: { deviceId: bucket.deviceId } } : {},
|
|
87880
|
+
meta: {
|
|
87881
|
+
collection: this.collection,
|
|
87882
|
+
series: bucket?.series,
|
|
87883
|
+
subject: bucket?.subject,
|
|
87884
|
+
bucketStart: bucket?.bucketStart,
|
|
87885
|
+
error: String(err)
|
|
87886
|
+
}
|
|
87887
|
+
});
|
|
87888
|
+
return 0;
|
|
87889
|
+
}
|
|
87890
|
+
}
|
|
87891
|
+
async upsert(batch) {
|
|
87892
|
+
await this.store.upsertMany.mutate({
|
|
87893
|
+
collection: this.collection,
|
|
87894
|
+
conflict: [...LTS_KEY_COLUMNS],
|
|
87895
|
+
records: batch.map((bucket) => ({ data: {
|
|
87896
|
+
subject: bucket.subject,
|
|
87897
|
+
deviceId: bucket.deviceId ?? null,
|
|
87898
|
+
series: bucket.series,
|
|
87899
|
+
scope: bucket.scope,
|
|
87900
|
+
bucketStart: bucket.bucketStart,
|
|
87901
|
+
samples: bucket.samples,
|
|
87902
|
+
sum: bucket.sum,
|
|
87903
|
+
min: bucket.min,
|
|
87904
|
+
max: bucket.max
|
|
87905
|
+
} }))
|
|
87906
|
+
});
|
|
87907
|
+
}
|
|
87671
87908
|
/** Read closed buckets back. The read surface every chart will use. */
|
|
87672
87909
|
async read(query = {}) {
|
|
87673
87910
|
const where = {};
|
|
@@ -87752,6 +87989,11 @@ var LtsAggregator = class {
|
|
|
87752
87989
|
}
|
|
87753
87990
|
}
|
|
87754
87991
|
};
|
|
87992
|
+
function chunk(items, size) {
|
|
87993
|
+
const out = [];
|
|
87994
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
87995
|
+
return out;
|
|
87996
|
+
}
|
|
87755
87997
|
function groupKey(subject, series, scope) {
|
|
87756
87998
|
return `${subject}${series}${scope}`;
|
|
87757
87999
|
}
|
|
@@ -87810,7 +88052,7 @@ function recordToRow(data) {
|
|
|
87810
88052
|
* write="one row per (node, series, 5-min bucket), written ONCE when the bucket closes; a node reporting no samples writes nothing"
|
|
87811
88053
|
* 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."
|
|
87812
88054
|
*/
|
|
87813
|
-
var NODE_STRESS_LTS_COLLECTION = "pipeline-orchestrator:stats-5m";
|
|
88055
|
+
var NODE_STRESS_LTS_COLLECTION = "pipeline-orchestrator:stats-5m-v2";
|
|
87814
88056
|
var NodeStressLts = class {
|
|
87815
88057
|
lts;
|
|
87816
88058
|
constructor(deps) {
|
|
@@ -94387,7 +94629,7 @@ async function buildOrchestratorControllers(deps) {
|
|
|
94387
94629
|
reconcile.scheduleReconcile();
|
|
94388
94630
|
const detectionWiring = new DetectionWiringController({
|
|
94389
94631
|
ctx: () => deps.ctx(),
|
|
94390
|
-
|
|
94632
|
+
persistFeatures: true,
|
|
94391
94633
|
ledger,
|
|
94392
94634
|
placement,
|
|
94393
94635
|
audio,
|
|
@@ -94607,6 +94849,24 @@ function buildGlobalSettingsSections(options) {
|
|
|
94607
94849
|
default: DEFAULT_DETAIL_CROP_CONVENTION.square
|
|
94608
94850
|
}]
|
|
94609
94851
|
},
|
|
94852
|
+
{
|
|
94853
|
+
id: FACE_RESCORE_SECTION_ID,
|
|
94854
|
+
title: "Face suggestions",
|
|
94855
|
+
tab: "pipeline",
|
|
94856
|
+
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.",
|
|
94857
|
+
fields: [{
|
|
94858
|
+
key: FACE_RESCORE_INTERVAL_KEY,
|
|
94859
|
+
type: "slider",
|
|
94860
|
+
label: "Suggestion refresh interval",
|
|
94861
|
+
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.",
|
|
94862
|
+
min: FACE_RESCORE_INTERVAL_FIELD.min,
|
|
94863
|
+
max: FACE_RESCORE_INTERVAL_FIELD.max,
|
|
94864
|
+
step: FACE_RESCORE_INTERVAL_FIELD.step,
|
|
94865
|
+
default: FACE_RESCORE_INTERVAL_FIELD.default,
|
|
94866
|
+
showValue: true,
|
|
94867
|
+
unit: "min"
|
|
94868
|
+
}]
|
|
94869
|
+
},
|
|
94610
94870
|
{
|
|
94611
94871
|
id: NATIVE_LEASE_SECTION_ID,
|
|
94612
94872
|
title: "Native frame lease",
|