camstack 1.2.35 → 1.2.36
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.
|
@@ -23631,9 +23631,9 @@ var require_zod = __commonJS({
|
|
|
23631
23631
|
}
|
|
23632
23632
|
});
|
|
23633
23633
|
|
|
23634
|
-
// ../system/dist/dist-
|
|
23635
|
-
var
|
|
23636
|
-
"../system/dist/dist-
|
|
23634
|
+
// ../system/dist/dist-B2CtwVW2.js
|
|
23635
|
+
var require_dist_B2CtwVW2 = __commonJS({
|
|
23636
|
+
"../system/dist/dist-B2CtwVW2.js"(exports) {
|
|
23637
23637
|
"use strict";
|
|
23638
23638
|
var zod = require_zod();
|
|
23639
23639
|
var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
|
|
@@ -25933,7 +25933,7 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
25933
25933
|
postBufferSec: zod.z.number().min(0).optional()
|
|
25934
25934
|
});
|
|
25935
25935
|
({
|
|
25936
|
-
preBufferSec:
|
|
25936
|
+
preBufferSec: 15,
|
|
25937
25937
|
postBufferSec: 30
|
|
25938
25938
|
}).postBufferSec * 1e3;
|
|
25939
25939
|
var RecordingRetentionSchema = zod.z.object({
|
|
@@ -32442,128 +32442,103 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
32442
32442
|
var AUDIO_MACRO_LABELS = [
|
|
32443
32443
|
{
|
|
32444
32444
|
id: "speech",
|
|
32445
|
-
name: "Speech"
|
|
32446
|
-
icon: "\u{1F5E3}\uFE0F"
|
|
32445
|
+
name: "Speech"
|
|
32447
32446
|
},
|
|
32448
32447
|
{
|
|
32449
32448
|
id: "scream",
|
|
32450
|
-
name: "Scream / Shout"
|
|
32451
|
-
icon: "\u{1F631}"
|
|
32449
|
+
name: "Scream / Shout"
|
|
32452
32450
|
},
|
|
32453
32451
|
{
|
|
32454
32452
|
id: "crying",
|
|
32455
|
-
name: "Crying / Baby"
|
|
32456
|
-
icon: "\u{1F622}"
|
|
32453
|
+
name: "Crying / Baby"
|
|
32457
32454
|
},
|
|
32458
32455
|
{
|
|
32459
32456
|
id: "laughter",
|
|
32460
|
-
name: "Laughter"
|
|
32461
|
-
icon: "\u{1F602}"
|
|
32457
|
+
name: "Laughter"
|
|
32462
32458
|
},
|
|
32463
32459
|
{
|
|
32464
32460
|
id: "music",
|
|
32465
|
-
name: "Music"
|
|
32466
|
-
icon: "\u{1F3B5}"
|
|
32461
|
+
name: "Music"
|
|
32467
32462
|
},
|
|
32468
32463
|
{
|
|
32469
32464
|
id: "dog",
|
|
32470
|
-
name: "Dog"
|
|
32471
|
-
icon: "\u{1F415}"
|
|
32465
|
+
name: "Dog"
|
|
32472
32466
|
},
|
|
32473
32467
|
{
|
|
32474
32468
|
id: "cat",
|
|
32475
|
-
name: "Cat"
|
|
32476
|
-
icon: "\u{1F408}"
|
|
32469
|
+
name: "Cat"
|
|
32477
32470
|
},
|
|
32478
32471
|
{
|
|
32479
32472
|
id: "bird",
|
|
32480
|
-
name: "Bird"
|
|
32481
|
-
icon: "\u{1F426}"
|
|
32473
|
+
name: "Bird"
|
|
32482
32474
|
},
|
|
32483
32475
|
{
|
|
32484
32476
|
id: "animal",
|
|
32485
|
-
name: "Animal (other)"
|
|
32486
|
-
icon: "\u{1F43E}"
|
|
32477
|
+
name: "Animal (other)"
|
|
32487
32478
|
},
|
|
32488
32479
|
{
|
|
32489
32480
|
id: "alarm",
|
|
32490
|
-
name: "Alarm / Siren"
|
|
32491
|
-
icon: "\u{1F6A8}"
|
|
32481
|
+
name: "Alarm / Siren"
|
|
32492
32482
|
},
|
|
32493
32483
|
{
|
|
32494
32484
|
id: "doorbell",
|
|
32495
|
-
name: "Doorbell / Knock"
|
|
32496
|
-
icon: "\u{1F514}"
|
|
32485
|
+
name: "Doorbell / Knock"
|
|
32497
32486
|
},
|
|
32498
32487
|
{
|
|
32499
32488
|
id: "glass_breaking",
|
|
32500
|
-
name: "Glass Breaking"
|
|
32501
|
-
icon: "\u{1F4A5}"
|
|
32489
|
+
name: "Glass Breaking"
|
|
32502
32490
|
},
|
|
32503
32491
|
{
|
|
32504
32492
|
id: "gunshot",
|
|
32505
|
-
name: "Gunshot / Explosion"
|
|
32506
|
-
icon: "\u{1F4A3}"
|
|
32493
|
+
name: "Gunshot / Explosion"
|
|
32507
32494
|
},
|
|
32508
32495
|
{
|
|
32509
32496
|
id: "vehicle",
|
|
32510
|
-
name: "Vehicle"
|
|
32511
|
-
icon: "\u{1F697}"
|
|
32497
|
+
name: "Vehicle"
|
|
32512
32498
|
},
|
|
32513
32499
|
{
|
|
32514
32500
|
id: "siren",
|
|
32515
|
-
name: "Emergency Siren"
|
|
32516
|
-
icon: "\u{1F691}"
|
|
32501
|
+
name: "Emergency Siren"
|
|
32517
32502
|
},
|
|
32518
32503
|
{
|
|
32519
32504
|
id: "fire",
|
|
32520
|
-
name: "Fire / Smoke"
|
|
32521
|
-
icon: "\u{1F525}"
|
|
32505
|
+
name: "Fire / Smoke"
|
|
32522
32506
|
},
|
|
32523
32507
|
{
|
|
32524
32508
|
id: "water",
|
|
32525
|
-
name: "Water"
|
|
32526
|
-
icon: "\u{1F4A7}"
|
|
32509
|
+
name: "Water"
|
|
32527
32510
|
},
|
|
32528
32511
|
{
|
|
32529
32512
|
id: "wind",
|
|
32530
|
-
name: "Wind / Weather"
|
|
32531
|
-
icon: "\u{1F32C}\uFE0F"
|
|
32513
|
+
name: "Wind / Weather"
|
|
32532
32514
|
},
|
|
32533
32515
|
{
|
|
32534
32516
|
id: "door",
|
|
32535
|
-
name: "Door"
|
|
32536
|
-
icon: "\u{1F6AA}"
|
|
32517
|
+
name: "Door"
|
|
32537
32518
|
},
|
|
32538
32519
|
{
|
|
32539
32520
|
id: "footsteps",
|
|
32540
|
-
name: "Footsteps"
|
|
32541
|
-
icon: "\u{1F463}"
|
|
32521
|
+
name: "Footsteps"
|
|
32542
32522
|
},
|
|
32543
32523
|
{
|
|
32544
32524
|
id: "crowd",
|
|
32545
|
-
name: "Crowd / Chatter"
|
|
32546
|
-
icon: "\u{1F465}"
|
|
32525
|
+
name: "Crowd / Chatter"
|
|
32547
32526
|
},
|
|
32548
32527
|
{
|
|
32549
32528
|
id: "telephone",
|
|
32550
|
-
name: "Telephone"
|
|
32551
|
-
icon: "\u{1F4DE}"
|
|
32529
|
+
name: "Telephone"
|
|
32552
32530
|
},
|
|
32553
32531
|
{
|
|
32554
32532
|
id: "engine",
|
|
32555
|
-
name: "Engine / Motor"
|
|
32556
|
-
icon: "\u2699\uFE0F"
|
|
32533
|
+
name: "Engine / Motor"
|
|
32557
32534
|
},
|
|
32558
32535
|
{
|
|
32559
32536
|
id: "tools",
|
|
32560
|
-
name: "Tools / Construction"
|
|
32561
|
-
icon: "\u{1F528}"
|
|
32537
|
+
name: "Tools / Construction"
|
|
32562
32538
|
},
|
|
32563
32539
|
{
|
|
32564
32540
|
id: "silence",
|
|
32565
|
-
name: "Silence"
|
|
32566
|
-
icon: "\u{1F92B}"
|
|
32541
|
+
name: "Silence"
|
|
32567
32542
|
}
|
|
32568
32543
|
];
|
|
32569
32544
|
var YAMNET_TO_MACRO = {
|
|
@@ -34677,6 +34652,35 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
34677
34652
|
*/
|
|
34678
34653
|
hasFace: zod.z.boolean().optional(),
|
|
34679
34654
|
/**
|
|
34655
|
+
* This track has a face row IN THE GALLERY: a crop **and** an embedding — a
|
|
34656
|
+
* face an operator could ASSIGN to an identity.
|
|
34657
|
+
*
|
|
34658
|
+
* The STRICT twin of {@link hasFace}, and the pair only earns its keep
|
|
34659
|
+
* because the two disagree. `hasFace` is stamped at the TOP of the face
|
|
34660
|
+
* branch, before every gate, and means no more than "a face detector produced
|
|
34661
|
+
* a face detail". This one is stamped at the single moment the gallery row
|
|
34662
|
+
* LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
|
|
34663
|
+
* past the embedding-magnitude verdict, the `minFacePx` detection gate, the
|
|
34664
|
+
* candidate gate, the imageless-track drop (no crop was ever captured) and
|
|
34665
|
+
* the crop-store drop. Everything between the detector and that insert can
|
|
34666
|
+
* legitimately refuse the face, so a flag written any earlier promises the
|
|
34667
|
+
* operator something to assign and delivers nothing.
|
|
34668
|
+
*
|
|
34669
|
+
* **Independent of recognition.** A face collected but never auto-matched is
|
|
34670
|
+
* still assignable — it is in fact the face an operator most wants to reach —
|
|
34671
|
+
* so this is NOT gated on `recognizedIdentityId`. Recognition lands in
|
|
34672
|
+
* `subLabel`; this says only that the raw material exists.
|
|
34673
|
+
*
|
|
34674
|
+
* **Set once, never cleared.** A track that produced a gallery row produced
|
|
34675
|
+
* one; deleting the row later is the gallery's business, not this flag's.
|
|
34676
|
+
*
|
|
34677
|
+
* **Absent ≠ false**, the same rule as {@link hasFace}: every row written
|
|
34678
|
+
* before the column omits it, and so does every server that predates the
|
|
34679
|
+
* field. A consumer must test `=== true` and render nothing otherwise —
|
|
34680
|
+
* never infer "no assignable face".
|
|
34681
|
+
*/
|
|
34682
|
+
hasEmbeddedFace: zod.z.boolean().optional(),
|
|
34683
|
+
/**
|
|
34680
34684
|
* This subject CONTAINS a folded rider — a person the rider-pairing step
|
|
34681
34685
|
* ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
|
|
34682
34686
|
* so the passage is tracked once and as a VEHICLE.
|
|
@@ -36437,6 +36441,32 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
36437
36441
|
/** FRAME-space bbox (already mapped back from crop space). */
|
|
36438
36442
|
bbox: NativeCropBboxSchema.optional(),
|
|
36439
36443
|
embedding: zod.z.string().optional(),
|
|
36444
|
+
/**
|
|
36445
|
+
* L2 magnitude of the RAW pre-normalization embedding. Additive and optional
|
|
36446
|
+
* so an older runner that does not send it degrades to "unmeasurable", which
|
|
36447
|
+
* the consuming gate treats as accept — never as reject.
|
|
36448
|
+
*/
|
|
36449
|
+
embeddingMagnitude: zod.z.number().optional(),
|
|
36450
|
+
/**
|
|
36451
|
+
* Model that produced `embedding` — the SAME contract `labelModelId` has for
|
|
36452
|
+
* `label`, and for the same reason: the producing node is the only side that
|
|
36453
|
+
* knows which model actually ran, and re-deriving it downstream is exactly
|
|
36454
|
+
* the inference this model exists to forbid.
|
|
36455
|
+
*
|
|
36456
|
+
* Additive and optional, so a runner that predates it degrades to
|
|
36457
|
+
* "unmeasurable" rather than to a wrong answer — the consumer falls back to
|
|
36458
|
+
* its previous assumption instead of refusing.
|
|
36459
|
+
*
|
|
36460
|
+
* Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
|
|
36461
|
+
* to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
|
|
36462
|
+
* with nothing on the wire it took the id from the GALLERY, so the gate
|
|
36463
|
+
* compared the gallery against itself and could never fire. That turned the
|
|
36464
|
+
* one guard protecting the face feature space into a tautology, and it is
|
|
36465
|
+
* why swapping `face-embedding`'s model would NOT have stranded the enrolled
|
|
36466
|
+
* gallery as the catalog comment predicts — it would have scored the new
|
|
36467
|
+
* model's vectors against the old model's samples in one cosine space.
|
|
36468
|
+
*/
|
|
36469
|
+
embeddingModelId: zod.z.string().optional(),
|
|
36440
36470
|
label: zod.z.string().optional(),
|
|
36441
36471
|
/**
|
|
36442
36472
|
* The tier `label` occupies, copied VERBATIM from the producing step's
|
|
@@ -38098,6 +38128,7 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
38098
38128
|
isBattery: zod.z.boolean(),
|
|
38099
38129
|
reason: zod.z.enum([
|
|
38100
38130
|
"disabled",
|
|
38131
|
+
"offline",
|
|
38101
38132
|
"sleeping",
|
|
38102
38133
|
"unreachable",
|
|
38103
38134
|
"waking"
|
|
@@ -38188,9 +38219,17 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
38188
38219
|
sleeping: zod.z.boolean(),
|
|
38189
38220
|
/** Current device state rendered over the cached frame. State images
|
|
38190
38221
|
* remain authoritative even when their photographic background is
|
|
38191
|
-
* old; null means the link must carry a current camera frame.
|
|
38222
|
+
* old; null means the link must carry a current camera frame.
|
|
38223
|
+
*
|
|
38224
|
+
* **This is the ONLY source a surface may derive an inactive tile
|
|
38225
|
+
* from.** A client that reads a device's `online`/`disabled` flags and
|
|
38226
|
+
* draws its own verdict will disagree with the pixels it is painting
|
|
38227
|
+
* — the frame comes from here, and so must the sentence over it
|
|
38228
|
+
* (D190). Pair with `ageMs` for the age of the photograph behind the
|
|
38229
|
+
* state. */
|
|
38192
38230
|
stateReason: zod.z.enum([
|
|
38193
38231
|
"disabled",
|
|
38232
|
+
"offline",
|
|
38194
38233
|
"sleeping",
|
|
38195
38234
|
"unreachable",
|
|
38196
38235
|
"waking"
|
|
@@ -55173,7 +55212,6 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
55173
55212
|
hitPercent: 60,
|
|
55174
55213
|
samplingSeconds: 10
|
|
55175
55214
|
};
|
|
55176
|
-
new Map(AUDIO_MACRO_LABELS.flatMap((macro2) => macro2.icon === void 0 ? [] : [[macro2.id, macro2.icon]]));
|
|
55177
55215
|
NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
|
|
55178
55216
|
var TimelapseTemplateSchema = zod.z.object({
|
|
55179
55217
|
title: zod.z.string().max(500).optional(),
|
|
@@ -55284,6 +55322,39 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
55284
55322
|
createdAt: zod.z.number(),
|
|
55285
55323
|
updatedAt: zod.z.number()
|
|
55286
55324
|
});
|
|
55325
|
+
Object.freeze(Object.fromEntries([{
|
|
55326
|
+
stepId: "face-embedding",
|
|
55327
|
+
label: "Face recognition model",
|
|
55328
|
+
defaultModelId: "arcface-r100",
|
|
55329
|
+
indexName: "the enrolled face gallery",
|
|
55330
|
+
options: [
|
|
55331
|
+
{
|
|
55332
|
+
id: "arcface-r100",
|
|
55333
|
+
label: "ArcFace (ResNet34)"
|
|
55334
|
+
},
|
|
55335
|
+
{
|
|
55336
|
+
id: "auraface-r100",
|
|
55337
|
+
label: "AuraFace R100"
|
|
55338
|
+
},
|
|
55339
|
+
{
|
|
55340
|
+
id: "inception-resnet-v1",
|
|
55341
|
+
label: "Inception ResNet V1"
|
|
55342
|
+
}
|
|
55343
|
+
]
|
|
55344
|
+
}, {
|
|
55345
|
+
stepId: "clip-embedding",
|
|
55346
|
+
label: "Semantic search model",
|
|
55347
|
+
defaultModelId: "mobileclip-s1",
|
|
55348
|
+
indexName: "the object semantic-search index",
|
|
55349
|
+
options: [{
|
|
55350
|
+
id: "mobileclip-s1",
|
|
55351
|
+
label: "MobileCLIP S1"
|
|
55352
|
+
}, {
|
|
55353
|
+
id: "mobileclip-s2",
|
|
55354
|
+
label: "MobileCLIP S2"
|
|
55355
|
+
}]
|
|
55356
|
+
}].map((s) => [s.stepId, s.defaultModelId])));
|
|
55357
|
+
zod.z.string().min(1);
|
|
55287
55358
|
zod.z.object({
|
|
55288
55359
|
/**
|
|
55289
55360
|
* Fraction of the box's own size added on EACH side before cutting.
|
|
@@ -55304,10 +55375,17 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
55304
55375
|
*/
|
|
55305
55376
|
square: zod.z.boolean()
|
|
55306
55377
|
});
|
|
55307
|
-
|
|
55378
|
+
var DEFAULT_DETAIL_CROP_CONVENTION = {
|
|
55308
55379
|
paddingRatio: 0.15,
|
|
55309
55380
|
square: false
|
|
55310
|
-
}
|
|
55381
|
+
};
|
|
55382
|
+
Object.freeze({
|
|
55383
|
+
x: 0,
|
|
55384
|
+
y: 0,
|
|
55385
|
+
w: 1,
|
|
55386
|
+
h: 1
|
|
55387
|
+
});
|
|
55388
|
+
DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
|
|
55311
55389
|
var NativeLeaseAdmissionSchema = zod.z.enum(["all", "inferred"]);
|
|
55312
55390
|
zod.z.object({
|
|
55313
55391
|
/**
|
|
@@ -56108,7 +56186,7 @@ var require_alerts_addon = __commonJS({
|
|
|
56108
56186
|
[Symbol.toStringTag]: { value: "Module" }
|
|
56109
56187
|
});
|
|
56110
56188
|
require_chunk_Cek0wNdY();
|
|
56111
|
-
var require_dist10 =
|
|
56189
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
56112
56190
|
function selectExpired(alerts, cutoffMs) {
|
|
56113
56191
|
return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
|
|
56114
56192
|
}
|
|
@@ -56921,7 +56999,7 @@ var require_console_logging = __commonJS({
|
|
|
56921
56999
|
[Symbol.toStringTag]: { value: "Module" }
|
|
56922
57000
|
});
|
|
56923
57001
|
require_chunk_Cek0wNdY();
|
|
56924
|
-
var require_dist10 =
|
|
57002
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
56925
57003
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
56926
57004
|
var LEVEL_RANK = {
|
|
56927
57005
|
debug: 0,
|
|
@@ -57015,7 +57093,7 @@ var require_core_blocks_addon = __commonJS({
|
|
|
57015
57093
|
"use strict";
|
|
57016
57094
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
57017
57095
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
57018
|
-
var require_dist10 =
|
|
57096
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
57019
57097
|
var node_crypto = __require("crypto");
|
|
57020
57098
|
var node_fs_promises = __require("fs/promises");
|
|
57021
57099
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -57912,6 +57990,230 @@ var require_core_blocks = __commonJS({
|
|
|
57912
57990
|
}
|
|
57913
57991
|
});
|
|
57914
57992
|
|
|
57993
|
+
// ../system/dist/retired-settings-keys-CYDGGkyM.js
|
|
57994
|
+
var require_retired_settings_keys_CYDGGkyM = __commonJS({
|
|
57995
|
+
"../system/dist/retired-settings-keys-CYDGGkyM.js"(exports) {
|
|
57996
|
+
"use strict";
|
|
57997
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
57998
|
+
function settingsStoreIsAuthoritativeHere(env) {
|
|
57999
|
+
const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
|
|
58000
|
+
return raw === "" || raw === "hub";
|
|
58001
|
+
}
|
|
58002
|
+
var RETIRED_SETTINGS_KEYS = [{
|
|
58003
|
+
namespace: "detection-pipeline",
|
|
58004
|
+
collection: "addon-settings",
|
|
58005
|
+
row: "root",
|
|
58006
|
+
keys: ["pipelineSteps", "pipelineEngine"],
|
|
58007
|
+
perNodeKeys: [
|
|
58008
|
+
"engineRuntime",
|
|
58009
|
+
"engineBackend",
|
|
58010
|
+
"engineDevice",
|
|
58011
|
+
"probedBestEngine"
|
|
58012
|
+
],
|
|
58013
|
+
reason: "global step/engine seed and the per-node engine cascade \u2014 both removed; the live per-camera dispatch path never read either"
|
|
58014
|
+
}];
|
|
58015
|
+
var RETIRED_SETTINGS_ROWS = [
|
|
58016
|
+
{
|
|
58017
|
+
collection: "addon-settings",
|
|
58018
|
+
row: "device-manager.deviceMeta",
|
|
58019
|
+
owner: "device-manager",
|
|
58020
|
+
evidence: { collection: "device-manager:devices" },
|
|
58021
|
+
reason: "the fleet is one row per device in `device-manager:devices` since the flatten; this blob was 467 KB read and JSON.parsed on hub-main per projection"
|
|
58022
|
+
},
|
|
58023
|
+
{
|
|
58024
|
+
collection: "addon-settings",
|
|
58025
|
+
row: "device-manager.deviceMetadata",
|
|
58026
|
+
owner: "device-manager",
|
|
58027
|
+
evidence: { collection: "device-manager:devices" },
|
|
58028
|
+
reason: "hardware-identity metadata is the `metadata` column of the device row; the separate blob existed only because a blob write is whole-value"
|
|
58029
|
+
},
|
|
58030
|
+
{
|
|
58031
|
+
collection: "addon-settings",
|
|
58032
|
+
row: "device-manager.deviceIndex",
|
|
58033
|
+
owner: "device-manager",
|
|
58034
|
+
evidence: { collection: "device-manager:devices" },
|
|
58035
|
+
reason: "derivable: `addonId`/`stableId` are indexed columns, and index membership is the `registered` column"
|
|
58036
|
+
}
|
|
58037
|
+
];
|
|
58038
|
+
function retiredKeyStoreOf(backend) {
|
|
58039
|
+
return {
|
|
58040
|
+
async read(spec) {
|
|
58041
|
+
return require_dist10.asJsonObject(await backend.get({
|
|
58042
|
+
namespace: spec.namespace,
|
|
58043
|
+
collection: spec.collection,
|
|
58044
|
+
key: spec.row
|
|
58045
|
+
}));
|
|
58046
|
+
},
|
|
58047
|
+
async write(spec, value) {
|
|
58048
|
+
await backend.set({
|
|
58049
|
+
namespace: spec.namespace,
|
|
58050
|
+
collection: spec.collection,
|
|
58051
|
+
key: spec.row,
|
|
58052
|
+
value
|
|
58053
|
+
});
|
|
58054
|
+
}
|
|
58055
|
+
};
|
|
58056
|
+
}
|
|
58057
|
+
function planNestedRetiredKeyPurge(blob, spec) {
|
|
58058
|
+
const nestedKeys = spec.mapValueKeys ?? [];
|
|
58059
|
+
if (nestedKeys.length === 0) return [];
|
|
58060
|
+
const found = [];
|
|
58061
|
+
for (const [mapKey, value] of Object.entries(blob)) {
|
|
58062
|
+
const inner = require_dist10.asJsonObject(value);
|
|
58063
|
+
if (inner === null) continue;
|
|
58064
|
+
for (const retired of nestedKeys) if (retired in inner) found.push(`${mapKey}.${retired}`);
|
|
58065
|
+
}
|
|
58066
|
+
return found;
|
|
58067
|
+
}
|
|
58068
|
+
function planRetiredKeyPurge(blob, spec) {
|
|
58069
|
+
const exact = new Set(spec.keys);
|
|
58070
|
+
const found = [];
|
|
58071
|
+
for (const key of Object.keys(blob)) {
|
|
58072
|
+
if (exact.has(key)) {
|
|
58073
|
+
found.push(key);
|
|
58074
|
+
continue;
|
|
58075
|
+
}
|
|
58076
|
+
for (const perNode of spec.perNodeKeys) if (key === perNode || key.startsWith(`${perNode}@`)) {
|
|
58077
|
+
found.push(key);
|
|
58078
|
+
break;
|
|
58079
|
+
}
|
|
58080
|
+
}
|
|
58081
|
+
return found;
|
|
58082
|
+
}
|
|
58083
|
+
async function purgeRetiredSettingsKeys(store, logger, specs = RETIRED_SETTINGS_KEYS, env = process.env) {
|
|
58084
|
+
if (!settingsStoreIsAuthoritativeHere(env)) return [];
|
|
58085
|
+
const results = [];
|
|
58086
|
+
for (const spec of specs) {
|
|
58087
|
+
let blob;
|
|
58088
|
+
try {
|
|
58089
|
+
blob = await store.read(spec);
|
|
58090
|
+
} catch (err) {
|
|
58091
|
+
logger.warn("retired settings keys \u2014 read failed, row left untouched", { meta: {
|
|
58092
|
+
addon: spec.namespace,
|
|
58093
|
+
collection: spec.collection,
|
|
58094
|
+
row: spec.row,
|
|
58095
|
+
error: err instanceof Error ? err.message : String(err)
|
|
58096
|
+
} });
|
|
58097
|
+
continue;
|
|
58098
|
+
}
|
|
58099
|
+
if (blob === null) continue;
|
|
58100
|
+
const doomed = planRetiredKeyPurge(blob, spec);
|
|
58101
|
+
const doomedNested = planNestedRetiredKeyPurge(blob, spec);
|
|
58102
|
+
if (doomed.length === 0 && doomedNested.length === 0) continue;
|
|
58103
|
+
const doomedSet = new Set(doomed);
|
|
58104
|
+
const nestedByMapKey = /* @__PURE__ */ new Map();
|
|
58105
|
+
for (const path of doomedNested) {
|
|
58106
|
+
const cut = path.indexOf(".");
|
|
58107
|
+
const mapKey = path.slice(0, cut);
|
|
58108
|
+
const inner = path.slice(cut + 1);
|
|
58109
|
+
const bucket = nestedByMapKey.get(mapKey) ?? /* @__PURE__ */ new Set();
|
|
58110
|
+
bucket.add(inner);
|
|
58111
|
+
nestedByMapKey.set(mapKey, bucket);
|
|
58112
|
+
}
|
|
58113
|
+
const next = Object.fromEntries(Object.entries(blob).filter(([key]) => !doomedSet.has(key)).map(([key, value]) => {
|
|
58114
|
+
const retired = nestedByMapKey.get(key);
|
|
58115
|
+
if (retired === void 0) return [key, value];
|
|
58116
|
+
const inner = require_dist10.asJsonObject(value);
|
|
58117
|
+
if (inner === null) return [key, value];
|
|
58118
|
+
return [key, Object.fromEntries(Object.entries(inner).filter(([k]) => !retired.has(k)))];
|
|
58119
|
+
}));
|
|
58120
|
+
await store.write(spec, next);
|
|
58121
|
+
const result = {
|
|
58122
|
+
addon: spec.namespace,
|
|
58123
|
+
collection: spec.collection,
|
|
58124
|
+
row: spec.row,
|
|
58125
|
+
keys: doomed,
|
|
58126
|
+
nested: doomedNested
|
|
58127
|
+
};
|
|
58128
|
+
results.push(result);
|
|
58129
|
+
logger.info(`purged ${doomed.length + doomedNested.length} retired settings keys`, { meta: {
|
|
58130
|
+
...result,
|
|
58131
|
+
reason: spec.reason
|
|
58132
|
+
} });
|
|
58133
|
+
}
|
|
58134
|
+
return results;
|
|
58135
|
+
}
|
|
58136
|
+
async function purgeRetiredSettingsRows(store, logger, specs = RETIRED_SETTINGS_ROWS, env = process.env) {
|
|
58137
|
+
if (!settingsStoreIsAuthoritativeHere(env)) return [];
|
|
58138
|
+
const results = [];
|
|
58139
|
+
const evidence = /* @__PURE__ */ new Map();
|
|
58140
|
+
for (const spec of specs) {
|
|
58141
|
+
let present;
|
|
58142
|
+
try {
|
|
58143
|
+
present = await store.hasRow(spec);
|
|
58144
|
+
} catch (err) {
|
|
58145
|
+
logger.warn("retired settings row \u2014 read failed, row left untouched", { meta: {
|
|
58146
|
+
addon: spec.owner,
|
|
58147
|
+
collection: spec.collection,
|
|
58148
|
+
row: spec.row,
|
|
58149
|
+
error: errText(err)
|
|
58150
|
+
} });
|
|
58151
|
+
continue;
|
|
58152
|
+
}
|
|
58153
|
+
if (!present) continue;
|
|
58154
|
+
const successor = spec.evidence.collection;
|
|
58155
|
+
let count = evidence.get(successor);
|
|
58156
|
+
if (count === void 0) {
|
|
58157
|
+
try {
|
|
58158
|
+
count = await store.countRows(successor);
|
|
58159
|
+
} catch (err) {
|
|
58160
|
+
logger.warn("retired settings row \u2014 successor unreadable, row left untouched", { meta: {
|
|
58161
|
+
addon: spec.owner,
|
|
58162
|
+
row: spec.row,
|
|
58163
|
+
successor,
|
|
58164
|
+
error: errText(err)
|
|
58165
|
+
} });
|
|
58166
|
+
count = 0;
|
|
58167
|
+
}
|
|
58168
|
+
evidence.set(successor, count);
|
|
58169
|
+
}
|
|
58170
|
+
if (count === 0) {
|
|
58171
|
+
logger.warn("retired settings row kept \u2014 its successor is empty, migration has not run", { meta: {
|
|
58172
|
+
addon: spec.owner,
|
|
58173
|
+
collection: spec.collection,
|
|
58174
|
+
row: spec.row,
|
|
58175
|
+
successor
|
|
58176
|
+
} });
|
|
58177
|
+
continue;
|
|
58178
|
+
}
|
|
58179
|
+
await store.deleteRow(spec);
|
|
58180
|
+
const result = {
|
|
58181
|
+
owner: spec.owner,
|
|
58182
|
+
collection: spec.collection,
|
|
58183
|
+
row: spec.row
|
|
58184
|
+
};
|
|
58185
|
+
results.push(result);
|
|
58186
|
+
logger.info("purged a retired settings row", { meta: {
|
|
58187
|
+
...result,
|
|
58188
|
+
reason: spec.reason
|
|
58189
|
+
} });
|
|
58190
|
+
}
|
|
58191
|
+
return results;
|
|
58192
|
+
}
|
|
58193
|
+
function errText(err) {
|
|
58194
|
+
return err instanceof Error ? err.message : String(err);
|
|
58195
|
+
}
|
|
58196
|
+
Object.defineProperty(exports, "purgeRetiredSettingsKeys", {
|
|
58197
|
+
enumerable: true,
|
|
58198
|
+
get: function() {
|
|
58199
|
+
return purgeRetiredSettingsKeys;
|
|
58200
|
+
}
|
|
58201
|
+
});
|
|
58202
|
+
Object.defineProperty(exports, "purgeRetiredSettingsRows", {
|
|
58203
|
+
enumerable: true,
|
|
58204
|
+
get: function() {
|
|
58205
|
+
return purgeRetiredSettingsRows;
|
|
58206
|
+
}
|
|
58207
|
+
});
|
|
58208
|
+
Object.defineProperty(exports, "retiredKeyStoreOf", {
|
|
58209
|
+
enumerable: true,
|
|
58210
|
+
get: function() {
|
|
58211
|
+
return retiredKeyStoreOf;
|
|
58212
|
+
}
|
|
58213
|
+
});
|
|
58214
|
+
}
|
|
58215
|
+
});
|
|
58216
|
+
|
|
57915
58217
|
// ../types/dist/canonical-hash-DNV8S5ET.js
|
|
57916
58218
|
var require_canonical_hash_DNV8S5ET = __commonJS({
|
|
57917
58219
|
"../types/dist/canonical-hash-DNV8S5ET.js"(exports) {
|
|
@@ -59735,7 +60037,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
59735
60037
|
[Symbol.toStringTag]: { value: "Module" }
|
|
59736
60038
|
});
|
|
59737
60039
|
require_chunk_Cek0wNdY();
|
|
59738
|
-
var require_dist10 =
|
|
60040
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
60041
|
+
var require_retired_settings_keys = require_retired_settings_keys_CYDGGkyM();
|
|
59739
60042
|
var node_crypto = __require("crypto");
|
|
59740
60043
|
var _camstack_types_node = require_node();
|
|
59741
60044
|
var JOB_HISTORY = 20;
|
|
@@ -60023,6 +60326,311 @@ var require_device_manager_addon = __commonJS({
|
|
|
60023
60326
|
}
|
|
60024
60327
|
}
|
|
60025
60328
|
};
|
|
60329
|
+
function tagContribution(contribution, capName, addonId, kind) {
|
|
60330
|
+
const source = kind === "settings" ? "settings" : "live";
|
|
60331
|
+
return {
|
|
60332
|
+
...contribution.tabs ? { tabs: [...contribution.tabs] } : {},
|
|
60333
|
+
sections: contribution.sections.map((section) => ({
|
|
60334
|
+
...section,
|
|
60335
|
+
fields: section.fields.map((field) => tagField(field, capName, addonId, source, kind))
|
|
60336
|
+
}))
|
|
60337
|
+
};
|
|
60338
|
+
}
|
|
60339
|
+
function isFieldRecord(value) {
|
|
60340
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
60341
|
+
}
|
|
60342
|
+
function toWireShape(input) {
|
|
60343
|
+
const out = { sections: input.sections.map((s) => ({
|
|
60344
|
+
id: s.id,
|
|
60345
|
+
title: s.title,
|
|
60346
|
+
...s.description !== void 0 ? { description: s.description } : {},
|
|
60347
|
+
...s.style !== void 0 ? { style: s.style } : {},
|
|
60348
|
+
...s.defaultCollapsed !== void 0 ? { defaultCollapsed: s.defaultCollapsed } : {},
|
|
60349
|
+
...s.columns !== void 0 ? { columns: s.columns } : {},
|
|
60350
|
+
...s.tab !== void 0 ? { tab: s.tab } : {},
|
|
60351
|
+
...s.location !== void 0 ? { location: s.location } : {},
|
|
60352
|
+
...s.order !== void 0 ? { order: s.order } : {},
|
|
60353
|
+
fields: [...s.fields]
|
|
60354
|
+
})) };
|
|
60355
|
+
if (input.tabs) out.tabs = [...input.tabs];
|
|
60356
|
+
return out;
|
|
60357
|
+
}
|
|
60358
|
+
function tagField(field, capName, addonId, source, kind) {
|
|
60359
|
+
if (!isFieldRecord(field)) return field;
|
|
60360
|
+
const f = field;
|
|
60361
|
+
const structuralTypes = /* @__PURE__ */ new Set([
|
|
60362
|
+
"separator",
|
|
60363
|
+
"info",
|
|
60364
|
+
"button"
|
|
60365
|
+
]);
|
|
60366
|
+
if (typeof f.type === "string" && structuralTypes.has(f.type)) return field;
|
|
60367
|
+
const tagged = {
|
|
60368
|
+
...f,
|
|
60369
|
+
source
|
|
60370
|
+
};
|
|
60371
|
+
if (kind === "live" || f.readonlyField === true) tagged.readonlyField = true;
|
|
60372
|
+
else {
|
|
60373
|
+
tagged.writerCapName = capName;
|
|
60374
|
+
tagged.writerAddonId = addonId;
|
|
60375
|
+
}
|
|
60376
|
+
if (f.type === "group") {
|
|
60377
|
+
const children = Array.isArray(f.fields) ? f.fields : [];
|
|
60378
|
+
if (children.length > 0) tagged.fields = children.map((child) => tagField(child, capName, addonId, source, kind));
|
|
60379
|
+
} else if (f.type === "sub-tabs") {
|
|
60380
|
+
const rawTabs = Array.isArray(f.tabs) ? f.tabs : [];
|
|
60381
|
+
if (rawTabs.length > 0) tagged.tabs = rawTabs.map((tab) => {
|
|
60382
|
+
if (!isFieldRecord(tab)) return tab;
|
|
60383
|
+
const tabChildren = Array.isArray(tab.fields) ? tab.fields : [];
|
|
60384
|
+
return {
|
|
60385
|
+
...tab,
|
|
60386
|
+
fields: tabChildren.map((child) => tagField(child, capName, addonId, source, kind))
|
|
60387
|
+
};
|
|
60388
|
+
});
|
|
60389
|
+
}
|
|
60390
|
+
return tagged;
|
|
60391
|
+
}
|
|
60392
|
+
function mergeAggregates(parts) {
|
|
60393
|
+
const tabDecls = /* @__PURE__ */ new Map();
|
|
60394
|
+
const sections = [];
|
|
60395
|
+
const seenSectionIds = /* @__PURE__ */ new Set();
|
|
60396
|
+
for (const part of parts) {
|
|
60397
|
+
if (part.tabs) {
|
|
60398
|
+
for (const t of part.tabs) if (!tabDecls.has(t.id)) tabDecls.set(t.id, t);
|
|
60399
|
+
}
|
|
60400
|
+
for (const s of part.sections) {
|
|
60401
|
+
if (s.id !== void 0) {
|
|
60402
|
+
if (seenSectionIds.has(s.id)) continue;
|
|
60403
|
+
seenSectionIds.add(s.id);
|
|
60404
|
+
}
|
|
60405
|
+
sections.push(s);
|
|
60406
|
+
}
|
|
60407
|
+
}
|
|
60408
|
+
for (const s of sections) {
|
|
60409
|
+
const tabId = s.tab ?? "general";
|
|
60410
|
+
if (tabDecls.has(tabId)) continue;
|
|
60411
|
+
const known = require_dist10.WELL_KNOWN_TAB_MAP[tabId];
|
|
60412
|
+
if (known) tabDecls.set(tabId, {
|
|
60413
|
+
id: known.id,
|
|
60414
|
+
label: known.label,
|
|
60415
|
+
icon: known.icon,
|
|
60416
|
+
order: known.order
|
|
60417
|
+
});
|
|
60418
|
+
else tabDecls.set(tabId, {
|
|
60419
|
+
id: tabId,
|
|
60420
|
+
label: tabId,
|
|
60421
|
+
icon: "wrench",
|
|
60422
|
+
order: 100
|
|
60423
|
+
});
|
|
60424
|
+
}
|
|
60425
|
+
sections.sort((a, b) => {
|
|
60426
|
+
const tabA = a.tab ?? "general";
|
|
60427
|
+
const tabB = b.tab ?? "general";
|
|
60428
|
+
if (tabA !== tabB) {
|
|
60429
|
+
const orderA = tabDecls.get(tabA)?.order ?? 100;
|
|
60430
|
+
const orderB = tabDecls.get(tabB)?.order ?? 100;
|
|
60431
|
+
if (orderA !== orderB) return orderA - orderB;
|
|
60432
|
+
return tabA.localeCompare(tabB);
|
|
60433
|
+
}
|
|
60434
|
+
return (a.order ?? 0) - (b.order ?? 0);
|
|
60435
|
+
});
|
|
60436
|
+
const sortedTabs = [...tabDecls.values()].toSorted((a, b) => (a.order ?? 100) - (b.order ?? 100));
|
|
60437
|
+
const out = { sections };
|
|
60438
|
+
if (sortedTabs.length > 0) out.tabs = sortedTabs;
|
|
60439
|
+
return out;
|
|
60440
|
+
}
|
|
60441
|
+
function wireRemoteNativeCapSync(ctx, remoteNativeCaps) {
|
|
60442
|
+
const localNodeId = ctx.kernel.localNodeId ?? "hub";
|
|
60443
|
+
ctx.eventBus.subscribe({ category: require_dist10.EventCategory.DeviceBindingsChanged }, (event) => {
|
|
60444
|
+
const { deviceId, capName, reason, addonId, nodeId } = event.data;
|
|
60445
|
+
if (nodeId === localNodeId) return;
|
|
60446
|
+
if (reason === "native-registered") {
|
|
60447
|
+
let perDevice = remoteNativeCaps.get(deviceId);
|
|
60448
|
+
if (!perDevice) {
|
|
60449
|
+
perDevice = /* @__PURE__ */ new Map();
|
|
60450
|
+
remoteNativeCaps.set(deviceId, perDevice);
|
|
60451
|
+
}
|
|
60452
|
+
perDevice.set(capName, {
|
|
60453
|
+
addonId,
|
|
60454
|
+
nodeId
|
|
60455
|
+
});
|
|
60456
|
+
} else if (reason === "native-unregistered") {
|
|
60457
|
+
const perDevice = remoteNativeCaps.get(deviceId);
|
|
60458
|
+
if (!perDevice) return;
|
|
60459
|
+
perDevice.delete(capName);
|
|
60460
|
+
if (perDevice.size === 0) remoteNativeCaps.delete(deviceId);
|
|
60461
|
+
}
|
|
60462
|
+
});
|
|
60463
|
+
const cluster = ctx.kernel.cluster;
|
|
60464
|
+
if (cluster) cluster.broker.localBus.on("$node.disconnected", (payload) => {
|
|
60465
|
+
const gone = payload.node.id;
|
|
60466
|
+
const emptyDevices = [];
|
|
60467
|
+
for (const [deviceId, perDevice] of remoteNativeCaps) {
|
|
60468
|
+
const toDelete = [];
|
|
60469
|
+
for (const [capName, entry] of perDevice) if (entry.nodeId === gone) toDelete.push(capName);
|
|
60470
|
+
for (const capName of toDelete) perDevice.delete(capName);
|
|
60471
|
+
if (perDevice.size === 0) emptyDevices.push(deviceId);
|
|
60472
|
+
}
|
|
60473
|
+
for (const deviceId of emptyDevices) remoteNativeCaps.delete(deviceId);
|
|
60474
|
+
});
|
|
60475
|
+
}
|
|
60476
|
+
async function readBindingsStore(deps) {
|
|
60477
|
+
return { deviceBindings: (await deps.ctx.settings.readAddonStore()).deviceBindings ?? {} };
|
|
60478
|
+
}
|
|
60479
|
+
async function writeBindingsStore(deps, next) {
|
|
60480
|
+
await deps.ctx.settings.writeAddonStore({ deviceBindings: next.deviceBindings });
|
|
60481
|
+
}
|
|
60482
|
+
function resolveWrapperNodeId(_wrapperAddonId) {
|
|
60483
|
+
return "hub";
|
|
60484
|
+
}
|
|
60485
|
+
function toRoutableProviderNodeId(nodeId) {
|
|
60486
|
+
const slash = nodeId.indexOf("/");
|
|
60487
|
+
return slash === -1 ? nodeId : nodeId.slice(0, slash);
|
|
60488
|
+
}
|
|
60489
|
+
function resolveRemoteNativeCapFromRegistry(deps, capName, deviceId) {
|
|
60490
|
+
const clusterCaps = deps.ctx.kernel.listClusterNativeCapsForDevice?.(deviceId) ?? deps.ctx.kernel.listClusterNativeCaps?.();
|
|
60491
|
+
if (!clusterCaps) return null;
|
|
60492
|
+
for (const entry of clusterCaps) if (entry.capName === capName && entry.deviceId === deviceId && entry.addonId) return {
|
|
60493
|
+
addonId: entry.addonId,
|
|
60494
|
+
nodeId: entry.nodeId
|
|
60495
|
+
};
|
|
60496
|
+
return null;
|
|
60497
|
+
}
|
|
60498
|
+
function resolveDeviceType(deps, row, deviceId) {
|
|
60499
|
+
const persisted = row?.meta.type;
|
|
60500
|
+
if (typeof persisted === "string" && persisted.length > 0) return persisted;
|
|
60501
|
+
const live = deps.ctx.kernel?.deviceRegistry?.getById(deviceId)?.type;
|
|
60502
|
+
return typeof live === "string" && live.length > 0 ? live : void 0;
|
|
60503
|
+
}
|
|
60504
|
+
async function resolveDevicePresence(deps, row, deviceId) {
|
|
60505
|
+
if (deps.ctx.kernel?.deviceRegistry?.getById(deviceId)) return "present";
|
|
60506
|
+
if (row !== null) return "present";
|
|
60507
|
+
return await deps.rows.count() > 0 ? "absent" : "unknown";
|
|
60508
|
+
}
|
|
60509
|
+
function capAppliesToDeviceType(def, deviceType) {
|
|
60510
|
+
if (deviceType === void 0) return true;
|
|
60511
|
+
const declared = def?.deviceTypes;
|
|
60512
|
+
if (!declared || declared.length === 0) return true;
|
|
60513
|
+
return declared.some((t) => t === deviceType);
|
|
60514
|
+
}
|
|
60515
|
+
async function getBindings(deps, input) {
|
|
60516
|
+
const storeKey = String(input.deviceId);
|
|
60517
|
+
const perDevice = (await readBindingsStore(deps)).deviceBindings[storeKey] ?? {};
|
|
60518
|
+
const row = await deps.rows.get(input.deviceId);
|
|
60519
|
+
const deviceType = resolveDeviceType(deps, row, input.deviceId);
|
|
60520
|
+
const presence = await resolveDevicePresence(deps, row, input.deviceId);
|
|
60521
|
+
const entries = [];
|
|
60522
|
+
const seenCaps = /* @__PURE__ */ new Set();
|
|
60523
|
+
const resolveRemote = (capName) => deps.remoteNativeCaps.get(input.deviceId)?.get(capName) ?? resolveRemoteNativeCapFromRegistry(deps, capName, input.deviceId);
|
|
60524
|
+
for (const [capName, { wrapperAddonId }] of Object.entries(perDevice)) {
|
|
60525
|
+
const hubLocalNative = deps.capabilityRegistry?.getNativeAddonId(capName, input.deviceId) ?? null;
|
|
60526
|
+
const remoteNative = resolveRemote(capName);
|
|
60527
|
+
const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? "";
|
|
60528
|
+
const nativeNodeId = hubLocalNative ? deps.ctx.kernel.localNodeId ?? "hub" : remoteNative?.nodeId ?? deps.ctx.kernel.localNodeId ?? "hub";
|
|
60529
|
+
if (wrapperAddonId === null && !nativeAddonId) {
|
|
60530
|
+
seenCaps.add(capName);
|
|
60531
|
+
continue;
|
|
60532
|
+
}
|
|
60533
|
+
entries.push({
|
|
60534
|
+
capName,
|
|
60535
|
+
kind: wrapperAddonId ? "wrapped" : "native",
|
|
60536
|
+
providerAddonId: wrapperAddonId ?? nativeAddonId,
|
|
60537
|
+
providerNodeId: wrapperAddonId ? resolveWrapperNodeId(wrapperAddonId) : toRoutableProviderNodeId(nativeNodeId),
|
|
60538
|
+
nativeAddonId
|
|
60539
|
+
});
|
|
60540
|
+
seenCaps.add(capName);
|
|
60541
|
+
}
|
|
60542
|
+
if (presence === "absent") deps.ctx.logger.debug("bindings requested for absent device \u2014 returning none", { tags: { deviceId: input.deviceId } });
|
|
60543
|
+
else if (deps.capabilityRegistry) {
|
|
60544
|
+
const skippedForType = [];
|
|
60545
|
+
for (const capName of deps.capabilityRegistry.getCapsWithDefaultWrapper()) {
|
|
60546
|
+
if (seenCaps.has(capName)) continue;
|
|
60547
|
+
if (!capAppliesToDeviceType(deps.capabilityRegistry.getDefinition(capName), deviceType)) {
|
|
60548
|
+
skippedForType.push(capName);
|
|
60549
|
+
continue;
|
|
60550
|
+
}
|
|
60551
|
+
const defaultWrapperAddonId = deps.capabilityRegistry.getDefaultWrapperForCap(capName);
|
|
60552
|
+
if (!defaultWrapperAddonId) continue;
|
|
60553
|
+
const hubLocalNative = deps.capabilityRegistry.getNativeAddonId(capName, input.deviceId) ?? null;
|
|
60554
|
+
const remoteNative = resolveRemote(capName);
|
|
60555
|
+
const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? "";
|
|
60556
|
+
entries.push({
|
|
60557
|
+
capName,
|
|
60558
|
+
kind: "wrapped",
|
|
60559
|
+
providerAddonId: defaultWrapperAddonId,
|
|
60560
|
+
providerNodeId: resolveWrapperNodeId(defaultWrapperAddonId),
|
|
60561
|
+
nativeAddonId
|
|
60562
|
+
});
|
|
60563
|
+
seenCaps.add(capName);
|
|
60564
|
+
}
|
|
60565
|
+
if (skippedForType.length > 0) deps.ctx.logger.debug("getBindings: default wrappers skipped \u2014 deviceTypes mismatch", {
|
|
60566
|
+
tags: { deviceId: input.deviceId },
|
|
60567
|
+
meta: {
|
|
60568
|
+
deviceType,
|
|
60569
|
+
skipped: skippedForType
|
|
60570
|
+
}
|
|
60571
|
+
});
|
|
60572
|
+
}
|
|
60573
|
+
if (deps.capabilityRegistry) for (const capName of deps.capabilityRegistry.getNativeCapsForDevice(input.deviceId)) {
|
|
60574
|
+
if (seenCaps.has(capName)) continue;
|
|
60575
|
+
const nativeAddonId = deps.capabilityRegistry.getNativeAddonId(capName, input.deviceId) ?? "";
|
|
60576
|
+
entries.push({
|
|
60577
|
+
capName,
|
|
60578
|
+
kind: "native",
|
|
60579
|
+
providerAddonId: nativeAddonId,
|
|
60580
|
+
providerNodeId: deps.ctx.kernel.localNodeId ?? "hub",
|
|
60581
|
+
nativeAddonId
|
|
60582
|
+
});
|
|
60583
|
+
seenCaps.add(capName);
|
|
60584
|
+
}
|
|
60585
|
+
const pushFed = deps.remoteNativeCaps.get(input.deviceId);
|
|
60586
|
+
if (pushFed) for (const [capName, info] of pushFed) {
|
|
60587
|
+
if (seenCaps.has(capName)) continue;
|
|
60588
|
+
entries.push({
|
|
60589
|
+
capName,
|
|
60590
|
+
kind: "native",
|
|
60591
|
+
providerAddonId: info.addonId,
|
|
60592
|
+
providerNodeId: toRoutableProviderNodeId(info.nodeId),
|
|
60593
|
+
nativeAddonId: info.addonId
|
|
60594
|
+
});
|
|
60595
|
+
seenCaps.add(capName);
|
|
60596
|
+
}
|
|
60597
|
+
const clusterCaps = deps.ctx.kernel.listClusterNativeCapsForDevice?.(input.deviceId) ?? deps.ctx.kernel.listClusterNativeCaps?.();
|
|
60598
|
+
if (clusterCaps) for (const entry of clusterCaps) {
|
|
60599
|
+
if (entry.deviceId !== input.deviceId) continue;
|
|
60600
|
+
if (seenCaps.has(entry.capName)) continue;
|
|
60601
|
+
if (!entry.addonId) continue;
|
|
60602
|
+
const localNodeId = deps.ctx.kernel.localNodeId ?? "hub";
|
|
60603
|
+
if (entry.nodeId === localNodeId) continue;
|
|
60604
|
+
entries.push({
|
|
60605
|
+
capName: entry.capName,
|
|
60606
|
+
kind: "native",
|
|
60607
|
+
providerAddonId: entry.addonId,
|
|
60608
|
+
providerNodeId: toRoutableProviderNodeId(entry.nodeId),
|
|
60609
|
+
nativeAddonId: entry.addonId
|
|
60610
|
+
});
|
|
60611
|
+
seenCaps.add(entry.capName);
|
|
60612
|
+
}
|
|
60613
|
+
return {
|
|
60614
|
+
deviceId: input.deviceId,
|
|
60615
|
+
entries
|
|
60616
|
+
};
|
|
60617
|
+
}
|
|
60618
|
+
async function getAllBindings(deps) {
|
|
60619
|
+
const ids = /* @__PURE__ */ new Set();
|
|
60620
|
+
for (const row of await deps.rows.listAll()) ids.add(row.meta.id);
|
|
60621
|
+
const registered = deps.ctx.kernel?.deviceRegistry?.getAll() ?? [];
|
|
60622
|
+
for (const device of registered) ids.add(device.id);
|
|
60623
|
+
if (ids.size === 0) {
|
|
60624
|
+
deps.ctx.logger.warn("getAllBindings found no devices \u2014 warm boot will see nothing", { meta: { hasRegistry: deps.ctx.kernel?.deviceRegistry !== void 0 } });
|
|
60625
|
+
return [];
|
|
60626
|
+
}
|
|
60627
|
+
const out = [];
|
|
60628
|
+
for (const deviceId of [...ids].sort((a, b) => a - b)) out.push(await getBindings(deps, { deviceId }));
|
|
60629
|
+
return out;
|
|
60630
|
+
}
|
|
60631
|
+
async function lookupPersistedStableId(deps, deviceId) {
|
|
60632
|
+
return (await deps.rows.get(deviceId))?.meta.stableId;
|
|
60633
|
+
}
|
|
60026
60634
|
var MODE_LABELS = {
|
|
60027
60635
|
auto: "Auto",
|
|
60028
60636
|
day: "Day",
|
|
@@ -60345,118 +60953,6 @@ var require_device_manager_addon = __commonJS({
|
|
|
60345
60953
|
if (reducer.kind !== "settings") throw new Error(`device-config: builderId "${builderId}" is not a settings-based derived-form`);
|
|
60346
60954
|
return reducer.parsePatch(patch);
|
|
60347
60955
|
}
|
|
60348
|
-
function tagContribution(contribution, capName, addonId, kind) {
|
|
60349
|
-
const source = kind === "settings" ? "settings" : "live";
|
|
60350
|
-
return {
|
|
60351
|
-
...contribution.tabs ? { tabs: [...contribution.tabs] } : {},
|
|
60352
|
-
sections: contribution.sections.map((section) => ({
|
|
60353
|
-
...section,
|
|
60354
|
-
fields: section.fields.map((field) => tagField(field, capName, addonId, source, kind))
|
|
60355
|
-
}))
|
|
60356
|
-
};
|
|
60357
|
-
}
|
|
60358
|
-
function isFieldRecord(value) {
|
|
60359
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
60360
|
-
}
|
|
60361
|
-
function toWireShape(input) {
|
|
60362
|
-
const out = { sections: input.sections.map((s) => ({
|
|
60363
|
-
id: s.id,
|
|
60364
|
-
title: s.title,
|
|
60365
|
-
...s.description !== void 0 ? { description: s.description } : {},
|
|
60366
|
-
...s.style !== void 0 ? { style: s.style } : {},
|
|
60367
|
-
...s.defaultCollapsed !== void 0 ? { defaultCollapsed: s.defaultCollapsed } : {},
|
|
60368
|
-
...s.columns !== void 0 ? { columns: s.columns } : {},
|
|
60369
|
-
...s.tab !== void 0 ? { tab: s.tab } : {},
|
|
60370
|
-
...s.location !== void 0 ? { location: s.location } : {},
|
|
60371
|
-
...s.order !== void 0 ? { order: s.order } : {},
|
|
60372
|
-
fields: [...s.fields]
|
|
60373
|
-
})) };
|
|
60374
|
-
if (input.tabs) out.tabs = [...input.tabs];
|
|
60375
|
-
return out;
|
|
60376
|
-
}
|
|
60377
|
-
function tagField(field, capName, addonId, source, kind) {
|
|
60378
|
-
if (!isFieldRecord(field)) return field;
|
|
60379
|
-
const f = field;
|
|
60380
|
-
const structuralTypes = /* @__PURE__ */ new Set([
|
|
60381
|
-
"separator",
|
|
60382
|
-
"info",
|
|
60383
|
-
"button"
|
|
60384
|
-
]);
|
|
60385
|
-
if (typeof f.type === "string" && structuralTypes.has(f.type)) return field;
|
|
60386
|
-
const tagged = {
|
|
60387
|
-
...f,
|
|
60388
|
-
source
|
|
60389
|
-
};
|
|
60390
|
-
if (kind === "live" || f.readonlyField === true) tagged.readonlyField = true;
|
|
60391
|
-
else {
|
|
60392
|
-
tagged.writerCapName = capName;
|
|
60393
|
-
tagged.writerAddonId = addonId;
|
|
60394
|
-
}
|
|
60395
|
-
if (f.type === "group") {
|
|
60396
|
-
const children = Array.isArray(f.fields) ? f.fields : [];
|
|
60397
|
-
if (children.length > 0) tagged.fields = children.map((child) => tagField(child, capName, addonId, source, kind));
|
|
60398
|
-
} else if (f.type === "sub-tabs") {
|
|
60399
|
-
const rawTabs = Array.isArray(f.tabs) ? f.tabs : [];
|
|
60400
|
-
if (rawTabs.length > 0) tagged.tabs = rawTabs.map((tab) => {
|
|
60401
|
-
if (!isFieldRecord(tab)) return tab;
|
|
60402
|
-
const tabChildren = Array.isArray(tab.fields) ? tab.fields : [];
|
|
60403
|
-
return {
|
|
60404
|
-
...tab,
|
|
60405
|
-
fields: tabChildren.map((child) => tagField(child, capName, addonId, source, kind))
|
|
60406
|
-
};
|
|
60407
|
-
});
|
|
60408
|
-
}
|
|
60409
|
-
return tagged;
|
|
60410
|
-
}
|
|
60411
|
-
function mergeAggregates(parts) {
|
|
60412
|
-
const tabDecls = /* @__PURE__ */ new Map();
|
|
60413
|
-
const sections = [];
|
|
60414
|
-
const seenSectionIds = /* @__PURE__ */ new Set();
|
|
60415
|
-
for (const part of parts) {
|
|
60416
|
-
if (part.tabs) {
|
|
60417
|
-
for (const t of part.tabs) if (!tabDecls.has(t.id)) tabDecls.set(t.id, t);
|
|
60418
|
-
}
|
|
60419
|
-
for (const s of part.sections) {
|
|
60420
|
-
if (s.id !== void 0) {
|
|
60421
|
-
if (seenSectionIds.has(s.id)) continue;
|
|
60422
|
-
seenSectionIds.add(s.id);
|
|
60423
|
-
}
|
|
60424
|
-
sections.push(s);
|
|
60425
|
-
}
|
|
60426
|
-
}
|
|
60427
|
-
for (const s of sections) {
|
|
60428
|
-
const tabId = s.tab ?? "general";
|
|
60429
|
-
if (tabDecls.has(tabId)) continue;
|
|
60430
|
-
const known = require_dist10.WELL_KNOWN_TAB_MAP[tabId];
|
|
60431
|
-
if (known) tabDecls.set(tabId, {
|
|
60432
|
-
id: known.id,
|
|
60433
|
-
label: known.label,
|
|
60434
|
-
icon: known.icon,
|
|
60435
|
-
order: known.order
|
|
60436
|
-
});
|
|
60437
|
-
else tabDecls.set(tabId, {
|
|
60438
|
-
id: tabId,
|
|
60439
|
-
label: tabId,
|
|
60440
|
-
icon: "wrench",
|
|
60441
|
-
order: 100
|
|
60442
|
-
});
|
|
60443
|
-
}
|
|
60444
|
-
sections.sort((a, b) => {
|
|
60445
|
-
const tabA = a.tab ?? "general";
|
|
60446
|
-
const tabB = b.tab ?? "general";
|
|
60447
|
-
if (tabA !== tabB) {
|
|
60448
|
-
const orderA = tabDecls.get(tabA)?.order ?? 100;
|
|
60449
|
-
const orderB = tabDecls.get(tabB)?.order ?? 100;
|
|
60450
|
-
if (orderA !== orderB) return orderA - orderB;
|
|
60451
|
-
return tabA.localeCompare(tabB);
|
|
60452
|
-
}
|
|
60453
|
-
return (a.order ?? 0) - (b.order ?? 0);
|
|
60454
|
-
});
|
|
60455
|
-
const sortedTabs = [...tabDecls.values()].toSorted((a, b) => (a.order ?? 100) - (b.order ?? 100));
|
|
60456
|
-
const out = { sections };
|
|
60457
|
-
if (sortedTabs.length > 0) out.tabs = sortedTabs;
|
|
60458
|
-
return out;
|
|
60459
|
-
}
|
|
60460
60956
|
function isTransientMoleculerError(err) {
|
|
60461
60957
|
if (err !== null && typeof err === "object") {
|
|
60462
60958
|
const e = err;
|
|
@@ -60548,22 +61044,15 @@ var require_device_manager_addon = __commonJS({
|
|
|
60548
61044
|
}
|
|
60549
61045
|
async function listPersistedByAddon(pctx, input) {
|
|
60550
61046
|
const { addonId } = input;
|
|
60551
|
-
|
|
60552
|
-
|
|
60553
|
-
|
|
60554
|
-
|
|
60555
|
-
|
|
60556
|
-
|
|
60557
|
-
|
|
60558
|
-
|
|
60559
|
-
|
|
60560
|
-
type: m.type,
|
|
60561
|
-
name: m.name,
|
|
60562
|
-
location: m.location ?? null,
|
|
60563
|
-
disabled: m.disabled ?? false,
|
|
60564
|
-
parentDeviceId: m.parentDeviceId
|
|
60565
|
-
};
|
|
60566
|
-
});
|
|
61047
|
+
return (await pctx.metaStore.rows.listByAddon(addonId)).map(({ meta: m }) => ({
|
|
61048
|
+
id: m.id,
|
|
61049
|
+
stableId: m.stableId,
|
|
61050
|
+
type: m.type,
|
|
61051
|
+
name: m.name,
|
|
61052
|
+
location: m.location ?? null,
|
|
61053
|
+
disabled: m.disabled ?? false,
|
|
61054
|
+
parentDeviceId: m.parentDeviceId
|
|
61055
|
+
}));
|
|
60567
61056
|
}
|
|
60568
61057
|
async function listAll(pctx, input) {
|
|
60569
61058
|
const ownerFilter = Reflect.get(input, "addonId");
|
|
@@ -60573,7 +61062,9 @@ var require_device_manager_addon = __commonJS({
|
|
|
60573
61062
|
const camerasOnly = input.isCamera === true;
|
|
60574
61063
|
const results = [];
|
|
60575
61064
|
const seen = /* @__PURE__ */ new Set();
|
|
60576
|
-
const
|
|
61065
|
+
const fleet = addonId ? await pctx.metaStore.rows.listByAddon(addonId) : await pctx.metaStore.rows.listAll();
|
|
61066
|
+
const rowById = /* @__PURE__ */ new Map();
|
|
61067
|
+
for (const row of fleet) rowById.set(row.meta.id, row);
|
|
60577
61068
|
if (pctx.registry) {
|
|
60578
61069
|
const liveEntries = addonId ? pctx.registry.getAllForAddon(addonId).map((device) => ({
|
|
60579
61070
|
addonId,
|
|
@@ -60581,7 +61072,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
60581
61072
|
})) : pctx.registry.getAllWithAddonId();
|
|
60582
61073
|
for (const { addonId: aid, device } of liveEntries) {
|
|
60583
61074
|
const key = String(device.id);
|
|
60584
|
-
const
|
|
61075
|
+
const row = rowById.get(device.id);
|
|
61076
|
+
const info = toDeviceInfo(aid, device, row?.metadata ?? null, row?.meta ?? null);
|
|
60585
61077
|
seen.add(key);
|
|
60586
61078
|
if (camerasOnly && !info.isCamera) continue;
|
|
60587
61079
|
results.push(slim ? {
|
|
@@ -60591,38 +61083,36 @@ var require_device_manager_addon = __commonJS({
|
|
|
60591
61083
|
} : info);
|
|
60592
61084
|
}
|
|
60593
61085
|
}
|
|
60594
|
-
const
|
|
60595
|
-
|
|
60596
|
-
|
|
60597
|
-
|
|
60598
|
-
|
|
60599
|
-
const key = String(m.id);
|
|
60600
|
-
if (seen.has(key)) continue;
|
|
61086
|
+
for (const row of fleet) {
|
|
61087
|
+
const m = row.meta;
|
|
61088
|
+
const aid = m.addonId;
|
|
61089
|
+
const stableId = m.stableId;
|
|
61090
|
+
if (seen.has(String(m.id))) continue;
|
|
60601
61091
|
const persistedType = m.type;
|
|
60602
61092
|
if (camerasOnly && persistedType !== require_dist10.DeviceType.Camera) continue;
|
|
60603
61093
|
const persistedConfig = slim ? {} : await pctx.settings.readDeviceStore(m.id);
|
|
60604
|
-
const metadata = slim ? null :
|
|
61094
|
+
const metadata = slim ? null : row.metadata;
|
|
60605
61095
|
results.push({
|
|
60606
61096
|
id: m.id,
|
|
60607
61097
|
stableId,
|
|
60608
61098
|
addonId: aid,
|
|
60609
61099
|
type: persistedType,
|
|
60610
|
-
name: m
|
|
60611
|
-
location: m
|
|
60612
|
-
disabled: m
|
|
60613
|
-
parentDeviceId: m
|
|
60614
|
-
role: toDeviceRole(m
|
|
61100
|
+
name: m.name,
|
|
61101
|
+
location: m.location ?? null,
|
|
61102
|
+
disabled: m.disabled ?? false,
|
|
61103
|
+
parentDeviceId: m.parentDeviceId ?? null,
|
|
61104
|
+
role: toDeviceRole(m.role),
|
|
60615
61105
|
online: pctx.host.resolveDeviceOnline(m.id, pctx.registry !== null),
|
|
60616
61106
|
probed: pctx.host.resolveDeviceProbed(m.id),
|
|
60617
|
-
features: persistedFeatures(m
|
|
61107
|
+
features: persistedFeatures(m.features),
|
|
60618
61108
|
isCamera: persistedType === require_dist10.DeviceType.Camera,
|
|
60619
61109
|
config: persistedConfig ?? {},
|
|
60620
61110
|
metadata,
|
|
60621
|
-
...m
|
|
60622
|
-
...m
|
|
60623
|
-
...m
|
|
60624
|
-
...m
|
|
60625
|
-
...m
|
|
61111
|
+
...m.integrationId !== void 0 ? { integrationId: m.integrationId } : {},
|
|
61112
|
+
...m.linkDeviceId !== void 0 ? { linkDeviceId: m.linkDeviceId } : {},
|
|
61113
|
+
...m.primaryChildEntityId !== void 0 ? { primaryChildEntityId: m.primaryChildEntityId } : {},
|
|
61114
|
+
...m.childLayout !== void 0 ? { childLayout: m.childLayout } : {},
|
|
61115
|
+
...m.display !== void 0 ? { display: m.display } : {},
|
|
60626
61116
|
...(() => {
|
|
60627
61117
|
if (slim) return {};
|
|
60628
61118
|
const si = buildSourceInfoFromConfig(persistedConfig ?? {}, stableId, aid);
|
|
@@ -60637,19 +61127,15 @@ var require_device_manager_addon = __commonJS({
|
|
|
60637
61127
|
if (pctx.registry) {
|
|
60638
61128
|
const found = resolveDeviceById(pctx.registry, deviceId);
|
|
60639
61129
|
if (found) {
|
|
60640
|
-
const
|
|
60641
|
-
|
|
60642
|
-
const metadata2 = map[key2] ?? null;
|
|
60643
|
-
const metaRow = metaMap[key2] ?? null;
|
|
60644
|
-
return toDeviceInfo(found.addonId, found.device, metadata2, metaRow);
|
|
61130
|
+
const row2 = await pctx.metaStore.getRow(found.device.id);
|
|
61131
|
+
return toDeviceInfo(found.addonId, found.device, row2?.metadata ?? null, row2?.meta ?? null);
|
|
60645
61132
|
}
|
|
60646
61133
|
}
|
|
60647
|
-
const
|
|
60648
|
-
if (
|
|
60649
|
-
const {
|
|
61134
|
+
const row = await pctx.metaStore.getRow(deviceId);
|
|
61135
|
+
if (row === null) return null;
|
|
61136
|
+
const { meta: m, metadata } = row;
|
|
61137
|
+
const { addonId: aid, stableId } = m;
|
|
60650
61138
|
const persistedConfig = await pctx.settings.readDeviceStore(m.id);
|
|
60651
|
-
const key = String(deviceId);
|
|
60652
|
-
const metadata = (await pctx.metaStore.readMetadataMap())[key] ?? null;
|
|
60653
61139
|
const sourceInfoGetDevice = buildSourceInfoFromConfig(persistedConfig ?? {}, stableId, aid);
|
|
60654
61140
|
return {
|
|
60655
61141
|
id: deviceId,
|
|
@@ -60688,26 +61174,25 @@ var require_device_manager_addon = __commonJS({
|
|
|
60688
61174
|
}
|
|
60689
61175
|
const results = [];
|
|
60690
61176
|
const seen = /* @__PURE__ */ new Set();
|
|
60691
|
-
const
|
|
61177
|
+
const childRows = await pctx.metaStore.rows.listByParent(parentDeviceId);
|
|
61178
|
+
const rowById = /* @__PURE__ */ new Map();
|
|
61179
|
+
for (const row of childRows) rowById.set(row.meta.id, row);
|
|
60692
61180
|
if (pctx.registry) {
|
|
60693
61181
|
const liveChildren = pctx.registry.getChildren(parentDeviceId);
|
|
60694
61182
|
for (const device of liveChildren) {
|
|
60695
61183
|
const key = String(device.id);
|
|
60696
|
-
const
|
|
60697
|
-
|
|
60698
|
-
results.push(toDeviceInfo(ownerAddonId, device, metadata, metaRow));
|
|
61184
|
+
const row = rowById.get(device.id);
|
|
61185
|
+
results.push(toDeviceInfo(ownerAddonId, device, row?.metadata ?? null, row?.meta ?? null));
|
|
60699
61186
|
seen.add(key);
|
|
60700
61187
|
}
|
|
60701
61188
|
}
|
|
60702
|
-
const
|
|
60703
|
-
|
|
60704
|
-
|
|
60705
|
-
for (const childStableId of persistedChildren) {
|
|
60706
|
-
const m = ownerMetaByStableId.get(childStableId);
|
|
61189
|
+
for (const row of childRows) {
|
|
61190
|
+
const m = row.meta;
|
|
61191
|
+
const childStableId = m.stableId;
|
|
60707
61192
|
const key = String(m.id);
|
|
60708
61193
|
if (seen.has(key)) continue;
|
|
60709
61194
|
const persistedConfig = await pctx.settings.readDeviceStore(m.id);
|
|
60710
|
-
const metadata =
|
|
61195
|
+
const metadata = row.metadata;
|
|
60711
61196
|
const sourceInfoChild = buildSourceInfoFromConfig(persistedConfig ?? {}, childStableId, ownerAddonId);
|
|
60712
61197
|
results.push({
|
|
60713
61198
|
id: m.id,
|
|
@@ -60851,16 +61336,10 @@ var require_device_manager_addon = __commonJS({
|
|
|
60851
61336
|
}
|
|
60852
61337
|
async function removeByIntegration(pctx, input) {
|
|
60853
61338
|
const { integrationId } = input;
|
|
60854
|
-
const
|
|
60855
|
-
const parentKeys = Object.keys(meta).filter((key) => {
|
|
60856
|
-
const m = meta[key];
|
|
60857
|
-
return m !== void 0 && m.integrationId === integrationId && m.parentDeviceId === null;
|
|
60858
|
-
});
|
|
61339
|
+
const parents = (await pctx.metaStore.rows.listByIntegration(integrationId)).filter((row) => row.meta.parentDeviceId === null);
|
|
60859
61340
|
let removed = 0;
|
|
60860
|
-
for (const
|
|
60861
|
-
|
|
60862
|
-
if (!m) continue;
|
|
60863
|
-
await pctx.provider.remove({ deviceId: m.id });
|
|
61341
|
+
for (const parent of parents) {
|
|
61342
|
+
await pctx.provider.remove({ deviceId: parent.meta.id });
|
|
60864
61343
|
removed++;
|
|
60865
61344
|
}
|
|
60866
61345
|
return { removed };
|
|
@@ -61249,204 +61728,6 @@ var require_device_manager_addon = __commonJS({
|
|
|
61249
61728
|
driverPatch
|
|
61250
61729
|
};
|
|
61251
61730
|
}
|
|
61252
|
-
function wireRemoteNativeCapSync(ctx, remoteNativeCaps) {
|
|
61253
|
-
const localNodeId = ctx.kernel.localNodeId ?? "hub";
|
|
61254
|
-
ctx.eventBus.subscribe({ category: require_dist10.EventCategory.DeviceBindingsChanged }, (event) => {
|
|
61255
|
-
const { deviceId, capName, reason, addonId, nodeId } = event.data;
|
|
61256
|
-
if (nodeId === localNodeId) return;
|
|
61257
|
-
if (reason === "native-registered") {
|
|
61258
|
-
let perDevice = remoteNativeCaps.get(deviceId);
|
|
61259
|
-
if (!perDevice) {
|
|
61260
|
-
perDevice = /* @__PURE__ */ new Map();
|
|
61261
|
-
remoteNativeCaps.set(deviceId, perDevice);
|
|
61262
|
-
}
|
|
61263
|
-
perDevice.set(capName, {
|
|
61264
|
-
addonId,
|
|
61265
|
-
nodeId
|
|
61266
|
-
});
|
|
61267
|
-
} else if (reason === "native-unregistered") {
|
|
61268
|
-
const perDevice = remoteNativeCaps.get(deviceId);
|
|
61269
|
-
if (!perDevice) return;
|
|
61270
|
-
perDevice.delete(capName);
|
|
61271
|
-
if (perDevice.size === 0) remoteNativeCaps.delete(deviceId);
|
|
61272
|
-
}
|
|
61273
|
-
});
|
|
61274
|
-
const cluster = ctx.kernel.cluster;
|
|
61275
|
-
if (cluster) cluster.broker.localBus.on("$node.disconnected", (payload) => {
|
|
61276
|
-
const gone = payload.node.id;
|
|
61277
|
-
const emptyDevices = [];
|
|
61278
|
-
for (const [deviceId, perDevice] of remoteNativeCaps) {
|
|
61279
|
-
const toDelete = [];
|
|
61280
|
-
for (const [capName, entry] of perDevice) if (entry.nodeId === gone) toDelete.push(capName);
|
|
61281
|
-
for (const capName of toDelete) perDevice.delete(capName);
|
|
61282
|
-
if (perDevice.size === 0) emptyDevices.push(deviceId);
|
|
61283
|
-
}
|
|
61284
|
-
for (const deviceId of emptyDevices) remoteNativeCaps.delete(deviceId);
|
|
61285
|
-
});
|
|
61286
|
-
}
|
|
61287
|
-
async function readBindingsStore(deps) {
|
|
61288
|
-
return { deviceBindings: (await deps.ctx.settings.readAddonStore()).deviceBindings ?? {} };
|
|
61289
|
-
}
|
|
61290
|
-
async function writeBindingsStore(deps, next) {
|
|
61291
|
-
await deps.ctx.settings.writeAddonStore({ deviceBindings: next.deviceBindings });
|
|
61292
|
-
}
|
|
61293
|
-
function resolveWrapperNodeId(_wrapperAddonId) {
|
|
61294
|
-
return "hub";
|
|
61295
|
-
}
|
|
61296
|
-
function toRoutableProviderNodeId(nodeId) {
|
|
61297
|
-
const slash = nodeId.indexOf("/");
|
|
61298
|
-
return slash === -1 ? nodeId : nodeId.slice(0, slash);
|
|
61299
|
-
}
|
|
61300
|
-
function resolveRemoteNativeCapFromRegistry(deps, capName, deviceId) {
|
|
61301
|
-
const clusterCaps = deps.ctx.kernel.listClusterNativeCapsForDevice?.(deviceId) ?? deps.ctx.kernel.listClusterNativeCaps?.();
|
|
61302
|
-
if (!clusterCaps) return null;
|
|
61303
|
-
for (const entry of clusterCaps) if (entry.capName === capName && entry.deviceId === deviceId && entry.addonId) return {
|
|
61304
|
-
addonId: entry.addonId,
|
|
61305
|
-
nodeId: entry.nodeId
|
|
61306
|
-
};
|
|
61307
|
-
return null;
|
|
61308
|
-
}
|
|
61309
|
-
function resolveDeviceType(deps, rawStore, deviceId) {
|
|
61310
|
-
const persisted = rawStore.deviceMeta?.[String(deviceId)]?.type;
|
|
61311
|
-
if (typeof persisted === "string" && persisted.length > 0) return persisted;
|
|
61312
|
-
const live = deps.ctx.kernel?.deviceRegistry?.getById(deviceId)?.type;
|
|
61313
|
-
return typeof live === "string" && live.length > 0 ? live : void 0;
|
|
61314
|
-
}
|
|
61315
|
-
function resolveDevicePresence(deps, rawStore, deviceId) {
|
|
61316
|
-
if (deps.ctx.kernel?.deviceRegistry?.getById(deviceId)) return "present";
|
|
61317
|
-
const meta = rawStore.deviceMeta;
|
|
61318
|
-
if (meta && Object.keys(meta).length > 0) return meta[String(deviceId)] === void 0 ? "absent" : "present";
|
|
61319
|
-
return "unknown";
|
|
61320
|
-
}
|
|
61321
|
-
function capAppliesToDeviceType(def, deviceType) {
|
|
61322
|
-
if (deviceType === void 0) return true;
|
|
61323
|
-
const declared = def?.deviceTypes;
|
|
61324
|
-
if (!declared || declared.length === 0) return true;
|
|
61325
|
-
return declared.some((t) => t === deviceType);
|
|
61326
|
-
}
|
|
61327
|
-
async function getBindings(deps, input) {
|
|
61328
|
-
const storeKey = String(input.deviceId);
|
|
61329
|
-
const rawStore = await deps.ctx.settings.readAddonStore();
|
|
61330
|
-
const perDevice = (rawStore.deviceBindings ?? {})[storeKey] ?? {};
|
|
61331
|
-
const deviceType = resolveDeviceType(deps, rawStore, input.deviceId);
|
|
61332
|
-
const presence = resolveDevicePresence(deps, rawStore, input.deviceId);
|
|
61333
|
-
const entries = [];
|
|
61334
|
-
const seenCaps = /* @__PURE__ */ new Set();
|
|
61335
|
-
const resolveRemote = (capName) => deps.remoteNativeCaps.get(input.deviceId)?.get(capName) ?? resolveRemoteNativeCapFromRegistry(deps, capName, input.deviceId);
|
|
61336
|
-
for (const [capName, { wrapperAddonId }] of Object.entries(perDevice)) {
|
|
61337
|
-
const hubLocalNative = deps.capabilityRegistry?.getNativeAddonId(capName, input.deviceId) ?? null;
|
|
61338
|
-
const remoteNative = resolveRemote(capName);
|
|
61339
|
-
const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? "";
|
|
61340
|
-
const nativeNodeId = hubLocalNative ? deps.ctx.kernel.localNodeId ?? "hub" : remoteNative?.nodeId ?? deps.ctx.kernel.localNodeId ?? "hub";
|
|
61341
|
-
if (wrapperAddonId === null && !nativeAddonId) {
|
|
61342
|
-
seenCaps.add(capName);
|
|
61343
|
-
continue;
|
|
61344
|
-
}
|
|
61345
|
-
entries.push({
|
|
61346
|
-
capName,
|
|
61347
|
-
kind: wrapperAddonId ? "wrapped" : "native",
|
|
61348
|
-
providerAddonId: wrapperAddonId ?? nativeAddonId,
|
|
61349
|
-
providerNodeId: wrapperAddonId ? resolveWrapperNodeId(wrapperAddonId) : toRoutableProviderNodeId(nativeNodeId),
|
|
61350
|
-
nativeAddonId
|
|
61351
|
-
});
|
|
61352
|
-
seenCaps.add(capName);
|
|
61353
|
-
}
|
|
61354
|
-
if (presence === "absent") deps.ctx.logger.debug("bindings requested for absent device \u2014 returning none", { tags: { deviceId: input.deviceId } });
|
|
61355
|
-
else if (deps.capabilityRegistry) {
|
|
61356
|
-
const skippedForType = [];
|
|
61357
|
-
for (const capName of deps.capabilityRegistry.getCapsWithDefaultWrapper()) {
|
|
61358
|
-
if (seenCaps.has(capName)) continue;
|
|
61359
|
-
if (!capAppliesToDeviceType(deps.capabilityRegistry.getDefinition(capName), deviceType)) {
|
|
61360
|
-
skippedForType.push(capName);
|
|
61361
|
-
continue;
|
|
61362
|
-
}
|
|
61363
|
-
const defaultWrapperAddonId = deps.capabilityRegistry.getDefaultWrapperForCap(capName);
|
|
61364
|
-
if (!defaultWrapperAddonId) continue;
|
|
61365
|
-
const hubLocalNative = deps.capabilityRegistry.getNativeAddonId(capName, input.deviceId) ?? null;
|
|
61366
|
-
const remoteNative = resolveRemote(capName);
|
|
61367
|
-
const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? "";
|
|
61368
|
-
entries.push({
|
|
61369
|
-
capName,
|
|
61370
|
-
kind: "wrapped",
|
|
61371
|
-
providerAddonId: defaultWrapperAddonId,
|
|
61372
|
-
providerNodeId: resolveWrapperNodeId(defaultWrapperAddonId),
|
|
61373
|
-
nativeAddonId
|
|
61374
|
-
});
|
|
61375
|
-
seenCaps.add(capName);
|
|
61376
|
-
}
|
|
61377
|
-
if (skippedForType.length > 0) deps.ctx.logger.debug("getBindings: default wrappers skipped \u2014 deviceTypes mismatch", {
|
|
61378
|
-
tags: { deviceId: input.deviceId },
|
|
61379
|
-
meta: {
|
|
61380
|
-
deviceType,
|
|
61381
|
-
skipped: skippedForType
|
|
61382
|
-
}
|
|
61383
|
-
});
|
|
61384
|
-
}
|
|
61385
|
-
if (deps.capabilityRegistry) for (const capName of deps.capabilityRegistry.getNativeCapsForDevice(input.deviceId)) {
|
|
61386
|
-
if (seenCaps.has(capName)) continue;
|
|
61387
|
-
const nativeAddonId = deps.capabilityRegistry.getNativeAddonId(capName, input.deviceId) ?? "";
|
|
61388
|
-
entries.push({
|
|
61389
|
-
capName,
|
|
61390
|
-
kind: "native",
|
|
61391
|
-
providerAddonId: nativeAddonId,
|
|
61392
|
-
providerNodeId: deps.ctx.kernel.localNodeId ?? "hub",
|
|
61393
|
-
nativeAddonId
|
|
61394
|
-
});
|
|
61395
|
-
seenCaps.add(capName);
|
|
61396
|
-
}
|
|
61397
|
-
const pushFed = deps.remoteNativeCaps.get(input.deviceId);
|
|
61398
|
-
if (pushFed) for (const [capName, info] of pushFed) {
|
|
61399
|
-
if (seenCaps.has(capName)) continue;
|
|
61400
|
-
entries.push({
|
|
61401
|
-
capName,
|
|
61402
|
-
kind: "native",
|
|
61403
|
-
providerAddonId: info.addonId,
|
|
61404
|
-
providerNodeId: toRoutableProviderNodeId(info.nodeId),
|
|
61405
|
-
nativeAddonId: info.addonId
|
|
61406
|
-
});
|
|
61407
|
-
seenCaps.add(capName);
|
|
61408
|
-
}
|
|
61409
|
-
const clusterCaps = deps.ctx.kernel.listClusterNativeCapsForDevice?.(input.deviceId) ?? deps.ctx.kernel.listClusterNativeCaps?.();
|
|
61410
|
-
if (clusterCaps) for (const entry of clusterCaps) {
|
|
61411
|
-
if (entry.deviceId !== input.deviceId) continue;
|
|
61412
|
-
if (seenCaps.has(entry.capName)) continue;
|
|
61413
|
-
if (!entry.addonId) continue;
|
|
61414
|
-
const localNodeId = deps.ctx.kernel.localNodeId ?? "hub";
|
|
61415
|
-
if (entry.nodeId === localNodeId) continue;
|
|
61416
|
-
entries.push({
|
|
61417
|
-
capName: entry.capName,
|
|
61418
|
-
kind: "native",
|
|
61419
|
-
providerAddonId: entry.addonId,
|
|
61420
|
-
providerNodeId: toRoutableProviderNodeId(entry.nodeId),
|
|
61421
|
-
nativeAddonId: entry.addonId
|
|
61422
|
-
});
|
|
61423
|
-
seenCaps.add(entry.capName);
|
|
61424
|
-
}
|
|
61425
|
-
return {
|
|
61426
|
-
deviceId: input.deviceId,
|
|
61427
|
-
entries
|
|
61428
|
-
};
|
|
61429
|
-
}
|
|
61430
|
-
async function getAllBindings(deps) {
|
|
61431
|
-
const store = await deps.ctx.settings.readAddonStore();
|
|
61432
|
-
const ids = /* @__PURE__ */ new Set();
|
|
61433
|
-
for (const key of Object.keys(store.deviceMeta ?? {})) {
|
|
61434
|
-
const id = Number(key);
|
|
61435
|
-
if (Number.isInteger(id)) ids.add(id);
|
|
61436
|
-
}
|
|
61437
|
-
const registered = deps.ctx.kernel?.deviceRegistry?.getAll() ?? [];
|
|
61438
|
-
for (const device of registered) ids.add(device.id);
|
|
61439
|
-
if (ids.size === 0) {
|
|
61440
|
-
deps.ctx.logger.warn("getAllBindings found no devices \u2014 warm boot will see nothing", { meta: { hasRegistry: deps.ctx.kernel?.deviceRegistry !== void 0 } });
|
|
61441
|
-
return [];
|
|
61442
|
-
}
|
|
61443
|
-
const out = [];
|
|
61444
|
-
for (const deviceId of [...ids].sort((a, b) => a - b)) out.push(await getBindings(deps, { deviceId }));
|
|
61445
|
-
return out;
|
|
61446
|
-
}
|
|
61447
|
-
async function lookupPersistedStableId(deps, deviceId) {
|
|
61448
|
-
return ((await deps.ctx.settings.readAddonStore()).deviceMeta ?? {})[String(deviceId)]?.stableId;
|
|
61449
|
-
}
|
|
61450
61731
|
async function getDeviceAggregate(deps, deviceId, kind) {
|
|
61451
61732
|
const registry = deps.capabilityRegistry;
|
|
61452
61733
|
if (!registry) {
|
|
@@ -62003,7 +62284,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62003
62284
|
if (wireable) caps.push(wireable);
|
|
62004
62285
|
}
|
|
62005
62286
|
if (input.includeSynthesizable === true) {
|
|
62006
|
-
const deviceType = (
|
|
62287
|
+
const deviceType = (await deps.bindingsDeps.rows.get(deviceId))?.meta.type;
|
|
62007
62288
|
if (deviceType !== void 0) for (const def of require_dist10.ALL_CAPABILITY_DEFINITIONS) {
|
|
62008
62289
|
if (seen.has(def.name)) continue;
|
|
62009
62290
|
if (def.scope !== "device" || def.kind === "wrapper") continue;
|
|
@@ -62091,20 +62372,10 @@ var require_device_manager_addon = __commonJS({
|
|
|
62091
62372
|
return chain;
|
|
62092
62373
|
}
|
|
62093
62374
|
};
|
|
62094
|
-
async function stampIntegrationId(metaStore,
|
|
62375
|
+
async function stampIntegrationId(metaStore, ctx, deviceId, integrationId) {
|
|
62095
62376
|
await metaStore.withMetaWriteLock(async () => {
|
|
62096
|
-
|
|
62097
|
-
|
|
62098
|
-
const { meta: m } = persisted;
|
|
62099
|
-
const key = String(deviceId);
|
|
62100
|
-
const allMeta = await metaStore.readMeta();
|
|
62101
|
-
await settings.writeAddonStore({ deviceMeta: {
|
|
62102
|
-
...allMeta,
|
|
62103
|
-
[key]: {
|
|
62104
|
-
...m,
|
|
62105
|
-
integrationId
|
|
62106
|
-
}
|
|
62107
|
-
} });
|
|
62377
|
+
if (!await metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] stampIntegrationId: unknown device id=${deviceId}`);
|
|
62378
|
+
await metaStore.rows.patch(deviceId, { integrationId });
|
|
62108
62379
|
});
|
|
62109
62380
|
ctx.eventBus.emit({
|
|
62110
62381
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62124,70 +62395,54 @@ var require_device_manager_addon = __commonJS({
|
|
|
62124
62395
|
async function allocateDeviceId(pctx, input) {
|
|
62125
62396
|
const { addonId, stableId } = input;
|
|
62126
62397
|
return await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62127
|
-
const
|
|
62128
|
-
|
|
62129
|
-
if (existing) return { id: existing.id };
|
|
62398
|
+
const existing = await pctx.metaStore.rows.findByStableId(addonId, stableId);
|
|
62399
|
+
if (existing) return { id: existing.meta.id };
|
|
62130
62400
|
const id = await pctx.metaStore.allocateNextDeviceId();
|
|
62131
|
-
await pctx.
|
|
62132
|
-
|
|
62133
|
-
|
|
62134
|
-
|
|
62135
|
-
|
|
62136
|
-
|
|
62137
|
-
|
|
62138
|
-
|
|
62139
|
-
|
|
62140
|
-
|
|
62141
|
-
|
|
62142
|
-
|
|
62143
|
-
}
|
|
62401
|
+
await pctx.metaStore.rows.upsert({
|
|
62402
|
+
addonId,
|
|
62403
|
+
stableId,
|
|
62404
|
+
type: "generic",
|
|
62405
|
+
name: stableId,
|
|
62406
|
+
location: null,
|
|
62407
|
+
disabled: false,
|
|
62408
|
+
parentDeviceId: null,
|
|
62409
|
+
id
|
|
62410
|
+
}, {
|
|
62411
|
+
registered: false,
|
|
62412
|
+
metadata: null
|
|
62413
|
+
});
|
|
62144
62414
|
return { id };
|
|
62145
62415
|
});
|
|
62146
62416
|
}
|
|
62147
62417
|
async function registerDevice(pctx, input) {
|
|
62148
62418
|
const { addonId, stableId, id, type, name, parentDeviceId, features, config } = input;
|
|
62149
|
-
const key = String(id);
|
|
62150
62419
|
const featuresArr = Array.isArray(features) ? [...features] : [];
|
|
62151
62420
|
const { isFirstRegistration, exportFingerprint, fingerprintChanged } = await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62152
|
-
const
|
|
62153
|
-
const
|
|
62154
|
-
const
|
|
62155
|
-
if (!wasInIndex) await pctx.settings.writeAddonStore({ deviceIndex: {
|
|
62156
|
-
...index,
|
|
62157
|
-
[addonId]: [...existing, stableId]
|
|
62158
|
-
} });
|
|
62159
|
-
const meta = await pctx.metaStore.readMeta();
|
|
62160
|
-
const existingMeta = meta[key];
|
|
62421
|
+
const existingRow = await pctx.metaStore.rows.get(id);
|
|
62422
|
+
const existingMeta = existingRow?.meta;
|
|
62423
|
+
const wasRegistered = existingRow?.registered ?? false;
|
|
62161
62424
|
const wasUserNamed = existingMeta?.userNamed ?? (existingMeta !== void 0 && existingMeta.name !== stableId);
|
|
62162
62425
|
const resolvedName = wasUserNamed && existingMeta !== void 0 ? existingMeta.name : name;
|
|
62163
|
-
const isFirst = !existingMeta || !
|
|
62426
|
+
const isFirst = !existingMeta || !wasRegistered;
|
|
62164
62427
|
const fingerprint = (0, _camstack_types_node.canonicalDeviceFingerprint)({
|
|
62165
62428
|
deviceId: id,
|
|
62166
62429
|
deviceType: type,
|
|
62167
62430
|
features: featuresArr
|
|
62168
62431
|
});
|
|
62169
|
-
await pctx.
|
|
62170
|
-
|
|
62171
|
-
|
|
62172
|
-
|
|
62173
|
-
|
|
62174
|
-
|
|
62175
|
-
|
|
62176
|
-
|
|
62177
|
-
|
|
62178
|
-
|
|
62179
|
-
|
|
62180
|
-
|
|
62181
|
-
|
|
62182
|
-
|
|
62183
|
-
...existingMeta?.role !== void 0 ? { role: existingMeta.role } : {},
|
|
62184
|
-
...existingMeta?.display !== void 0 ? { display: existingMeta.display } : {},
|
|
62185
|
-
parentDeviceId,
|
|
62186
|
-
id,
|
|
62187
|
-
features: featuresArr,
|
|
62188
|
-
exportFingerprint: fingerprint
|
|
62189
|
-
}
|
|
62190
|
-
} });
|
|
62432
|
+
await pctx.metaStore.rows.upsertRegistration({
|
|
62433
|
+
deviceId: id,
|
|
62434
|
+
addonId,
|
|
62435
|
+
stableId,
|
|
62436
|
+
type,
|
|
62437
|
+
name: resolvedName,
|
|
62438
|
+
userNamed: wasUserNamed,
|
|
62439
|
+
location: existingMeta?.location ?? null,
|
|
62440
|
+
disabled: existingMeta?.disabled ?? false,
|
|
62441
|
+
parentDeviceId: parentDeviceId ?? null,
|
|
62442
|
+
registered: true,
|
|
62443
|
+
features: featuresArr,
|
|
62444
|
+
exportFingerprint: fingerprint
|
|
62445
|
+
});
|
|
62191
62446
|
return {
|
|
62192
62447
|
isFirstRegistration: isFirst,
|
|
62193
62448
|
exportFingerprint: fingerprint,
|
|
@@ -62247,26 +62502,9 @@ var require_device_manager_addon = __commonJS({
|
|
|
62247
62502
|
const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
|
|
62248
62503
|
if (!persisted) return;
|
|
62249
62504
|
const { addonId, stableId, meta: persistedMeta } = persisted;
|
|
62250
|
-
const key = String(deviceId);
|
|
62251
62505
|
const deviceName = persistedMeta.name;
|
|
62252
62506
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62253
|
-
|
|
62254
|
-
const remaining = (index[addonId] ?? []).filter((sid) => sid !== stableId);
|
|
62255
|
-
const updatedIndex = remaining.length > 0 ? {
|
|
62256
|
-
...index,
|
|
62257
|
-
[addonId]: remaining
|
|
62258
|
-
} : (() => {
|
|
62259
|
-
const { [addonId]: _removed, ...rest } = index;
|
|
62260
|
-
return rest;
|
|
62261
|
-
})();
|
|
62262
|
-
await pctx.settings.writeAddonStore({ deviceIndex: updatedIndex });
|
|
62263
|
-
const { [key]: _removedMeta, ...restMeta } = await pctx.metaStore.readMeta();
|
|
62264
|
-
await pctx.settings.writeAddonStore({ deviceMeta: restMeta });
|
|
62265
|
-
const map = await pctx.metaStore.readMetadataMap();
|
|
62266
|
-
if (key in map) {
|
|
62267
|
-
const { [key]: _removedMetadata, ...restMap } = map;
|
|
62268
|
-
await pctx.settings.writeAddonStore({ deviceMetadata: restMap });
|
|
62269
|
-
}
|
|
62507
|
+
await pctx.metaStore.rows.remove(deviceId);
|
|
62270
62508
|
});
|
|
62271
62509
|
await pctx.settings.clearDeviceStore(deviceId);
|
|
62272
62510
|
await pctx.settings.clearDeviceRuntimeState(deviceId);
|
|
@@ -62310,11 +62548,10 @@ var require_device_manager_addon = __commonJS({
|
|
|
62310
62548
|
}
|
|
62311
62549
|
async function loadMeta(pctx, input) {
|
|
62312
62550
|
const { deviceId } = input;
|
|
62313
|
-
const
|
|
62314
|
-
if (
|
|
62315
|
-
const {
|
|
62316
|
-
const
|
|
62317
|
-
const metadata = (await pctx.metaStore.readMetadataMap())[key] ?? null;
|
|
62551
|
+
const row = await pctx.metaStore.getRow(deviceId);
|
|
62552
|
+
if (row === null) return null;
|
|
62553
|
+
const { meta: m, metadata } = row;
|
|
62554
|
+
const { addonId, stableId } = m;
|
|
62318
62555
|
return {
|
|
62319
62556
|
id: m.id,
|
|
62320
62557
|
stableId,
|
|
@@ -62337,32 +62574,26 @@ var require_device_manager_addon = __commonJS({
|
|
|
62337
62574
|
const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
|
|
62338
62575
|
if (!persisted) throw new Error(`[device-manager] setName: unknown device id=${deviceId}`);
|
|
62339
62576
|
const { meta: m } = persisted;
|
|
62340
|
-
const key = String(deviceId);
|
|
62341
62577
|
const oldName = m.name;
|
|
62342
|
-
|
|
62343
|
-
|
|
62344
|
-
|
|
62345
|
-
|
|
62346
|
-
|
|
62347
|
-
|
|
62348
|
-
|
|
62578
|
+
await pctx.metaStore.rows.patch(deviceId, {
|
|
62579
|
+
name,
|
|
62580
|
+
userNamed: true
|
|
62581
|
+
});
|
|
62582
|
+
if (oldName.length > 0 && oldName !== name) {
|
|
62583
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
62584
|
+
for (const row of await pctx.metaStore.rows.listByParent(deviceId)) candidates.set(row.meta.id, row.meta);
|
|
62585
|
+
for (const row of await pctx.metaStore.rows.listAll()) if (row.meta.linkDeviceId === deviceId) candidates.set(row.meta.id, row.meta);
|
|
62586
|
+
candidates.delete(deviceId);
|
|
62587
|
+
for (const childMeta of candidates.values()) {
|
|
62588
|
+
if (childMeta.name !== oldName && !childMeta.name.startsWith(`${oldName} `)) continue;
|
|
62589
|
+
const childName = childMeta.name === oldName ? name : `${name}${childMeta.name.slice(oldName.length)}`;
|
|
62590
|
+
await pctx.metaStore.rows.patch(childMeta.id, { name: childName });
|
|
62591
|
+
cascaded.push({
|
|
62592
|
+
id: childMeta.id,
|
|
62593
|
+
name: childName
|
|
62594
|
+
});
|
|
62349
62595
|
}
|
|
62350
|
-
};
|
|
62351
|
-
if (oldName.length > 0 && oldName !== name) for (const [childKey, childMeta] of Object.entries(allMeta)) {
|
|
62352
|
-
if (childKey === key) continue;
|
|
62353
|
-
if (!(childMeta.parentDeviceId === deviceId || childMeta.linkDeviceId === deviceId)) continue;
|
|
62354
|
-
if (childMeta.name !== oldName && !childMeta.name.startsWith(`${oldName} `)) continue;
|
|
62355
|
-
const childName = childMeta.name === oldName ? name : `${name}${childMeta.name.slice(oldName.length)}`;
|
|
62356
|
-
nextMeta[childKey] = {
|
|
62357
|
-
...childMeta,
|
|
62358
|
-
name: childName
|
|
62359
|
-
};
|
|
62360
|
-
cascaded.push({
|
|
62361
|
-
id: Number(childKey),
|
|
62362
|
-
name: childName
|
|
62363
|
-
});
|
|
62364
62596
|
}
|
|
62365
|
-
await pctx.settings.writeAddonStore({ deviceMeta: nextMeta });
|
|
62366
62597
|
});
|
|
62367
62598
|
pctx.host.ctx.eventBus.emit({
|
|
62368
62599
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62396,18 +62627,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62396
62627
|
async function setLocation(pctx, input) {
|
|
62397
62628
|
const { deviceId, location } = input;
|
|
62398
62629
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62399
|
-
|
|
62400
|
-
|
|
62401
|
-
const { meta: m } = persisted;
|
|
62402
|
-
const key = String(deviceId);
|
|
62403
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62404
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62405
|
-
...allMeta,
|
|
62406
|
-
[key]: {
|
|
62407
|
-
...m,
|
|
62408
|
-
location
|
|
62409
|
-
}
|
|
62410
|
-
} });
|
|
62630
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setLocation: unknown device id=${deviceId}`);
|
|
62631
|
+
await pctx.metaStore.rows.patch(deviceId, { location });
|
|
62411
62632
|
});
|
|
62412
62633
|
pctx.host.ctx.eventBus.emit({
|
|
62413
62634
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62427,18 +62648,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62427
62648
|
async function setType(pctx, input) {
|
|
62428
62649
|
const { deviceId, type } = input;
|
|
62429
62650
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62430
|
-
|
|
62431
|
-
|
|
62432
|
-
const { meta: m } = persisted;
|
|
62433
|
-
const key = String(deviceId);
|
|
62434
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62435
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62436
|
-
...allMeta,
|
|
62437
|
-
[key]: {
|
|
62438
|
-
...m,
|
|
62439
|
-
type
|
|
62440
|
-
}
|
|
62441
|
-
} });
|
|
62651
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setType: unknown device id=${deviceId}`);
|
|
62652
|
+
await pctx.metaStore.rows.patch(deviceId, { type });
|
|
62442
62653
|
});
|
|
62443
62654
|
pctx.host.ctx.eventBus.emit({
|
|
62444
62655
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62461,18 +62672,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62461
62672
|
async function setLinkDeviceId(pctx, input) {
|
|
62462
62673
|
const { deviceId, linkDeviceId } = input;
|
|
62463
62674
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62464
|
-
|
|
62465
|
-
|
|
62466
|
-
const { meta: m } = persisted;
|
|
62467
|
-
const key = String(deviceId);
|
|
62468
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62469
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62470
|
-
...allMeta,
|
|
62471
|
-
[key]: {
|
|
62472
|
-
...m,
|
|
62473
|
-
linkDeviceId
|
|
62474
|
-
}
|
|
62475
|
-
} });
|
|
62675
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setLinkDeviceId: unknown device id=${deviceId}`);
|
|
62676
|
+
await pctx.metaStore.rows.patch(deviceId, { linkDeviceId });
|
|
62476
62677
|
});
|
|
62477
62678
|
pctx.host.ctx.eventBus.emit({
|
|
62478
62679
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62492,18 +62693,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62492
62693
|
async function setPrimaryChildEntityId(pctx, input) {
|
|
62493
62694
|
const { deviceId, primaryChildEntityId } = input;
|
|
62494
62695
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62495
|
-
|
|
62496
|
-
|
|
62497
|
-
const { meta: m } = persisted;
|
|
62498
|
-
const key = String(deviceId);
|
|
62499
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62500
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62501
|
-
...allMeta,
|
|
62502
|
-
[key]: {
|
|
62503
|
-
...m,
|
|
62504
|
-
primaryChildEntityId
|
|
62505
|
-
}
|
|
62506
|
-
} });
|
|
62696
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setPrimaryChildEntityId: unknown device id=${deviceId}`);
|
|
62697
|
+
await pctx.metaStore.rows.patch(deviceId, { primaryChildEntityId });
|
|
62507
62698
|
});
|
|
62508
62699
|
pctx.host.ctx.eventBus.emit({
|
|
62509
62700
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62523,18 +62714,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62523
62714
|
async function setChildLayout(pctx, input) {
|
|
62524
62715
|
const { deviceId, childLayout } = input;
|
|
62525
62716
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62526
|
-
|
|
62527
|
-
|
|
62528
|
-
const { meta: m } = persisted;
|
|
62529
|
-
const key = String(deviceId);
|
|
62530
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62531
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62532
|
-
...allMeta,
|
|
62533
|
-
[key]: {
|
|
62534
|
-
...m,
|
|
62535
|
-
childLayout
|
|
62536
|
-
}
|
|
62537
|
-
} });
|
|
62717
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setChildLayout: unknown device id=${deviceId}`);
|
|
62718
|
+
await pctx.metaStore.rows.patch(deviceId, { childLayout });
|
|
62538
62719
|
});
|
|
62539
62720
|
pctx.host.ctx.eventBus.emit({
|
|
62540
62721
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62554,18 +62735,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62554
62735
|
async function setRole(pctx, input) {
|
|
62555
62736
|
const { deviceId, role } = input;
|
|
62556
62737
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62557
|
-
|
|
62558
|
-
|
|
62559
|
-
const { meta: m } = persisted;
|
|
62560
|
-
const key = String(deviceId);
|
|
62561
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62562
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62563
|
-
...allMeta,
|
|
62564
|
-
[key]: {
|
|
62565
|
-
...m,
|
|
62566
|
-
role
|
|
62567
|
-
}
|
|
62568
|
-
} });
|
|
62738
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setRole: unknown device id=${deviceId}`);
|
|
62739
|
+
await pctx.metaStore.rows.patch(deviceId, { role });
|
|
62569
62740
|
});
|
|
62570
62741
|
pctx.host.ctx.eventBus.emit({
|
|
62571
62742
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62596,19 +62767,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62596
62767
|
const { deviceId, display } = input;
|
|
62597
62768
|
const normalized = display === null ? null : normalizeDisplayOverride(display);
|
|
62598
62769
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62599
|
-
|
|
62600
|
-
|
|
62601
|
-
const { meta: m } = persisted;
|
|
62602
|
-
const key = String(deviceId);
|
|
62603
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62604
|
-
const nextRow = normalized === null ? (({ display: _drop, ...rest }) => rest)(m) : {
|
|
62605
|
-
...m,
|
|
62606
|
-
display: normalized
|
|
62607
|
-
};
|
|
62608
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62609
|
-
...allMeta,
|
|
62610
|
-
[key]: nextRow
|
|
62611
|
-
} });
|
|
62770
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setDisplay: unknown device id=${deviceId}`);
|
|
62771
|
+
await pctx.metaStore.rows.patch(deviceId, { display: normalized });
|
|
62612
62772
|
});
|
|
62613
62773
|
pctx.host.ctx.eventBus.emit({
|
|
62614
62774
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62638,24 +62798,15 @@ var require_device_manager_addon = __commonJS({
|
|
|
62638
62798
|
async function applyInitialMeta(pctx, input) {
|
|
62639
62799
|
const { deviceId, name, location, type, integrationId, linkDeviceId, role } = input;
|
|
62640
62800
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62641
|
-
|
|
62642
|
-
|
|
62643
|
-
const { meta: m } = persisted;
|
|
62644
|
-
const key = String(deviceId);
|
|
62645
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62646
|
-
const merged = {
|
|
62647
|
-
...m,
|
|
62801
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] applyInitialMeta: unknown device id=${deviceId}`);
|
|
62802
|
+
await pctx.metaStore.rows.patch(deviceId, {
|
|
62648
62803
|
...name !== void 0 ? { name } : {},
|
|
62649
62804
|
...location !== void 0 ? { location } : {},
|
|
62650
62805
|
...type !== void 0 ? { type } : {},
|
|
62651
62806
|
...integrationId !== void 0 ? { integrationId } : {},
|
|
62652
62807
|
...linkDeviceId !== void 0 ? { linkDeviceId } : {},
|
|
62653
62808
|
...role !== void 0 ? { role } : {}
|
|
62654
|
-
};
|
|
62655
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62656
|
-
...allMeta,
|
|
62657
|
-
[key]: merged
|
|
62658
|
-
} });
|
|
62809
|
+
});
|
|
62659
62810
|
});
|
|
62660
62811
|
const emitMetaChanged = (field, value) => {
|
|
62661
62812
|
pctx.host.ctx.eventBus.emit({
|
|
@@ -62683,10 +62834,9 @@ var require_device_manager_addon = __commonJS({
|
|
|
62683
62834
|
async function setMetadata(pctx, input) {
|
|
62684
62835
|
const { deviceId, patch } = input;
|
|
62685
62836
|
const result = await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62686
|
-
|
|
62687
|
-
|
|
62688
|
-
const
|
|
62689
|
-
const next = { ...map[key] ?? {} };
|
|
62837
|
+
const row = await pctx.metaStore.rows.get(deviceId);
|
|
62838
|
+
if (row === null) throw new Error(`[device-manager] setMetadata: unknown device id=${deviceId}`);
|
|
62839
|
+
const next = { ...row.metadata ?? {} };
|
|
62690
62840
|
let changed = false;
|
|
62691
62841
|
for (const [k, v] of Object.entries(patch)) if (v === null) {
|
|
62692
62842
|
if (k in next) {
|
|
@@ -62699,10 +62849,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62699
62849
|
}
|
|
62700
62850
|
if (!changed) return { changed: false };
|
|
62701
62851
|
const hasFields = Object.keys(next).length > 0;
|
|
62702
|
-
|
|
62703
|
-
if (hasFields) updatedMap[key] = next;
|
|
62704
|
-
else delete updatedMap[key];
|
|
62705
|
-
await pctx.settings.writeAddonStore({ deviceMetadata: updatedMap });
|
|
62852
|
+
await pctx.metaStore.rows.patch(deviceId, { metadata: hasFields ? next : null });
|
|
62706
62853
|
return {
|
|
62707
62854
|
changed: true,
|
|
62708
62855
|
finalMeta: hasFields ? next : null
|
|
@@ -62730,15 +62877,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62730
62877
|
const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
|
|
62731
62878
|
if (!persisted) throw new Error(`[device-manager] setDisabled: unknown device id=${deviceId}`);
|
|
62732
62879
|
const { meta: m } = persisted;
|
|
62733
|
-
|
|
62734
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62735
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62736
|
-
...allMeta,
|
|
62737
|
-
[key]: {
|
|
62738
|
-
...m,
|
|
62739
|
-
disabled
|
|
62740
|
-
}
|
|
62741
|
-
} });
|
|
62880
|
+
await pctx.metaStore.rows.patch(deviceId, { disabled });
|
|
62742
62881
|
return {
|
|
62743
62882
|
changed: (m.disabled ?? false) !== disabled,
|
|
62744
62883
|
integrationId: m.integrationId ?? ""
|
|
@@ -62781,9 +62920,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62781
62920
|
return data;
|
|
62782
62921
|
}
|
|
62783
62922
|
async function listLocations(pctx) {
|
|
62784
|
-
const
|
|
62785
|
-
const meta = store.deviceMeta ?? {};
|
|
62786
|
-
const locations = store.locations ?? [];
|
|
62923
|
+
const locations = (await pctx.metaStore.readStore()).locations ?? [];
|
|
62787
62924
|
const seen = /* @__PURE__ */ new Map();
|
|
62788
62925
|
const consider = (raw) => {
|
|
62789
62926
|
if (typeof raw !== "string") return;
|
|
@@ -62793,38 +62930,34 @@ var require_device_manager_addon = __commonJS({
|
|
|
62793
62930
|
if (!seen.has(key)) seen.set(key, trimmed);
|
|
62794
62931
|
};
|
|
62795
62932
|
for (const label of locations) consider(label);
|
|
62796
|
-
for (const
|
|
62933
|
+
for (const row of await pctx.metaStore.rows.listAll()) consider(row.meta.location);
|
|
62797
62934
|
return [...seen.values()].toSorted((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
|
|
62798
62935
|
}
|
|
62799
62936
|
async function addLocation(pctx, input) {
|
|
62800
62937
|
const trimmed = input.name.trim();
|
|
62801
62938
|
if (trimmed.length === 0) throw new Error("[device-manager] addLocation: name must be non-empty");
|
|
62802
|
-
const current = (await pctx.
|
|
62939
|
+
const current = (await pctx.metaStore.readStore()).locations ?? [];
|
|
62803
62940
|
if (current.some((l) => l.toLowerCase() === trimmed.toLowerCase())) return;
|
|
62804
62941
|
await pctx.settings.writeAddonStore({ locations: [...current, trimmed] });
|
|
62805
62942
|
}
|
|
62806
62943
|
async function removeLocation(pctx, input) {
|
|
62807
62944
|
const trimmed = input.name.trim();
|
|
62808
62945
|
if (trimmed.length === 0) return;
|
|
62809
|
-
const
|
|
62810
|
-
const current = store.locations ?? [];
|
|
62946
|
+
const current = (await pctx.metaStore.readStore()).locations ?? [];
|
|
62811
62947
|
const remaining = current.filter((l) => l.toLowerCase() !== trimmed.toLowerCase());
|
|
62812
62948
|
if (remaining.length !== current.length) await pctx.settings.writeAddonStore({ locations: remaining });
|
|
62813
62949
|
if (input.cascade !== true) return;
|
|
62814
|
-
const
|
|
62815
|
-
|
|
62816
|
-
|
|
62817
|
-
|
|
62818
|
-
|
|
62819
|
-
|
|
62820
|
-
|
|
62821
|
-
|
|
62822
|
-
|
|
62823
|
-
|
|
62824
|
-
|
|
62825
|
-
}
|
|
62826
|
-
if (cleared.length === 0) return;
|
|
62827
|
-
await pctx.settings.writeAddonStore({ deviceMeta: updates });
|
|
62950
|
+
const cleared = await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62951
|
+
const out = [];
|
|
62952
|
+
for (const row of await pctx.metaStore.rows.listAll()) {
|
|
62953
|
+
const location = row.meta.location;
|
|
62954
|
+
if (typeof location !== "string") continue;
|
|
62955
|
+
if (location.trim().toLowerCase() !== trimmed.toLowerCase()) continue;
|
|
62956
|
+
await pctx.metaStore.rows.patch(row.meta.id, { location: null });
|
|
62957
|
+
out.push(row.meta.id);
|
|
62958
|
+
}
|
|
62959
|
+
return out;
|
|
62960
|
+
});
|
|
62828
62961
|
for (const deviceId of cleared) pctx.host.ctx.eventBus.emit({
|
|
62829
62962
|
id: (0, node_crypto.randomUUID)(),
|
|
62830
62963
|
timestamp: /* @__PURE__ */ new Date(),
|
|
@@ -62840,41 +62973,48 @@ var require_device_manager_addon = __commonJS({
|
|
|
62840
62973
|
}
|
|
62841
62974
|
});
|
|
62842
62975
|
}
|
|
62976
|
+
function decodeAddonStore(raw) {
|
|
62977
|
+
const nextDeviceId = raw["nextDeviceId"];
|
|
62978
|
+
const locations = raw["locations"];
|
|
62979
|
+
const roleDisplayDefaults = raw["roleDisplayDefaults"];
|
|
62980
|
+
return {
|
|
62981
|
+
...typeof nextDeviceId === "number" && Number.isFinite(nextDeviceId) ? { nextDeviceId } : {},
|
|
62982
|
+
...Array.isArray(locations) ? { locations: locations.filter((l) => typeof l === "string") } : {},
|
|
62983
|
+
...isRoleDisplayDefaults(roleDisplayDefaults) ? { roleDisplayDefaults } : {}
|
|
62984
|
+
};
|
|
62985
|
+
}
|
|
62986
|
+
function isRoleDisplayDefaults(value) {
|
|
62987
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
62988
|
+
}
|
|
62843
62989
|
var DeviceMetaStore = class {
|
|
62844
62990
|
settings;
|
|
62845
62991
|
registry;
|
|
62992
|
+
rows;
|
|
62846
62993
|
/** Synchronous ownership cache, keyed by NUMERIC deviceId → owning addonId.
|
|
62847
|
-
* The persisted
|
|
62994
|
+
* The persisted row store is authoritative but reads are async; hub-side
|
|
62848
62995
|
* callers (e.g. `CapabilityRegistry.getNativeProvider` fallback) need
|
|
62849
62996
|
* ownership without awaiting. Kept in sync with every register/remove and
|
|
62850
62997
|
* warmed from persistence on boot. */
|
|
62851
62998
|
idToAddonId = /* @__PURE__ */ new Map();
|
|
62852
|
-
/** Serialises every read-modify-write of
|
|
62853
|
-
*
|
|
62854
|
-
* identical to the former `onInitialize` closure variable. */
|
|
62999
|
+
/** Serialises every read-modify-write of a device row through one promise
|
|
63000
|
+
* chain (see `withMetaWriteLock`). Per-instance state. */
|
|
62855
63001
|
metaWriteChain = Promise.resolve();
|
|
62856
|
-
constructor(settings, registry) {
|
|
63002
|
+
constructor(settings, registry, rows) {
|
|
62857
63003
|
this.settings = settings;
|
|
62858
63004
|
this.registry = registry;
|
|
63005
|
+
this.rows = rows;
|
|
62859
63006
|
}
|
|
62860
63007
|
/** The read currently in flight, or null. Never a settled value — see
|
|
62861
63008
|
* {@link readStore}. */
|
|
62862
63009
|
inFlightRead = null;
|
|
62863
63010
|
/**
|
|
62864
|
-
* The
|
|
63011
|
+
* The addon's own settings row set — `nextDeviceId`, `roleDisplayDefaults`,
|
|
63012
|
+
* `locations`. NOT the fleet: no device has lived here since the flatten.
|
|
62865
63013
|
*
|
|
62866
63014
|
* **Concurrent callers join the read already in flight.** This is not a
|
|
62867
63015
|
* cache and nothing survives settlement: a caller that awaited the running
|
|
62868
63016
|
* promise could not have observed anything older than its result, so the
|
|
62869
|
-
* only thing that changes is cost.
|
|
62870
|
-
* 2026-08-19 boot: `readAddonStore` lands in `SqliteSettingsBackend
|
|
62871
|
-
* .getAllAddon`, which reads and `JSON.parse`s this addon's rows —
|
|
62872
|
-
* 625 KB on the live hub (`deviceMeta` 467 KB + `deviceMetadata` 87 KB +
|
|
62873
|
-
* `deviceIndex` 70 KB) — synchronously, on the hub's event loop. A V8
|
|
62874
|
-
* profile of that boot had hub-main's JS thread 99.9% busy with 64% of it
|
|
62875
|
-
* inside `getAllAddon`, ~89% of that entered here, and every runner's first
|
|
62876
|
-
* store read queued behind it (the notification centre's six parallel reads
|
|
62877
|
-
* all resolved together at t+44.7 s).
|
|
63017
|
+
* only thing that changes is cost.
|
|
62878
63018
|
*
|
|
62879
63019
|
* A rejection is NOT latched: the slot is cleared before the promise
|
|
62880
63020
|
* settles either way, so a failed read costs the joiners that one failure
|
|
@@ -62885,7 +63025,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62885
63025
|
if (existing !== null) return existing;
|
|
62886
63026
|
const read = (async () => {
|
|
62887
63027
|
try {
|
|
62888
|
-
return await this.settings.readAddonStore();
|
|
63028
|
+
return decodeAddonStore(await this.settings.readAddonStore());
|
|
62889
63029
|
} finally {
|
|
62890
63030
|
this.inFlightRead = null;
|
|
62891
63031
|
}
|
|
@@ -62893,41 +63033,6 @@ var require_device_manager_addon = __commonJS({
|
|
|
62893
63033
|
this.inFlightRead = read;
|
|
62894
63034
|
return read;
|
|
62895
63035
|
};
|
|
62896
|
-
/**
|
|
62897
|
-
* The three fleet projections from ONE read.
|
|
62898
|
-
*
|
|
62899
|
-
* `listAll` asked for `deviceMeta`, then `deviceMetadata`, then
|
|
62900
|
-
* `deviceIndex` — three SEQUENTIAL awaits, which {@link readStore}'s
|
|
62901
|
-
* in-flight join cannot collapse because each starts after the previous one
|
|
62902
|
-
* settled. Three full 625 KB parses per call, on a call made once per device
|
|
62903
|
-
* lifecycle event during a 974-device boot: 23% of hub-main's CPU.
|
|
62904
|
-
*
|
|
62905
|
-
* It is also ONE snapshot. Three separate reads could straddle a write and
|
|
62906
|
-
* hand back an index that names a device the meta map no longer has.
|
|
62907
|
-
*/
|
|
62908
|
-
readAll = async () => {
|
|
62909
|
-
const store = await this.readStore();
|
|
62910
|
-
return {
|
|
62911
|
-
index: store.deviceIndex ?? {},
|
|
62912
|
-
meta: store.deviceMeta ?? {},
|
|
62913
|
-
metadata: store.deviceMetadata ?? {}
|
|
62914
|
-
};
|
|
62915
|
-
};
|
|
62916
|
-
readIndex = async () => {
|
|
62917
|
-
return (await this.readStore()).deviceIndex ?? {};
|
|
62918
|
-
};
|
|
62919
|
-
readMeta = async () => {
|
|
62920
|
-
return (await this.readStore()).deviceMeta ?? {};
|
|
62921
|
-
};
|
|
62922
|
-
/** Hardware-identity metadata map. Lives in a sibling key on the
|
|
62923
|
-
* device-manager addon store so its writers (`setMetadata`) never
|
|
62924
|
-
* collide with the lifecycle writers on `deviceMeta`
|
|
62925
|
-
* (`registerDevice` / `setName` / `setLocation` / `setDisabled`).
|
|
62926
|
-
* Single-writer per row eliminates the "writer X clobbers writer
|
|
62927
|
-
* Y's field" bug class — `setMetadata` is the only producer. */
|
|
62928
|
-
readMetadataMap = async () => {
|
|
62929
|
-
return (await this.readStore()).deviceMetadata ?? {};
|
|
62930
|
-
};
|
|
62931
63036
|
withMetaWriteLock = async (fn) => {
|
|
62932
63037
|
const previous = this.metaWriteChain;
|
|
62933
63038
|
let release = () => {
|
|
@@ -62944,31 +63049,36 @@ var require_device_manager_addon = __commonJS({
|
|
|
62944
63049
|
release();
|
|
62945
63050
|
}
|
|
62946
63051
|
};
|
|
63052
|
+
/** The whole persisted row for one device, or `null`. */
|
|
63053
|
+
getRow = async (deviceId) => this.rows.get(deviceId);
|
|
62947
63054
|
/**
|
|
62948
63055
|
* Resolve a numeric deviceId to the owning `(addonId, stableId)` pair.
|
|
62949
|
-
*
|
|
63056
|
+
* Reads the device's own row — live IDevice lookup (hub registry) is handled
|
|
62950
63057
|
* separately per call site so callers can decide whether to route to
|
|
62951
63058
|
* an in-process driver or to the cross-process `device-ops` bridge.
|
|
62952
63059
|
* Returns null when no device with that id is known to the hub.
|
|
62953
63060
|
*/
|
|
62954
63061
|
resolvePersistedById = async (deviceId) => {
|
|
62955
|
-
const
|
|
62956
|
-
if (
|
|
63062
|
+
const row = await this.rows.get(deviceId);
|
|
63063
|
+
if (row === null) return null;
|
|
62957
63064
|
return {
|
|
62958
|
-
addonId:
|
|
62959
|
-
stableId:
|
|
62960
|
-
meta:
|
|
63065
|
+
addonId: row.meta.addonId,
|
|
63066
|
+
stableId: row.meta.stableId,
|
|
63067
|
+
meta: row.meta
|
|
62961
63068
|
};
|
|
62962
63069
|
};
|
|
63070
|
+
/** The device's hardware-identity metadata blob, or `null`. */
|
|
63071
|
+
readMetadata = async (deviceId) => {
|
|
63072
|
+
return (await this.rows.get(deviceId))?.metadata ?? null;
|
|
63073
|
+
};
|
|
62963
63074
|
/** Direct children of a device: the union of the live registry's children
|
|
62964
|
-
* and the persisted
|
|
63075
|
+
* and the persisted rows whose `parentDeviceId` is `parentId`, deduplicated
|
|
62965
63076
|
* and excluding self. Shared by the `remove` cascade and the `resetToSource`
|
|
62966
63077
|
* resync purge (#19). */
|
|
62967
63078
|
directChildIds = async (parentId) => {
|
|
62968
63079
|
const ids = /* @__PURE__ */ new Set();
|
|
62969
63080
|
if (this.registry) for (const c of this.registry.getChildren(parentId)) ids.add(c.id);
|
|
62970
|
-
const
|
|
62971
|
-
for (const m of Object.values(meta)) if (m.parentDeviceId === parentId) ids.add(m.id);
|
|
63081
|
+
for (const row of await this.rows.listByParent(parentId)) ids.add(row.meta.id);
|
|
62972
63082
|
ids.delete(parentId);
|
|
62973
63083
|
return [...ids];
|
|
62974
63084
|
};
|
|
@@ -62978,6 +63088,476 @@ var require_device_manager_addon = __commonJS({
|
|
|
62978
63088
|
return current;
|
|
62979
63089
|
};
|
|
62980
63090
|
};
|
|
63091
|
+
var DEVICE_ROWS_COLLECTION = "device-manager:devices";
|
|
63092
|
+
var DEVICE_ROWS_FLEET_LIMIT = 2e4;
|
|
63093
|
+
var DEVICE_ROWS_COLUMNS = [
|
|
63094
|
+
{
|
|
63095
|
+
name: "id",
|
|
63096
|
+
type: "TEXT",
|
|
63097
|
+
primaryKey: true,
|
|
63098
|
+
notNull: true
|
|
63099
|
+
},
|
|
63100
|
+
{
|
|
63101
|
+
name: "deviceId",
|
|
63102
|
+
type: "INTEGER",
|
|
63103
|
+
notNull: true
|
|
63104
|
+
},
|
|
63105
|
+
{
|
|
63106
|
+
name: "addonId",
|
|
63107
|
+
type: "TEXT",
|
|
63108
|
+
notNull: true
|
|
63109
|
+
},
|
|
63110
|
+
{
|
|
63111
|
+
name: "stableId",
|
|
63112
|
+
type: "TEXT",
|
|
63113
|
+
notNull: true
|
|
63114
|
+
},
|
|
63115
|
+
{
|
|
63116
|
+
name: "type",
|
|
63117
|
+
type: "TEXT",
|
|
63118
|
+
notNull: true
|
|
63119
|
+
},
|
|
63120
|
+
{
|
|
63121
|
+
name: "name",
|
|
63122
|
+
type: "TEXT",
|
|
63123
|
+
notNull: true
|
|
63124
|
+
},
|
|
63125
|
+
{
|
|
63126
|
+
name: "userNamed",
|
|
63127
|
+
type: "BOOLEAN"
|
|
63128
|
+
},
|
|
63129
|
+
{
|
|
63130
|
+
name: "location",
|
|
63131
|
+
type: "TEXT"
|
|
63132
|
+
},
|
|
63133
|
+
{
|
|
63134
|
+
name: "disabled",
|
|
63135
|
+
type: "BOOLEAN",
|
|
63136
|
+
notNull: true,
|
|
63137
|
+
defaultValue: false
|
|
63138
|
+
},
|
|
63139
|
+
{
|
|
63140
|
+
name: "parentDeviceId",
|
|
63141
|
+
type: "INTEGER"
|
|
63142
|
+
},
|
|
63143
|
+
{
|
|
63144
|
+
name: "registered",
|
|
63145
|
+
type: "BOOLEAN",
|
|
63146
|
+
notNull: true,
|
|
63147
|
+
defaultValue: false
|
|
63148
|
+
},
|
|
63149
|
+
{
|
|
63150
|
+
name: "features",
|
|
63151
|
+
type: "JSON"
|
|
63152
|
+
},
|
|
63153
|
+
{
|
|
63154
|
+
name: "exportFingerprint",
|
|
63155
|
+
type: "TEXT"
|
|
63156
|
+
},
|
|
63157
|
+
{
|
|
63158
|
+
name: "integrationId",
|
|
63159
|
+
type: "TEXT"
|
|
63160
|
+
},
|
|
63161
|
+
{
|
|
63162
|
+
name: "linkDeviceId",
|
|
63163
|
+
type: "INTEGER"
|
|
63164
|
+
},
|
|
63165
|
+
{
|
|
63166
|
+
name: "primaryChildEntityId",
|
|
63167
|
+
type: "TEXT"
|
|
63168
|
+
},
|
|
63169
|
+
{
|
|
63170
|
+
name: "childLayout",
|
|
63171
|
+
type: "JSON"
|
|
63172
|
+
},
|
|
63173
|
+
{
|
|
63174
|
+
name: "role",
|
|
63175
|
+
type: "TEXT"
|
|
63176
|
+
},
|
|
63177
|
+
{
|
|
63178
|
+
name: "display",
|
|
63179
|
+
type: "JSON"
|
|
63180
|
+
},
|
|
63181
|
+
{
|
|
63182
|
+
name: "metadata",
|
|
63183
|
+
type: "JSON"
|
|
63184
|
+
}
|
|
63185
|
+
];
|
|
63186
|
+
var DEVICE_ROWS_INDEXES = [
|
|
63187
|
+
{
|
|
63188
|
+
name: "idx_dm_devices_addon_stable",
|
|
63189
|
+
columns: ["addonId", "stableId"]
|
|
63190
|
+
},
|
|
63191
|
+
{
|
|
63192
|
+
name: "idx_dm_devices_parent",
|
|
63193
|
+
columns: ["parentDeviceId"]
|
|
63194
|
+
},
|
|
63195
|
+
{
|
|
63196
|
+
name: "idx_dm_devices_integration",
|
|
63197
|
+
columns: ["integrationId"]
|
|
63198
|
+
}
|
|
63199
|
+
];
|
|
63200
|
+
function deviceRowBackendOf(client) {
|
|
63201
|
+
return {
|
|
63202
|
+
declareCollection: async (input) => {
|
|
63203
|
+
await client.declareCollection.mutate(input);
|
|
63204
|
+
},
|
|
63205
|
+
get: (input) => client.get.query(input),
|
|
63206
|
+
set: async (input) => {
|
|
63207
|
+
await client.set.mutate(input);
|
|
63208
|
+
},
|
|
63209
|
+
query: (input) => client.query.query(input),
|
|
63210
|
+
updateWhere: (input) => client.updateWhere.mutate(input),
|
|
63211
|
+
delete: async (input) => {
|
|
63212
|
+
await client.delete.mutate(input);
|
|
63213
|
+
},
|
|
63214
|
+
count: (input) => client.count.query(input)
|
|
63215
|
+
};
|
|
63216
|
+
}
|
|
63217
|
+
function isPlainObject3(value) {
|
|
63218
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
63219
|
+
}
|
|
63220
|
+
function readString(value) {
|
|
63221
|
+
return typeof value === "string" ? value : void 0;
|
|
63222
|
+
}
|
|
63223
|
+
function readNumber(value) {
|
|
63224
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
63225
|
+
}
|
|
63226
|
+
function readBoolean(value) {
|
|
63227
|
+
if (typeof value === "boolean") return value;
|
|
63228
|
+
if (value === 1) return true;
|
|
63229
|
+
if (value === 0) return false;
|
|
63230
|
+
}
|
|
63231
|
+
function readStringArray(value) {
|
|
63232
|
+
if (!Array.isArray(value)) return void 0;
|
|
63233
|
+
const out = [];
|
|
63234
|
+
for (const entry of value) if (typeof entry === "string") out.push(entry);
|
|
63235
|
+
return out;
|
|
63236
|
+
}
|
|
63237
|
+
function decodeDeviceRow(data) {
|
|
63238
|
+
const deviceId = readNumber(data["deviceId"]);
|
|
63239
|
+
const addonId = readString(data["addonId"]);
|
|
63240
|
+
const stableId = readString(data["stableId"]);
|
|
63241
|
+
const type = readString(data["type"]);
|
|
63242
|
+
const name = readString(data["name"]);
|
|
63243
|
+
if (deviceId === void 0 || addonId === void 0 || stableId === void 0 || type === void 0 || name === void 0) return null;
|
|
63244
|
+
const location = readString(data["location"]);
|
|
63245
|
+
const parentDeviceId = readNumber(data["parentDeviceId"]);
|
|
63246
|
+
const userNamed = readBoolean(data["userNamed"]);
|
|
63247
|
+
const features = readStringArray(data["features"]);
|
|
63248
|
+
const exportFingerprint = readString(data["exportFingerprint"]);
|
|
63249
|
+
const integrationId = readString(data["integrationId"]);
|
|
63250
|
+
const linkDeviceId = readNumber(data["linkDeviceId"]);
|
|
63251
|
+
const primaryChildEntityId = readString(data["primaryChildEntityId"]);
|
|
63252
|
+
const role = readString(data["role"]);
|
|
63253
|
+
const rawChildLayout = data["childLayout"];
|
|
63254
|
+
const rawDisplay = data["display"];
|
|
63255
|
+
const rawMetadata = data["metadata"];
|
|
63256
|
+
return {
|
|
63257
|
+
meta: {
|
|
63258
|
+
id: deviceId,
|
|
63259
|
+
addonId,
|
|
63260
|
+
stableId,
|
|
63261
|
+
type,
|
|
63262
|
+
name,
|
|
63263
|
+
location: location ?? null,
|
|
63264
|
+
disabled: readBoolean(data["disabled"]) ?? false,
|
|
63265
|
+
parentDeviceId: parentDeviceId ?? null,
|
|
63266
|
+
...userNamed !== void 0 ? { userNamed } : {},
|
|
63267
|
+
...features !== void 0 ? { features } : {},
|
|
63268
|
+
...exportFingerprint !== void 0 ? { exportFingerprint } : {},
|
|
63269
|
+
...integrationId !== void 0 ? { integrationId } : {},
|
|
63270
|
+
...linkDeviceId !== void 0 ? { linkDeviceId } : {},
|
|
63271
|
+
...primaryChildEntityId !== void 0 ? { primaryChildEntityId } : {},
|
|
63272
|
+
...isChildLayout(rawChildLayout) ? { childLayout: rawChildLayout } : {},
|
|
63273
|
+
...role !== void 0 ? { role } : {},
|
|
63274
|
+
...isPlainObject3(rawDisplay) ? { display: toDisplayOverride(rawDisplay) } : {}
|
|
63275
|
+
},
|
|
63276
|
+
metadata: isPlainObject3(rawMetadata) ? rawMetadata : null,
|
|
63277
|
+
registered: readBoolean(data["registered"]) ?? false
|
|
63278
|
+
};
|
|
63279
|
+
}
|
|
63280
|
+
function isChildLayout(value) {
|
|
63281
|
+
return Array.isArray(value);
|
|
63282
|
+
}
|
|
63283
|
+
function toDisplayOverride(value) {
|
|
63284
|
+
return value;
|
|
63285
|
+
}
|
|
63286
|
+
function encodeDeviceRow(meta, extra = {}) {
|
|
63287
|
+
const row = {
|
|
63288
|
+
id: String(meta.id),
|
|
63289
|
+
deviceId: meta.id,
|
|
63290
|
+
addonId: meta.addonId,
|
|
63291
|
+
stableId: meta.stableId,
|
|
63292
|
+
type: meta.type,
|
|
63293
|
+
name: meta.name,
|
|
63294
|
+
userNamed: meta.userNamed ?? null,
|
|
63295
|
+
location: meta.location,
|
|
63296
|
+
disabled: meta.disabled,
|
|
63297
|
+
parentDeviceId: meta.parentDeviceId,
|
|
63298
|
+
registered: extra.registered ?? false,
|
|
63299
|
+
features: meta.features === void 0 ? null : [...meta.features],
|
|
63300
|
+
exportFingerprint: meta.exportFingerprint ?? null,
|
|
63301
|
+
integrationId: meta.integrationId ?? null,
|
|
63302
|
+
linkDeviceId: meta.linkDeviceId ?? null,
|
|
63303
|
+
primaryChildEntityId: meta.primaryChildEntityId ?? null,
|
|
63304
|
+
childLayout: meta.childLayout ?? null,
|
|
63305
|
+
role: meta.role ?? null,
|
|
63306
|
+
display: meta.display ?? null
|
|
63307
|
+
};
|
|
63308
|
+
if (extra.metadata !== void 0) row["metadata"] = extra.metadata;
|
|
63309
|
+
return row;
|
|
63310
|
+
}
|
|
63311
|
+
function encodeDeviceRowPatch(patch) {
|
|
63312
|
+
const row = {};
|
|
63313
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
63314
|
+
if (value === void 0) continue;
|
|
63315
|
+
row[key] = value;
|
|
63316
|
+
}
|
|
63317
|
+
return row;
|
|
63318
|
+
}
|
|
63319
|
+
var DeviceRowStore = class {
|
|
63320
|
+
backend;
|
|
63321
|
+
logger;
|
|
63322
|
+
declared = null;
|
|
63323
|
+
constructor(backend, logger) {
|
|
63324
|
+
this.backend = backend;
|
|
63325
|
+
this.logger = logger;
|
|
63326
|
+
}
|
|
63327
|
+
/**
|
|
63328
|
+
* Lazy, idempotent `declareCollection`, memoised on the PROMISE so N
|
|
63329
|
+
* concurrent first-callers issue one declaration rather than N. A rejection
|
|
63330
|
+
* is not latched — the slot is cleared so the next caller retries instead of
|
|
63331
|
+
* inheriting a dead collection forever.
|
|
63332
|
+
*/
|
|
63333
|
+
async declare() {
|
|
63334
|
+
const existing = this.declared;
|
|
63335
|
+
if (existing !== null) return existing;
|
|
63336
|
+
const run = (async () => {
|
|
63337
|
+
try {
|
|
63338
|
+
await this.backend.declareCollection({
|
|
63339
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63340
|
+
columns: DEVICE_ROWS_COLUMNS,
|
|
63341
|
+
indexes: DEVICE_ROWS_INDEXES
|
|
63342
|
+
});
|
|
63343
|
+
} catch (err) {
|
|
63344
|
+
this.declared = null;
|
|
63345
|
+
throw err;
|
|
63346
|
+
}
|
|
63347
|
+
})();
|
|
63348
|
+
this.declared = run;
|
|
63349
|
+
return run;
|
|
63350
|
+
}
|
|
63351
|
+
/** One device, by numeric id. `null` when the fleet does not know it. */
|
|
63352
|
+
async get(deviceId) {
|
|
63353
|
+
await this.declare();
|
|
63354
|
+
const raw = await this.backend.get({
|
|
63355
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63356
|
+
key: String(deviceId)
|
|
63357
|
+
});
|
|
63358
|
+
if (!isPlainObject3(raw)) return null;
|
|
63359
|
+
const decoded = decodeDeviceRow(raw);
|
|
63360
|
+
if (decoded === null) {
|
|
63361
|
+
this.logger.warn("device row skipped \u2014 identity fields missing", { tags: { deviceId } });
|
|
63362
|
+
return null;
|
|
63363
|
+
}
|
|
63364
|
+
return decoded;
|
|
63365
|
+
}
|
|
63366
|
+
/** Every device, ordered by numeric id. */
|
|
63367
|
+
async listAll() {
|
|
63368
|
+
return this.list({
|
|
63369
|
+
orderBy: {
|
|
63370
|
+
field: "deviceId",
|
|
63371
|
+
direction: "asc"
|
|
63372
|
+
},
|
|
63373
|
+
limit: DEVICE_ROWS_FLEET_LIMIT
|
|
63374
|
+
});
|
|
63375
|
+
}
|
|
63376
|
+
/**
|
|
63377
|
+
* The device an addon knows as `stableId`, or `null`.
|
|
63378
|
+
*
|
|
63379
|
+
* `(addonId, stableId)` is the addon-facing identity — unique by
|
|
63380
|
+
* construction, since `allocateDeviceId` is the only thing that mints a row
|
|
63381
|
+
* and it returns the existing id for a pair it already knows. A second row
|
|
63382
|
+
* for the pair would be a corruption, so this takes the LOWEST id and says
|
|
63383
|
+
* so rather than picking arbitrarily.
|
|
63384
|
+
*/
|
|
63385
|
+
async findByStableId(addonId, stableId) {
|
|
63386
|
+
const rows = await this.list({
|
|
63387
|
+
where: {
|
|
63388
|
+
addonId,
|
|
63389
|
+
stableId
|
|
63390
|
+
},
|
|
63391
|
+
orderBy: {
|
|
63392
|
+
field: "deviceId",
|
|
63393
|
+
direction: "asc"
|
|
63394
|
+
},
|
|
63395
|
+
limit: 2
|
|
63396
|
+
});
|
|
63397
|
+
const first = rows[0];
|
|
63398
|
+
if (first === void 0) return null;
|
|
63399
|
+
if (rows.length > 1) this.logger.warn("duplicate device rows for one (addonId, stableId) \u2014 using the lowest id", {
|
|
63400
|
+
tags: { deviceId: first.meta.id },
|
|
63401
|
+
meta: {
|
|
63402
|
+
addonId,
|
|
63403
|
+
stableId,
|
|
63404
|
+
ids: rows.map((r) => r.meta.id)
|
|
63405
|
+
}
|
|
63406
|
+
});
|
|
63407
|
+
return first;
|
|
63408
|
+
}
|
|
63409
|
+
/** Every device owned by one addon, ordered by numeric id. */
|
|
63410
|
+
async listByAddon(addonId) {
|
|
63411
|
+
return this.list({
|
|
63412
|
+
where: { addonId },
|
|
63413
|
+
orderBy: {
|
|
63414
|
+
field: "deviceId",
|
|
63415
|
+
direction: "asc"
|
|
63416
|
+
},
|
|
63417
|
+
limit: DEVICE_ROWS_FLEET_LIMIT
|
|
63418
|
+
});
|
|
63419
|
+
}
|
|
63420
|
+
/** Every device an integration owns, ordered by numeric id. */
|
|
63421
|
+
async listByIntegration(integrationId) {
|
|
63422
|
+
return this.list({
|
|
63423
|
+
where: { integrationId },
|
|
63424
|
+
orderBy: {
|
|
63425
|
+
field: "deviceId",
|
|
63426
|
+
direction: "asc"
|
|
63427
|
+
},
|
|
63428
|
+
limit: DEVICE_ROWS_FLEET_LIMIT
|
|
63429
|
+
});
|
|
63430
|
+
}
|
|
63431
|
+
/** Direct children of one device, ordered by numeric id. */
|
|
63432
|
+
async listByParent(parentDeviceId) {
|
|
63433
|
+
return this.list({
|
|
63434
|
+
where: { parentDeviceId },
|
|
63435
|
+
orderBy: {
|
|
63436
|
+
field: "deviceId",
|
|
63437
|
+
direction: "asc"
|
|
63438
|
+
},
|
|
63439
|
+
limit: DEVICE_ROWS_FLEET_LIMIT
|
|
63440
|
+
});
|
|
63441
|
+
}
|
|
63442
|
+
/** How many devices the fleet holds. Used to tell "empty store" from "gone device". */
|
|
63443
|
+
async count() {
|
|
63444
|
+
await this.declare();
|
|
63445
|
+
return this.backend.count({ collection: DEVICE_ROWS_COLLECTION });
|
|
63446
|
+
}
|
|
63447
|
+
async list(filter) {
|
|
63448
|
+
await this.declare();
|
|
63449
|
+
const records = await this.backend.query({
|
|
63450
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63451
|
+
filter
|
|
63452
|
+
});
|
|
63453
|
+
const out = [];
|
|
63454
|
+
for (const record of records) {
|
|
63455
|
+
const decoded = decodeDeviceRow(record.data);
|
|
63456
|
+
if (decoded === null) {
|
|
63457
|
+
this.logger.warn("device row skipped \u2014 identity fields missing", { meta: { rowId: record.id } });
|
|
63458
|
+
continue;
|
|
63459
|
+
}
|
|
63460
|
+
out.push(decoded);
|
|
63461
|
+
}
|
|
63462
|
+
return out;
|
|
63463
|
+
}
|
|
63464
|
+
/** Insert or replace the whole identity row. */
|
|
63465
|
+
async upsert(meta, extra = {}) {
|
|
63466
|
+
await this.declare();
|
|
63467
|
+
await this.backend.set({
|
|
63468
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63469
|
+
key: String(meta.id),
|
|
63470
|
+
value: encodeDeviceRow(meta, extra)
|
|
63471
|
+
});
|
|
63472
|
+
}
|
|
63473
|
+
/**
|
|
63474
|
+
* Write the named columns of an EXISTING row and nothing else.
|
|
63475
|
+
*
|
|
63476
|
+
* An UPDATE, deliberately not an upsert: a partial upsert would try to INSERT
|
|
63477
|
+
* a row carrying only the patched columns, and SQLite rejects that on the
|
|
63478
|
+
* `NOT NULL` identity columns before the primary-key conflict can turn it
|
|
63479
|
+
* into an update. Every caller resolves the row under the write lock and
|
|
63480
|
+
* throws when it is gone, so matching zero rows means a device was removed
|
|
63481
|
+
* between the resolve and the write — the patch is lost, and a lost write
|
|
63482
|
+
* that says nothing reads as a write that happened.
|
|
63483
|
+
*/
|
|
63484
|
+
async patch(deviceId, patch) {
|
|
63485
|
+
const columns = encodeDeviceRowPatch(patch);
|
|
63486
|
+
if (Object.keys(columns).length === 0) return;
|
|
63487
|
+
await this.declare();
|
|
63488
|
+
const { updated } = await this.backend.updateWhere({
|
|
63489
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63490
|
+
filter: { where: { id: String(deviceId) } },
|
|
63491
|
+
data: columns
|
|
63492
|
+
});
|
|
63493
|
+
if (updated === 0) this.logger.warn("device row patch matched no row \u2014 the device was removed under it", {
|
|
63494
|
+
tags: { deviceId },
|
|
63495
|
+
meta: { columns: Object.keys(columns) }
|
|
63496
|
+
});
|
|
63497
|
+
}
|
|
63498
|
+
/**
|
|
63499
|
+
* Insert-or-update the registration columns. See {@link DeviceRegistrationRow}
|
|
63500
|
+
* for why this is a separate, fully-specified statement rather than a patch.
|
|
63501
|
+
*/
|
|
63502
|
+
async upsertRegistration(row) {
|
|
63503
|
+
await this.declare();
|
|
63504
|
+
await this.backend.set({
|
|
63505
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63506
|
+
key: String(row.deviceId),
|
|
63507
|
+
value: {
|
|
63508
|
+
deviceId: row.deviceId,
|
|
63509
|
+
addonId: row.addonId,
|
|
63510
|
+
stableId: row.stableId,
|
|
63511
|
+
type: row.type,
|
|
63512
|
+
name: row.name,
|
|
63513
|
+
userNamed: row.userNamed,
|
|
63514
|
+
location: row.location,
|
|
63515
|
+
disabled: row.disabled,
|
|
63516
|
+
parentDeviceId: row.parentDeviceId,
|
|
63517
|
+
registered: row.registered,
|
|
63518
|
+
features: [...row.features],
|
|
63519
|
+
exportFingerprint: row.exportFingerprint
|
|
63520
|
+
}
|
|
63521
|
+
});
|
|
63522
|
+
}
|
|
63523
|
+
/** Drop the device's row. Idempotent. */
|
|
63524
|
+
async remove(deviceId) {
|
|
63525
|
+
await this.declare();
|
|
63526
|
+
await this.backend.delete({
|
|
63527
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63528
|
+
key: String(deviceId)
|
|
63529
|
+
});
|
|
63530
|
+
}
|
|
63531
|
+
/**
|
|
63532
|
+
* The retirement door for the three blobs this collection replaced.
|
|
63533
|
+
*
|
|
63534
|
+
* The purge is gated on THIS collection being non-empty, and the count has to
|
|
63535
|
+
* be taken after `declare()` — which is why the owning addon runs it and the
|
|
63536
|
+
* settings engine cannot: at the engine's own boot no addon has declared
|
|
63537
|
+
* anything, so every successor would read as unreadable, forever.
|
|
63538
|
+
*/
|
|
63539
|
+
retiredRowStore() {
|
|
63540
|
+
return {
|
|
63541
|
+
hasRow: async (spec) => {
|
|
63542
|
+
const raw = await this.backend.get({
|
|
63543
|
+
collection: spec.collection,
|
|
63544
|
+
key: spec.row
|
|
63545
|
+
});
|
|
63546
|
+
return raw !== void 0 && raw !== null;
|
|
63547
|
+
},
|
|
63548
|
+
deleteRow: async (spec) => {
|
|
63549
|
+
await this.backend.delete({
|
|
63550
|
+
collection: spec.collection,
|
|
63551
|
+
key: spec.row
|
|
63552
|
+
});
|
|
63553
|
+
},
|
|
63554
|
+
countRows: async (collection) => {
|
|
63555
|
+
await this.declare();
|
|
63556
|
+
return this.backend.count({ collection });
|
|
63557
|
+
}
|
|
63558
|
+
};
|
|
63559
|
+
}
|
|
63560
|
+
};
|
|
62981
63561
|
function persistableBlob(blob, policyFor) {
|
|
62982
63562
|
const out = {};
|
|
62983
63563
|
for (const [capName, slice] of Object.entries(blob)) {
|
|
@@ -63456,12 +64036,21 @@ var require_device_manager_addon = __commonJS({
|
|
|
63456
64036
|
}
|
|
63457
64037
|
}))).filter((id) => id !== null);
|
|
63458
64038
|
}
|
|
64039
|
+
/**
|
|
64040
|
+
* Row access for `device-manager:devices`. Built once in `onInitialize`
|
|
64041
|
+
* (which is also where the collection is declared) — every consumer reaches
|
|
64042
|
+
* it through {@link bindingsDeps} or the `ProviderContext`.
|
|
64043
|
+
*/
|
|
64044
|
+
deviceRows = null;
|
|
63459
64045
|
/** Build the dependency context the extracted binding resolvers consume. */
|
|
63460
64046
|
get bindingsDeps() {
|
|
64047
|
+
const rows = this.deviceRows;
|
|
64048
|
+
if (rows === null) throw new Error("[device-manager] device row store not initialized");
|
|
63461
64049
|
return {
|
|
63462
64050
|
ctx: this.ctx,
|
|
63463
64051
|
capabilityRegistry: this.capabilityRegistry,
|
|
63464
|
-
remoteNativeCaps: this.remoteNativeCaps
|
|
64052
|
+
remoteNativeCaps: this.remoteNativeCaps,
|
|
64053
|
+
rows
|
|
63465
64054
|
};
|
|
63466
64055
|
}
|
|
63467
64056
|
async getBindings(input) {
|
|
@@ -63517,17 +64106,23 @@ var require_device_manager_addon = __commonJS({
|
|
|
63517
64106
|
if (!ops) throw new Error(`[device-manager] device-ops native provider not found for '${deviceId}'`);
|
|
63518
64107
|
return ops;
|
|
63519
64108
|
};
|
|
63520
|
-
const
|
|
64109
|
+
const settingsStoreApi = this.ctx.api?.settingsStore;
|
|
64110
|
+
if (!settingsStoreApi) throw new Error("[device-manager] settings-store API not available \u2014 refusing to serve a fleet it cannot persist");
|
|
64111
|
+
const deviceRows = new DeviceRowStore(deviceRowBackendOf(settingsStoreApi), this.ctx.logger.child("rows"));
|
|
64112
|
+
await deviceRows.declare();
|
|
64113
|
+
this.deviceRows = deviceRows;
|
|
64114
|
+
try {
|
|
64115
|
+
await require_retired_settings_keys.purgeRetiredSettingsRows(deviceRows.retiredRowStore(), this.ctx.logger.child("RetiredRows"));
|
|
64116
|
+
} catch (err) {
|
|
64117
|
+
this.ctx.logger.warn("retired-row purge failed", { meta: { error: require_dist10.errMsg(err) } });
|
|
64118
|
+
}
|
|
64119
|
+
const metaStore = new DeviceMetaStore(settings, registry, deviceRows);
|
|
63521
64120
|
this.stateMirrorImpl = new DeviceStateMirror(this.ctx);
|
|
63522
64121
|
const stateMirror = this.stateMirrorImpl;
|
|
63523
|
-
const readMeta = metaStore.readMeta;
|
|
63524
64122
|
const resolvePersistedById = metaStore.resolvePersistedById;
|
|
63525
64123
|
const idToAddonId = metaStore.idToAddonId;
|
|
63526
|
-
|
|
63527
|
-
|
|
63528
|
-
for (const m of Object.values(meta)) idToAddonId.set(m.id, m.addonId);
|
|
63529
|
-
}
|
|
63530
|
-
const stampIntegrationId$1 = (deviceId, integrationId) => stampIntegrationId(metaStore, settings, this.ctx, deviceId, integrationId);
|
|
64124
|
+
for (const row of await deviceRows.listAll()) idToAddonId.set(row.meta.id, row.meta.addonId);
|
|
64125
|
+
const stampIntegrationId$1 = (deviceId, integrationId) => stampIntegrationId(metaStore, this.ctx, deviceId, integrationId);
|
|
63531
64126
|
const pctx = {
|
|
63532
64127
|
host: this.providerHost,
|
|
63533
64128
|
metaStore,
|
|
@@ -63770,7 +64365,7 @@ var require_hub_forwarder = __commonJS({
|
|
|
63770
64365
|
[Symbol.toStringTag]: { value: "Module" }
|
|
63771
64366
|
});
|
|
63772
64367
|
require_chunk_Cek0wNdY();
|
|
63773
|
-
var require_dist10 =
|
|
64368
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
63774
64369
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
63775
64370
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
63776
64371
|
var HubForwarderDestination = class {
|
|
@@ -63907,7 +64502,7 @@ var require_liveness_monitor_addon = __commonJS({
|
|
|
63907
64502
|
"use strict";
|
|
63908
64503
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
63909
64504
|
require_chunk_Cek0wNdY();
|
|
63910
|
-
var require_dist10 =
|
|
64505
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
63911
64506
|
var NO_DEVICES = "liveness:no-devices";
|
|
63912
64507
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
63913
64508
|
var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
|
|
@@ -64097,7 +64692,7 @@ var require_local_auth_addon = __commonJS({
|
|
|
64097
64692
|
[Symbol.toStringTag]: { value: "Module" }
|
|
64098
64693
|
});
|
|
64099
64694
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
64100
|
-
var require_dist10 =
|
|
64695
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
64101
64696
|
var node_crypto = __require("crypto");
|
|
64102
64697
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
64103
64698
|
var crypto$1 = __require("crypto");
|
|
@@ -71781,7 +72376,7 @@ var require_loki_logging = __commonJS({
|
|
|
71781
72376
|
[Symbol.toStringTag]: { value: "Module" }
|
|
71782
72377
|
});
|
|
71783
72378
|
require_chunk_Cek0wNdY();
|
|
71784
|
-
var require_dist10 =
|
|
72379
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
71785
72380
|
function sanitizeLabelName(raw) {
|
|
71786
72381
|
const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
|
|
71787
72382
|
return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
|
|
@@ -72346,7 +72941,7 @@ var require_native_metrics_addon = __commonJS({
|
|
|
72346
72941
|
[Symbol.toStringTag]: { value: "Module" }
|
|
72347
72942
|
});
|
|
72348
72943
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
72349
|
-
var require_dist10 =
|
|
72944
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
72350
72945
|
var node_child_process = __require("child_process");
|
|
72351
72946
|
var node_util = __require("util");
|
|
72352
72947
|
var node_os = __require("os");
|
|
@@ -73288,7 +73883,7 @@ var require_filesystem_storage_addon = __commonJS({
|
|
|
73288
73883
|
[Symbol.toStringTag]: { value: "Module" }
|
|
73289
73884
|
});
|
|
73290
73885
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
73291
|
-
var require_dist10 =
|
|
73886
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
73292
73887
|
var node_crypto = __require("crypto");
|
|
73293
73888
|
var node_fs_promises = __require("fs/promises");
|
|
73294
73889
|
var node_path = __require("path");
|
|
@@ -74404,7 +74999,8 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74404
74999
|
[Symbol.toStringTag]: { value: "Module" }
|
|
74405
75000
|
});
|
|
74406
75001
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
74407
|
-
var require_dist10 =
|
|
75002
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
75003
|
+
var require_retired_settings_keys = require_retired_settings_keys_CYDGGkyM();
|
|
74408
75004
|
var node_crypto = __require("crypto");
|
|
74409
75005
|
var node_fs = __require("fs");
|
|
74410
75006
|
var node_module = __require("module");
|
|
@@ -74413,123 +75009,47 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74413
75009
|
var SQLITE_PAGE_CACHE_KIB = -65536;
|
|
74414
75010
|
var SQLITE_MMAP_SIZE_BYTES = 268435456;
|
|
74415
75011
|
var SQLITE_JOURNAL_SIZE_LIMIT_BYTES = 67108864;
|
|
74416
|
-
|
|
74417
|
-
|
|
74418
|
-
|
|
74419
|
-
|
|
74420
|
-
var RETIRED_SETTINGS_KEYS = [{
|
|
74421
|
-
namespace: "detection-pipeline",
|
|
74422
|
-
collection: "addon-settings",
|
|
74423
|
-
row: "root",
|
|
74424
|
-
keys: ["pipelineSteps", "pipelineEngine"],
|
|
74425
|
-
perNodeKeys: [
|
|
74426
|
-
"engineRuntime",
|
|
74427
|
-
"engineBackend",
|
|
74428
|
-
"engineDevice",
|
|
74429
|
-
"probedBestEngine"
|
|
74430
|
-
],
|
|
74431
|
-
reason: "global step/engine seed and the per-node engine cascade \u2014 both removed; the live per-camera dispatch path never read either"
|
|
75012
|
+
var RETIRED_COLLECTIONS = [{
|
|
75013
|
+
collection: "devices",
|
|
75014
|
+
owner: "integration-registry",
|
|
75015
|
+
reason: "the device half of the integration-registry is deleted; the fleet is `device-manager:devices` and always was (0 rows here against 974 live devices)"
|
|
74432
75016
|
}, {
|
|
74433
|
-
|
|
74434
|
-
|
|
74435
|
-
|
|
74436
|
-
keys: [],
|
|
74437
|
-
perNodeKeys: [],
|
|
74438
|
-
mapValueKeys: ["deviceLinks"],
|
|
74439
|
-
reason: "cross-device field wiring, deleted 2026-08-08 \u2014 0 links across 302 devices a month after the UI shipped. There is no reader left: the resolver, the overlay, the cycle guard, the fifth binding kind and the tab are gone"
|
|
75017
|
+
collection: "device_settings_kv",
|
|
75018
|
+
owner: "integration-registry",
|
|
75019
|
+
reason: "per-device settings of a device table that never had a row; the live per-camera store is the device-manager row plus `addon-device-settings`"
|
|
74440
75020
|
}];
|
|
74441
|
-
function
|
|
74442
|
-
return {
|
|
74443
|
-
|
|
74444
|
-
|
|
74445
|
-
namespace: spec.namespace,
|
|
74446
|
-
collection: spec.collection,
|
|
74447
|
-
key: spec.row
|
|
74448
|
-
}));
|
|
74449
|
-
},
|
|
74450
|
-
async write(spec, value) {
|
|
74451
|
-
await backend.set({
|
|
74452
|
-
namespace: spec.namespace,
|
|
74453
|
-
collection: spec.collection,
|
|
74454
|
-
key: spec.row,
|
|
74455
|
-
value
|
|
74456
|
-
});
|
|
74457
|
-
}
|
|
74458
|
-
};
|
|
74459
|
-
}
|
|
74460
|
-
function planNestedRetiredKeyPurge(blob, spec) {
|
|
74461
|
-
const nestedKeys = spec.mapValueKeys ?? [];
|
|
74462
|
-
if (nestedKeys.length === 0) return [];
|
|
74463
|
-
const found = [];
|
|
74464
|
-
for (const [mapKey, value] of Object.entries(blob)) {
|
|
74465
|
-
const inner = require_dist10.asJsonObject(value);
|
|
74466
|
-
if (inner === null) continue;
|
|
74467
|
-
for (const retired of nestedKeys) if (retired in inner) found.push(`${mapKey}.${retired}`);
|
|
74468
|
-
}
|
|
74469
|
-
return found;
|
|
74470
|
-
}
|
|
74471
|
-
function planRetiredKeyPurge(blob, spec) {
|
|
74472
|
-
const exact = new Set(spec.keys);
|
|
74473
|
-
const found = [];
|
|
74474
|
-
for (const key of Object.keys(blob)) {
|
|
74475
|
-
if (exact.has(key)) {
|
|
74476
|
-
found.push(key);
|
|
74477
|
-
continue;
|
|
74478
|
-
}
|
|
74479
|
-
for (const perNode of spec.perNodeKeys) if (key === perNode || key.startsWith(`${perNode}@`)) {
|
|
74480
|
-
found.push(key);
|
|
74481
|
-
break;
|
|
74482
|
-
}
|
|
74483
|
-
}
|
|
74484
|
-
return found;
|
|
75021
|
+
function retiredCollectionStoreOf(backend) {
|
|
75022
|
+
return { async drop(collection) {
|
|
75023
|
+
return backend.dropRetiredTable(collection);
|
|
75024
|
+
} };
|
|
74485
75025
|
}
|
|
74486
|
-
async function
|
|
74487
|
-
if (!settingsStoreIsAuthoritativeHere(env)) return [];
|
|
75026
|
+
async function purgeRetiredCollections(store, logger, specs = RETIRED_COLLECTIONS) {
|
|
74488
75027
|
const results = [];
|
|
74489
75028
|
for (const spec of specs) {
|
|
74490
|
-
let
|
|
75029
|
+
let dropped;
|
|
74491
75030
|
try {
|
|
74492
|
-
|
|
75031
|
+
dropped = await store.drop(spec.collection);
|
|
74493
75032
|
} catch (err) {
|
|
74494
|
-
logger.warn("retired
|
|
74495
|
-
addon: spec.
|
|
75033
|
+
logger.warn("retired collection \u2014 drop failed, table left in place", { meta: {
|
|
75034
|
+
addon: spec.owner,
|
|
74496
75035
|
collection: spec.collection,
|
|
74497
|
-
row: spec.row,
|
|
74498
75036
|
error: err instanceof Error ? err.message : String(err)
|
|
74499
75037
|
} });
|
|
74500
75038
|
continue;
|
|
74501
75039
|
}
|
|
74502
|
-
if (
|
|
74503
|
-
const doomed = planRetiredKeyPurge(blob, spec);
|
|
74504
|
-
const doomedNested = planNestedRetiredKeyPurge(blob, spec);
|
|
74505
|
-
if (doomed.length === 0 && doomedNested.length === 0) continue;
|
|
74506
|
-
const doomedSet = new Set(doomed);
|
|
74507
|
-
const nestedByMapKey = /* @__PURE__ */ new Map();
|
|
74508
|
-
for (const path of doomedNested) {
|
|
74509
|
-
const cut = path.indexOf(".");
|
|
74510
|
-
const mapKey = path.slice(0, cut);
|
|
74511
|
-
const inner = path.slice(cut + 1);
|
|
74512
|
-
const bucket = nestedByMapKey.get(mapKey) ?? /* @__PURE__ */ new Set();
|
|
74513
|
-
bucket.add(inner);
|
|
74514
|
-
nestedByMapKey.set(mapKey, bucket);
|
|
74515
|
-
}
|
|
74516
|
-
const next = Object.fromEntries(Object.entries(blob).filter(([key]) => !doomedSet.has(key)).map(([key, value]) => {
|
|
74517
|
-
const retired = nestedByMapKey.get(key);
|
|
74518
|
-
if (retired === void 0) return [key, value];
|
|
74519
|
-
const inner = require_dist10.asJsonObject(value);
|
|
74520
|
-
if (inner === null) return [key, value];
|
|
74521
|
-
return [key, Object.fromEntries(Object.entries(inner).filter(([k]) => !retired.has(k)))];
|
|
74522
|
-
}));
|
|
74523
|
-
await store.write(spec, next);
|
|
75040
|
+
if (dropped === null) continue;
|
|
74524
75041
|
const result = {
|
|
74525
|
-
addon: spec.namespace,
|
|
74526
75042
|
collection: spec.collection,
|
|
74527
|
-
|
|
74528
|
-
|
|
74529
|
-
nested: doomedNested
|
|
75043
|
+
owner: spec.owner,
|
|
75044
|
+
rows: dropped.rows
|
|
74530
75045
|
};
|
|
74531
75046
|
results.push(result);
|
|
74532
|
-
|
|
75047
|
+
const line = "dropped a retired collection";
|
|
75048
|
+
if (dropped.rows > 0) logger.warn(`${line} \u2014 it still held rows`, { meta: {
|
|
75049
|
+
...result,
|
|
75050
|
+
reason: spec.reason
|
|
75051
|
+
} });
|
|
75052
|
+
else logger.info(line, { meta: {
|
|
74533
75053
|
...result,
|
|
74534
75054
|
reason: spec.reason
|
|
74535
75055
|
} });
|
|
@@ -74623,6 +75143,9 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74623
75143
|
function isImposedBound(source) {
|
|
74624
75144
|
return source !== "caller";
|
|
74625
75145
|
}
|
|
75146
|
+
function isCountRow(raw) {
|
|
75147
|
+
return raw !== null && typeof raw === "object" && typeof Reflect.get(raw, "n") === "number";
|
|
75148
|
+
}
|
|
74626
75149
|
function parseRowData(raw) {
|
|
74627
75150
|
return require_dist10.asJsonObject(require_dist10.parseJsonUnknown(raw)) ?? {};
|
|
74628
75151
|
}
|
|
@@ -75307,6 +75830,31 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
75307
75830
|
}
|
|
75308
75831
|
this.structuredTables.set(table, fingerprint);
|
|
75309
75832
|
}
|
|
75833
|
+
/**
|
|
75834
|
+
* Drop a table nothing declares any more, reporting what went with it.
|
|
75835
|
+
*
|
|
75836
|
+
* The DDL lives here because the engine owns the handle AND the two memos
|
|
75837
|
+
* that outlive a table: `structuredTables` (schema fingerprint) and
|
|
75838
|
+
* `declaredCollections` (the column set `compileFilter` resolves against).
|
|
75839
|
+
* Dropping behind their backs would leave a ghost — a later declaration of
|
|
75840
|
+
* the same name would be memoised as "already materialised" against a table
|
|
75841
|
+
* that no longer exists.
|
|
75842
|
+
*
|
|
75843
|
+
* WHICH tables are retired, and the logging, are policy: they live in
|
|
75844
|
+
* `retired-collections.ts`. This method knows only how to drop one.
|
|
75845
|
+
*
|
|
75846
|
+
* Answers `null` when the table is not there — the steady state after the
|
|
75847
|
+
* first boot that ran the purge, and the case on a fresh install.
|
|
75848
|
+
*/
|
|
75849
|
+
dropRetiredTable(table) {
|
|
75850
|
+
if (this.getDb().prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`).get(table) === void 0) return null;
|
|
75851
|
+
const counted = this.getDb().prepare(`SELECT COUNT(*) AS n FROM "${table}"`).get();
|
|
75852
|
+
const rows = isCountRow(counted) ? counted.n : 0;
|
|
75853
|
+
this.getDb().exec(`DROP TABLE "${table}"`);
|
|
75854
|
+
this.structuredTables.delete(table);
|
|
75855
|
+
this.declaredCollections.delete(table);
|
|
75856
|
+
return { rows };
|
|
75857
|
+
}
|
|
75310
75858
|
async tableInsert(table, row) {
|
|
75311
75859
|
const keys = Object.keys(row);
|
|
75312
75860
|
const cols = keys.map((k) => `"${k}"`).join(", ");
|
|
@@ -75817,10 +76365,11 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
75817
76365
|
this.backend = new SqliteSettingsBackend(dbPath, { ...require_dist10.RUNTIME_DEFAULTS }, this.ctx.logger.child("Query"));
|
|
75818
76366
|
await this.backend.initialize();
|
|
75819
76367
|
try {
|
|
75820
|
-
await purgeRetiredSettingsKeys(retiredKeyStoreOf(this.backend), this.ctx.logger.child("RetiredKeys"));
|
|
76368
|
+
await require_retired_settings_keys.purgeRetiredSettingsKeys(require_retired_settings_keys.retiredKeyStoreOf(this.backend), this.ctx.logger.child("RetiredKeys"));
|
|
75821
76369
|
} catch (err) {
|
|
75822
76370
|
this.ctx.logger.warn("Retired-key purge failed", { meta: { error: require_dist10.errMsg(err) } });
|
|
75823
76371
|
}
|
|
76372
|
+
await purgeRetiredCollections(retiredCollectionStoreOf(this.backend), this.ctx.logger.child("RetiredCollections"));
|
|
75824
76373
|
const vectorIndex = this.createVectorIndex(this.backend);
|
|
75825
76374
|
await vectorIndex.loadIndexRegistry();
|
|
75826
76375
|
this.vectorIndex = vectorIndex;
|
|
@@ -75995,7 +76544,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
75995
76544
|
[Symbol.toStringTag]: { value: "Module" }
|
|
75996
76545
|
});
|
|
75997
76546
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
75998
|
-
var require_dist10 =
|
|
76547
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
75999
76548
|
var node_crypto = __require("crypto");
|
|
76000
76549
|
var node_fs_promises = __require("fs/promises");
|
|
76001
76550
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -77874,7 +78423,7 @@ var require_system_config_addon = __commonJS({
|
|
|
77874
78423
|
[Symbol.toStringTag]: { value: "Module" }
|
|
77875
78424
|
});
|
|
77876
78425
|
require_chunk_Cek0wNdY();
|
|
77877
|
-
var require_dist10 =
|
|
78426
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
77878
78427
|
var SECTION_TITLES = {
|
|
77879
78428
|
server: "Server",
|
|
77880
78429
|
auth: "Authentication"
|
|
@@ -95935,7 +96484,7 @@ var require_winston_logging = __commonJS({
|
|
|
95935
96484
|
[Symbol.toStringTag]: { value: "Module" }
|
|
95936
96485
|
});
|
|
95937
96486
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
95938
|
-
var require_dist10 =
|
|
96487
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
95939
96488
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
95940
96489
|
var node_path = __require("path");
|
|
95941
96490
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -107405,9 +107954,9 @@ var require_dist2 = __commonJS({
|
|
|
107405
107954
|
}
|
|
107406
107955
|
});
|
|
107407
107956
|
|
|
107408
|
-
// ../system/dist/manifest-python-deps-
|
|
107409
|
-
var
|
|
107410
|
-
"../system/dist/manifest-python-deps-
|
|
107957
|
+
// ../system/dist/manifest-python-deps-MA_BZGBz.js
|
|
107958
|
+
var require_manifest_python_deps_MA_BZGBz = __commonJS({
|
|
107959
|
+
"../system/dist/manifest-python-deps-MA_BZGBz.js"(exports) {
|
|
107411
107960
|
"use strict";
|
|
107412
107961
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
107413
107962
|
var node_crypto = __require("crypto");
|
|
@@ -111384,6 +111933,8 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111384
111933
|
};
|
|
111385
111934
|
goneHandler = () => {
|
|
111386
111935
|
};
|
|
111936
|
+
/** Source of {@link RegisteredChild.incarnation}; one bump per accepted connection. */
|
|
111937
|
+
nextIncarnation = 1;
|
|
111387
111938
|
eventHandler = null;
|
|
111388
111939
|
logHandler = null;
|
|
111389
111940
|
readinessHandler = null;
|
|
@@ -111658,14 +112209,20 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111658
112209
|
listChildren() {
|
|
111659
112210
|
return [...this.children.values()].map((e) => ({
|
|
111660
112211
|
childId: e.childId,
|
|
111661
|
-
caps: e.caps
|
|
112212
|
+
caps: e.caps,
|
|
112213
|
+
incarnation: e.incarnation
|
|
111662
112214
|
}));
|
|
111663
112215
|
}
|
|
111664
112216
|
/** Register the (single) child-registered handler. Only one handler is active at a time. */
|
|
111665
112217
|
onChildRegistered(handler) {
|
|
111666
112218
|
this.registeredHandler = handler;
|
|
111667
112219
|
}
|
|
111668
|
-
/**
|
|
112220
|
+
/**
|
|
112221
|
+
* Register the (single) child-gone handler. Only one handler is active at a
|
|
112222
|
+
* time. The handler receives the {@link RegisteredChild.incarnation} of the
|
|
112223
|
+
* connection that closed — carry it into whatever teardown it triggers, so a
|
|
112224
|
+
* dead generation cannot tear down its successor's work.
|
|
112225
|
+
*/
|
|
111669
112226
|
onChildGone(handler) {
|
|
111670
112227
|
this.goneHandler = handler;
|
|
111671
112228
|
}
|
|
@@ -111750,6 +112307,7 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111750
112307
|
}
|
|
111751
112308
|
onConnection(channel) {
|
|
111752
112309
|
let childId = null;
|
|
112310
|
+
const incarnation = this.nextIncarnation++;
|
|
111753
112311
|
channel.onEvent((body) => {
|
|
111754
112312
|
const msg = body;
|
|
111755
112313
|
if (msg.kind === "event") {
|
|
@@ -111778,16 +112336,24 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111778
112336
|
if (childId !== null && childId !== msg.childId) throw new Error(`child attempted to change identity from "${childId}" to "${msg.childId}"`);
|
|
111779
112337
|
childId = msg.childId;
|
|
111780
112338
|
const eventPatterns = msg.eventPatterns ?? null;
|
|
112339
|
+
const superseded = this.children.get(msg.childId);
|
|
112340
|
+
if (superseded !== void 0 && superseded.channel !== channel) this.logger?.info("local child superseded by a new connection", {
|
|
112341
|
+
childId: msg.childId,
|
|
112342
|
+
supersededIncarnation: superseded.incarnation,
|
|
112343
|
+
incarnation
|
|
112344
|
+
});
|
|
111781
112345
|
this.children.set(msg.childId, {
|
|
111782
112346
|
childId: msg.childId,
|
|
111783
112347
|
channel,
|
|
111784
112348
|
caps: msg.caps,
|
|
111785
|
-
eventPatterns
|
|
112349
|
+
eventPatterns,
|
|
112350
|
+
incarnation
|
|
111786
112351
|
});
|
|
111787
112352
|
this.logPatternSet(msg.childId, eventPatterns);
|
|
111788
112353
|
this.registeredHandler({
|
|
111789
112354
|
childId: msg.childId,
|
|
111790
|
-
caps: msg.caps
|
|
112355
|
+
caps: msg.caps,
|
|
112356
|
+
incarnation
|
|
111791
112357
|
});
|
|
111792
112358
|
return { ok: true };
|
|
111793
112359
|
}
|
|
@@ -111822,11 +112388,21 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111822
112388
|
throw new Error(`unknown child request kind: ${msg.kind}`);
|
|
111823
112389
|
});
|
|
111824
112390
|
channel.onClose(() => {
|
|
111825
|
-
if (childId
|
|
111826
|
-
|
|
111827
|
-
|
|
111828
|
-
|
|
112391
|
+
if (childId === null) return;
|
|
112392
|
+
const entry = this.children.get(childId);
|
|
112393
|
+
if (entry === void 0) return;
|
|
112394
|
+
if (entry.channel !== channel) {
|
|
112395
|
+
this.logger?.info("ignoring close of a superseded local child connection", {
|
|
112396
|
+
childId,
|
|
112397
|
+
incarnation,
|
|
112398
|
+
liveIncarnation: entry.incarnation
|
|
112399
|
+
});
|
|
112400
|
+
return;
|
|
111829
112401
|
}
|
|
112402
|
+
this.children.delete(childId);
|
|
112403
|
+
this.childEventStats.delete(childId);
|
|
112404
|
+
this.loggedPatternSet.delete(childId);
|
|
112405
|
+
this.goneHandler(childId, incarnation);
|
|
111830
112406
|
});
|
|
111831
112407
|
}
|
|
111832
112408
|
};
|
|
@@ -118184,7 +118760,7 @@ var require_dist3 = __commonJS({
|
|
|
118184
118760
|
"use strict";
|
|
118185
118761
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
118186
118762
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
118187
|
-
var require_dist10 =
|
|
118763
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
118188
118764
|
var require_builtins_alerts_alerts_addon = require_alerts_addon();
|
|
118189
118765
|
require_alerts();
|
|
118190
118766
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
@@ -118209,7 +118785,7 @@ var require_dist3 = __commonJS({
|
|
|
118209
118785
|
require_system_config();
|
|
118210
118786
|
var require_builtins_winston_logging_index = require_winston_logging();
|
|
118211
118787
|
var require_file_data_plane = require_file_data_plane_DUHPHa_Y();
|
|
118212
|
-
var require_manifest_python_deps =
|
|
118788
|
+
var require_manifest_python_deps = require_manifest_python_deps_MA_BZGBz();
|
|
118213
118789
|
var require_resource_monitor = require_resource_monitor_CdnzxBLP();
|
|
118214
118790
|
var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
|
|
118215
118791
|
var zod = require_zod();
|
|
@@ -120105,13 +120681,9 @@ var require_dist3 = __commonJS({
|
|
|
120105
120681
|
return {};
|
|
120106
120682
|
}
|
|
120107
120683
|
var integrationCounter = 0;
|
|
120108
|
-
var deviceCounter = 0;
|
|
120109
120684
|
function nextIntegrationId() {
|
|
120110
120685
|
return `int_${String(++integrationCounter).padStart(4, "0")}`;
|
|
120111
120686
|
}
|
|
120112
|
-
function nextDeviceId() {
|
|
120113
|
-
return `dev_${String(++deviceCounter).padStart(4, "0")}`;
|
|
120114
|
-
}
|
|
120115
120687
|
var INTEGRATIONS_COLUMNS = [
|
|
120116
120688
|
{
|
|
120117
120689
|
name: "id",
|
|
@@ -120151,102 +120723,13 @@ var require_dist3 = __commonJS({
|
|
|
120151
120723
|
notNull: true
|
|
120152
120724
|
}
|
|
120153
120725
|
];
|
|
120154
|
-
var INTEGRATIONS_INDEXES = [{
|
|
120155
|
-
name: "idx_integrations_addon",
|
|
120156
|
-
columns: ["addon_id"]
|
|
120157
|
-
}];
|
|
120158
|
-
var INTEGRATION_SETTINGS_COLUMNS = [
|
|
120159
|
-
{
|
|
120160
|
-
name: "integration_id",
|
|
120161
|
-
type: "TEXT",
|
|
120162
|
-
notNull: true
|
|
120163
|
-
},
|
|
120164
|
-
{
|
|
120165
|
-
name: "key",
|
|
120166
|
-
type: "TEXT",
|
|
120167
|
-
notNull: true
|
|
120168
|
-
},
|
|
120169
|
-
{
|
|
120170
|
-
name: "value",
|
|
120171
|
-
type: "TEXT",
|
|
120172
|
-
notNull: true
|
|
120173
|
-
},
|
|
120174
|
-
{
|
|
120175
|
-
name: "value_type",
|
|
120176
|
-
type: "TEXT",
|
|
120177
|
-
notNull: true,
|
|
120178
|
-
defaultValue: "string"
|
|
120179
|
-
}
|
|
120180
|
-
];
|
|
120181
|
-
var DEVICES_COLUMNS = [
|
|
120182
|
-
{
|
|
120183
|
-
name: "id",
|
|
120184
|
-
type: "TEXT",
|
|
120185
|
-
primaryKey: true
|
|
120186
|
-
},
|
|
120187
|
-
{
|
|
120188
|
-
name: "integration_id",
|
|
120189
|
-
type: "TEXT",
|
|
120190
|
-
notNull: true
|
|
120191
|
-
},
|
|
120192
|
-
{
|
|
120193
|
-
name: "stable_id",
|
|
120194
|
-
type: "TEXT",
|
|
120195
|
-
notNull: true,
|
|
120196
|
-
unique: true
|
|
120197
|
-
},
|
|
120198
|
-
{
|
|
120199
|
-
name: "type",
|
|
120200
|
-
type: "TEXT",
|
|
120201
|
-
notNull: true,
|
|
120202
|
-
defaultValue: "camera"
|
|
120203
|
-
},
|
|
120204
|
-
{
|
|
120205
|
-
name: "name",
|
|
120206
|
-
type: "TEXT",
|
|
120207
|
-
notNull: true
|
|
120208
|
-
},
|
|
120209
|
-
{
|
|
120210
|
-
name: "enabled",
|
|
120211
|
-
type: "INTEGER",
|
|
120212
|
-
notNull: true,
|
|
120213
|
-
defaultValue: 1
|
|
120214
|
-
},
|
|
120215
|
-
{
|
|
120216
|
-
name: "info",
|
|
120217
|
-
type: "JSON",
|
|
120218
|
-
notNull: true,
|
|
120219
|
-
defaultValue: "{}"
|
|
120220
|
-
},
|
|
120221
|
-
{
|
|
120222
|
-
name: "created_at",
|
|
120223
|
-
type: "INTEGER",
|
|
120224
|
-
notNull: true
|
|
120225
|
-
},
|
|
120226
|
-
{
|
|
120227
|
-
name: "updated_at",
|
|
120228
|
-
type: "INTEGER",
|
|
120229
|
-
notNull: true
|
|
120230
|
-
}
|
|
120231
|
-
];
|
|
120232
|
-
var DEVICES_INDEXES = [
|
|
120233
|
-
{
|
|
120234
|
-
name: "idx_devices_integration",
|
|
120235
|
-
columns: ["integration_id"]
|
|
120236
|
-
},
|
|
120237
|
-
{
|
|
120238
|
-
name: "idx_devices_stable",
|
|
120239
|
-
columns: ["stable_id"],
|
|
120240
|
-
unique: true
|
|
120241
|
-
},
|
|
120242
|
-
{
|
|
120243
|
-
name: "idx_devices_type",
|
|
120244
|
-
columns: ["type"]
|
|
120245
|
-
}
|
|
120246
|
-
];
|
|
120247
|
-
var DEVICE_SETTINGS_COLUMNS = [
|
|
120726
|
+
var INTEGRATIONS_INDEXES = [{
|
|
120727
|
+
name: "idx_integrations_addon",
|
|
120728
|
+
columns: ["addon_id"]
|
|
120729
|
+
}];
|
|
120730
|
+
var INTEGRATION_SETTINGS_COLUMNS = [
|
|
120248
120731
|
{
|
|
120249
|
-
name: "
|
|
120732
|
+
name: "integration_id",
|
|
120250
120733
|
type: "TEXT",
|
|
120251
120734
|
notNull: true
|
|
120252
120735
|
},
|
|
@@ -120269,8 +120752,6 @@ var require_dist3 = __commonJS({
|
|
|
120269
120752
|
];
|
|
120270
120753
|
var INTEGRATIONS = "integrations";
|
|
120271
120754
|
var INTEGRATION_SETTINGS = "integration_settings";
|
|
120272
|
-
var DEVICES = "devices";
|
|
120273
|
-
var DEVICE_SETTINGS = "device_settings_kv";
|
|
120274
120755
|
var IntegrationRegistry = class IntegrationRegistry2 {
|
|
120275
120756
|
store;
|
|
120276
120757
|
constructor(backend) {
|
|
@@ -120286,15 +120767,6 @@ var require_dist3 = __commonJS({
|
|
|
120286
120767
|
collection: INTEGRATION_SETTINGS,
|
|
120287
120768
|
columns: INTEGRATION_SETTINGS_COLUMNS
|
|
120288
120769
|
});
|
|
120289
|
-
await this.store.declareCollection({
|
|
120290
|
-
collection: DEVICES,
|
|
120291
|
-
columns: DEVICES_COLUMNS,
|
|
120292
|
-
indexes: DEVICES_INDEXES
|
|
120293
|
-
});
|
|
120294
|
-
await this.store.declareCollection({
|
|
120295
|
-
collection: DEVICE_SETTINGS,
|
|
120296
|
-
columns: DEVICE_SETTINGS_COLUMNS
|
|
120297
|
-
});
|
|
120298
120770
|
const ints = await this.store.query({
|
|
120299
120771
|
collection: INTEGRATIONS,
|
|
120300
120772
|
filter: {
|
|
@@ -120309,20 +120781,6 @@ var require_dist3 = __commonJS({
|
|
|
120309
120781
|
const num = parseInt(ints[0].id.replace("int_", ""), 10);
|
|
120310
120782
|
if (!isNaN(num)) integrationCounter = num;
|
|
120311
120783
|
}
|
|
120312
|
-
const devs = await this.store.query({
|
|
120313
|
-
collection: DEVICES,
|
|
120314
|
-
filter: {
|
|
120315
|
-
orderBy: {
|
|
120316
|
-
field: "id",
|
|
120317
|
-
direction: "desc"
|
|
120318
|
-
},
|
|
120319
|
-
limit: 1
|
|
120320
|
-
}
|
|
120321
|
-
});
|
|
120322
|
-
if (devs[0]) {
|
|
120323
|
-
const num = parseInt(devs[0].id.replace("dev_", ""), 10);
|
|
120324
|
-
if (!isNaN(num)) deviceCounter = num;
|
|
120325
|
-
}
|
|
120326
120784
|
}
|
|
120327
120785
|
async createIntegration(input) {
|
|
120328
120786
|
const id = nextIntegrationId();
|
|
@@ -120406,15 +120864,6 @@ var require_dist3 = __commonJS({
|
|
|
120406
120864
|
async deleteIntegration(id) {
|
|
120407
120865
|
const integration = await this.getIntegration(id);
|
|
120408
120866
|
if (integration !== null && IntegrationRegistry2.isFixed(integration)) throw new Error(`integration "${id}" is fixed \u2014 it belongs to the addon that declared it and cannot be deleted`);
|
|
120409
|
-
const devices = await this.listDevices(id);
|
|
120410
|
-
for (const d of devices) await this.store.deleteWhere({
|
|
120411
|
-
collection: DEVICE_SETTINGS,
|
|
120412
|
-
filter: { where: { device_id: d.id } }
|
|
120413
|
-
});
|
|
120414
|
-
await this.store.deleteWhere({
|
|
120415
|
-
collection: DEVICES,
|
|
120416
|
-
filter: { where: { integration_id: id } }
|
|
120417
|
-
});
|
|
120418
120867
|
await this.store.deleteWhere({
|
|
120419
120868
|
collection: INTEGRATION_SETTINGS,
|
|
120420
120869
|
filter: { where: { integration_id: id } }
|
|
@@ -120459,139 +120908,6 @@ var require_dist3 = __commonJS({
|
|
|
120459
120908
|
async setIntegrationSettings(integrationId, settings) {
|
|
120460
120909
|
for (const [key, value] of Object.entries(settings)) await this.setIntegrationSetting(integrationId, key, value);
|
|
120461
120910
|
}
|
|
120462
|
-
async createDevice(input) {
|
|
120463
|
-
const id = nextDeviceId();
|
|
120464
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
120465
|
-
await this.store.insert({
|
|
120466
|
-
collection: DEVICES,
|
|
120467
|
-
record: {
|
|
120468
|
-
id,
|
|
120469
|
-
data: {
|
|
120470
|
-
integration_id: input.integrationId,
|
|
120471
|
-
stable_id: input.stableId,
|
|
120472
|
-
type: input.type,
|
|
120473
|
-
name: input.name,
|
|
120474
|
-
enabled: input.enabled !== false ? 1 : 0,
|
|
120475
|
-
info: input.info ?? {},
|
|
120476
|
-
created_at: now,
|
|
120477
|
-
updated_at: now
|
|
120478
|
-
}
|
|
120479
|
-
}
|
|
120480
|
-
});
|
|
120481
|
-
if (input.settings) await this.setDeviceSettings(id, input.settings);
|
|
120482
|
-
return {
|
|
120483
|
-
id,
|
|
120484
|
-
integrationId: input.integrationId,
|
|
120485
|
-
stableId: input.stableId,
|
|
120486
|
-
type: input.type,
|
|
120487
|
-
name: input.name,
|
|
120488
|
-
enabled: input.enabled !== false,
|
|
120489
|
-
info: input.info ?? {},
|
|
120490
|
-
createdAt: now,
|
|
120491
|
-
updatedAt: now
|
|
120492
|
-
};
|
|
120493
|
-
}
|
|
120494
|
-
async getDevice(id) {
|
|
120495
|
-
const rows = await this.store.query({
|
|
120496
|
-
collection: DEVICES,
|
|
120497
|
-
filter: {
|
|
120498
|
-
where: { id },
|
|
120499
|
-
limit: 1
|
|
120500
|
-
}
|
|
120501
|
-
});
|
|
120502
|
-
return rows[0] ? this.mapDevice(rows[0].data) : null;
|
|
120503
|
-
}
|
|
120504
|
-
async getDeviceByStableId(stableId) {
|
|
120505
|
-
const rows = await this.store.query({
|
|
120506
|
-
collection: DEVICES,
|
|
120507
|
-
filter: {
|
|
120508
|
-
where: { stable_id: stableId },
|
|
120509
|
-
limit: 1
|
|
120510
|
-
}
|
|
120511
|
-
});
|
|
120512
|
-
return rows[0] ? this.mapDevice(rows[0].data) : null;
|
|
120513
|
-
}
|
|
120514
|
-
async listDevices(integrationId) {
|
|
120515
|
-
return (await this.store.query({
|
|
120516
|
-
collection: DEVICES,
|
|
120517
|
-
filter: {
|
|
120518
|
-
...integrationId ? { where: { integration_id: integrationId } } : {},
|
|
120519
|
-
orderBy: {
|
|
120520
|
-
field: "created_at",
|
|
120521
|
-
direction: "asc"
|
|
120522
|
-
}
|
|
120523
|
-
}
|
|
120524
|
-
})).map((r) => this.mapDevice(r.data));
|
|
120525
|
-
}
|
|
120526
|
-
async listCameras() {
|
|
120527
|
-
return (await this.store.query({
|
|
120528
|
-
collection: DEVICES,
|
|
120529
|
-
filter: {
|
|
120530
|
-
where: { type: "camera" },
|
|
120531
|
-
orderBy: {
|
|
120532
|
-
field: "created_at",
|
|
120533
|
-
direction: "asc"
|
|
120534
|
-
}
|
|
120535
|
-
}
|
|
120536
|
-
})).map((r) => this.mapDevice(r.data));
|
|
120537
|
-
}
|
|
120538
|
-
async updateDevice(id, updates) {
|
|
120539
|
-
const updateRow = { updated_at: Math.floor(Date.now() / 1e3) };
|
|
120540
|
-
if (updates.name !== void 0) updateRow["name"] = updates.name;
|
|
120541
|
-
if (updates.enabled !== void 0) updateRow["enabled"] = updates.enabled ? 1 : 0;
|
|
120542
|
-
if (updates.info !== void 0) updateRow["info"] = updates.info;
|
|
120543
|
-
await this.store.updateWhere({
|
|
120544
|
-
collection: DEVICES,
|
|
120545
|
-
filter: { where: { id } },
|
|
120546
|
-
data: updateRow
|
|
120547
|
-
});
|
|
120548
|
-
return this.getDevice(id);
|
|
120549
|
-
}
|
|
120550
|
-
async deleteDevice(id) {
|
|
120551
|
-
await this.store.deleteWhere({
|
|
120552
|
-
collection: DEVICE_SETTINGS,
|
|
120553
|
-
filter: { where: { device_id: id } }
|
|
120554
|
-
});
|
|
120555
|
-
await this.store.deleteWhere({
|
|
120556
|
-
collection: DEVICES,
|
|
120557
|
-
filter: { where: { id } }
|
|
120558
|
-
});
|
|
120559
|
-
return true;
|
|
120560
|
-
}
|
|
120561
|
-
async getDeviceSettings(deviceId) {
|
|
120562
|
-
const result = {};
|
|
120563
|
-
const rows = await this.store.query({
|
|
120564
|
-
collection: DEVICE_SETTINGS,
|
|
120565
|
-
filter: { where: { device_id: deviceId } }
|
|
120566
|
-
});
|
|
120567
|
-
for (const row of rows) result[String(row.data["key"])] = deserializeSetting(row.data["value"], String(row.data["value_type"]));
|
|
120568
|
-
return result;
|
|
120569
|
-
}
|
|
120570
|
-
async setDeviceSetting(deviceId, key, value) {
|
|
120571
|
-
const s = serializeSetting(value);
|
|
120572
|
-
await this.store.deleteWhere({
|
|
120573
|
-
collection: DEVICE_SETTINGS,
|
|
120574
|
-
filter: { where: {
|
|
120575
|
-
device_id: deviceId,
|
|
120576
|
-
key
|
|
120577
|
-
} }
|
|
120578
|
-
});
|
|
120579
|
-
await this.store.insert({
|
|
120580
|
-
collection: DEVICE_SETTINGS,
|
|
120581
|
-
record: {
|
|
120582
|
-
id: `${deviceId}:${key}`,
|
|
120583
|
-
data: {
|
|
120584
|
-
device_id: deviceId,
|
|
120585
|
-
key,
|
|
120586
|
-
value: s.value,
|
|
120587
|
-
value_type: s.valueType
|
|
120588
|
-
}
|
|
120589
|
-
}
|
|
120590
|
-
});
|
|
120591
|
-
}
|
|
120592
|
-
async setDeviceSettings(deviceId, settings) {
|
|
120593
|
-
for (const [key, value] of Object.entries(settings)) await this.setDeviceSetting(deviceId, key, value);
|
|
120594
|
-
}
|
|
120595
120911
|
mapIntegration(row) {
|
|
120596
120912
|
return {
|
|
120597
120913
|
id: String(row["id"]),
|
|
@@ -120603,19 +120919,6 @@ var require_dist3 = __commonJS({
|
|
|
120603
120919
|
updatedAt: Number(row["updated_at"])
|
|
120604
120920
|
};
|
|
120605
120921
|
}
|
|
120606
|
-
mapDevice(row) {
|
|
120607
|
-
return {
|
|
120608
|
-
id: String(row["id"]),
|
|
120609
|
-
integrationId: String(row["integration_id"]),
|
|
120610
|
-
stableId: String(row["stable_id"]),
|
|
120611
|
-
type: String(row["type"]),
|
|
120612
|
-
name: String(row["name"]),
|
|
120613
|
-
enabled: row["enabled"] === 1,
|
|
120614
|
-
info: readInfo(row["info"]),
|
|
120615
|
-
createdAt: Number(row["created_at"]),
|
|
120616
|
-
updatedAt: Number(row["updated_at"])
|
|
120617
|
-
};
|
|
120618
|
-
}
|
|
120619
120922
|
};
|
|
120620
120923
|
var execFileAsync$2 = (0, node_util.promisify)(node_child_process.execFile);
|
|
120621
120924
|
var NATIVE_SCAN_DEPTH = 4;
|
|
@@ -123109,6 +123412,7 @@ var require_dist3 = __commonJS({
|
|
|
123109
123412
|
const state = {
|
|
123110
123413
|
definition,
|
|
123111
123414
|
providers: /* @__PURE__ */ new Map(),
|
|
123415
|
+
providerOwners: /* @__PURE__ */ new Map(),
|
|
123112
123416
|
activeAddonId: null,
|
|
123113
123417
|
router: null,
|
|
123114
123418
|
disabledProviders: /* @__PURE__ */ new Set()
|
|
@@ -123166,8 +123470,15 @@ var require_dist3 = __commonJS({
|
|
|
123166
123470
|
* Double-register of the same pair is always a programmer error —
|
|
123167
123471
|
* two legitimately different addons implementing the same cap keep
|
|
123168
123472
|
* using different `addonId`s and the user picks one via `configReader`.
|
|
123169
|
-
|
|
123170
|
-
|
|
123473
|
+
*
|
|
123474
|
+
* `owner` (optional) stamps the registration with the incarnation that made
|
|
123475
|
+
* it — see {@link ProviderOwnerToken}. Once stamped, only an unregister
|
|
123476
|
+
* presenting the SAME token can delete it. Registration paths whose key is
|
|
123477
|
+
* reused across process generations (the UDS child manifest, the D3
|
|
123478
|
+
* `registerNode` handshake) MUST pass one; ownerless registrations keep the
|
|
123479
|
+
* historical unconditional behaviour.
|
|
123480
|
+
*/
|
|
123481
|
+
registerProvider(capabilityName, addonId, provider, owner) {
|
|
123171
123482
|
let state = this.capabilities.get(capabilityName);
|
|
123172
123483
|
if (!state) {
|
|
123173
123484
|
const colonIdx = capabilityName.indexOf(":");
|
|
@@ -123199,6 +123510,7 @@ var require_dist3 = __commonJS({
|
|
|
123199
123510
|
if (missing.length > 0) throw new Error(`CapabilityRegistry: provider "${addonId}" for capability "${capabilityName}" declares exposesDeviceSettings: true but is missing DeviceSettingsContribution method(s): ${missing.join(", ")}. Implement them on the provider (or drop the flag from the cap def).`);
|
|
123200
123511
|
}
|
|
123201
123512
|
state.providers.set(addonId, provider);
|
|
123513
|
+
if (owner !== void 0) state.providerOwners.set(addonId, owner);
|
|
123202
123514
|
if (state.definition.mode === "singleton") {
|
|
123203
123515
|
const userChoice = this.configReader?.(capabilityName);
|
|
123204
123516
|
const preferred = state.definition.preferredProvider;
|
|
@@ -123229,7 +123541,8 @@ var require_dist3 = __commonJS({
|
|
|
123229
123541
|
tags: { addonId },
|
|
123230
123542
|
meta: {
|
|
123231
123543
|
capability: capabilityName,
|
|
123232
|
-
mode: state.definition.mode
|
|
123544
|
+
mode: state.definition.mode,
|
|
123545
|
+
...owner === void 0 ? {} : { owner }
|
|
123233
123546
|
}
|
|
123234
123547
|
});
|
|
123235
123548
|
this.emitEvent("capability:provider-registered", {
|
|
@@ -123276,11 +123589,70 @@ var require_dist3 = __commonJS({
|
|
|
123276
123589
|
const declarers = this.manifestDeclarers.get(capabilityName);
|
|
123277
123590
|
return declarers ? [...declarers] : [];
|
|
123278
123591
|
}
|
|
123279
|
-
/**
|
|
123280
|
-
|
|
123592
|
+
/**
|
|
123593
|
+
* The owner token a registration declared, or `null` when the pair is not
|
|
123594
|
+
* registered or was registered without one.
|
|
123595
|
+
*/
|
|
123596
|
+
getProviderOwner(capabilityName, addonId) {
|
|
123597
|
+
return this.capabilities.get(capabilityName)?.providerOwners.get(addonId) ?? null;
|
|
123598
|
+
}
|
|
123599
|
+
/**
|
|
123600
|
+
* Hand an EXISTING registration to a new owner, without touching the provider
|
|
123601
|
+
* instance, the active-singleton selection or any event.
|
|
123602
|
+
*
|
|
123603
|
+
* The takeover case: a replacement runner handshakes with an UNCHANGED cap
|
|
123604
|
+
* set, so the manifest diff correctly leaves the registration alone — but the
|
|
123605
|
+
* registration is still stamped with the dead generation's token, and nothing
|
|
123606
|
+
* the LIVE generation can present would ever match it. Re-stamping keeps the
|
|
123607
|
+
* capability cleanable when the successor eventually dies.
|
|
123608
|
+
*
|
|
123609
|
+
* Returns `false` when the pair is not registered (nothing to hand over) —
|
|
123610
|
+
* the caller decides whether that is a register-instead or a diagnosis.
|
|
123611
|
+
*/
|
|
123612
|
+
reassignProviderOwner(capabilityName, addonId, owner) {
|
|
123613
|
+
const state = this.capabilities.get(capabilityName);
|
|
123614
|
+
if (!state || !state.providers.has(addonId)) return false;
|
|
123615
|
+
const previous = state.providerOwners.get(addonId);
|
|
123616
|
+
if (previous === owner) return true;
|
|
123617
|
+
state.providerOwners.set(addonId, owner);
|
|
123618
|
+
this.logger.info("Capability registration ownership reassigned", {
|
|
123619
|
+
tags: { addonId },
|
|
123620
|
+
meta: {
|
|
123621
|
+
capability: capabilityName,
|
|
123622
|
+
owner,
|
|
123623
|
+
previousOwner: previous ?? null
|
|
123624
|
+
}
|
|
123625
|
+
});
|
|
123626
|
+
return true;
|
|
123627
|
+
}
|
|
123628
|
+
/**
|
|
123629
|
+
* Unregister a provider. For singleton: clears active. For collection:
|
|
123630
|
+
* removes from list.
|
|
123631
|
+
*
|
|
123632
|
+
* `owner` is the caller's PROOF that it owns the registration it is deleting.
|
|
123633
|
+
* When the registration declared an owner (see {@link ProviderOwnerToken})
|
|
123634
|
+
* and the caller presents a different one — or none at all — this is a
|
|
123635
|
+
* logged no-op: the caller belongs to a generation that no longer holds the
|
|
123636
|
+
* key. This is what stops a dead runner's teardown from erasing its
|
|
123637
|
+
* successor's live registration ([D188](../../../../docs/decisions/adr-0188-an-unregister-carries-proof-of-ownership.md)).
|
|
123638
|
+
*/
|
|
123639
|
+
unregisterProvider(capabilityName, addonId, owner) {
|
|
123281
123640
|
const state = this.capabilities.get(capabilityName);
|
|
123282
123641
|
if (!state) return;
|
|
123642
|
+
const registeredOwner = state.providerOwners.get(addonId);
|
|
123643
|
+
if (registeredOwner !== void 0 && registeredOwner !== owner) {
|
|
123644
|
+
this.logger.info("Unregister ignored \u2014 registration is owned by another incarnation", {
|
|
123645
|
+
tags: { addonId },
|
|
123646
|
+
meta: {
|
|
123647
|
+
capability: capabilityName,
|
|
123648
|
+
registeredOwner,
|
|
123649
|
+
presentedOwner: owner ?? null
|
|
123650
|
+
}
|
|
123651
|
+
});
|
|
123652
|
+
return;
|
|
123653
|
+
}
|
|
123283
123654
|
state.providers.delete(addonId);
|
|
123655
|
+
state.providerOwners.delete(addonId);
|
|
123284
123656
|
if (state.definition.mode === "singleton" && state.activeAddonId === addonId) {
|
|
123285
123657
|
const userChoice = this.configReader?.(capabilityName);
|
|
123286
123658
|
const preferred = state.definition.preferredProvider;
|
|
@@ -198254,6 +198626,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198254
198626
|
function safeSegment(name2) {
|
|
198255
198627
|
return name2.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
198256
198628
|
}
|
|
198629
|
+
var EXACT_SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
198257
198630
|
var StagingArea = class {
|
|
198258
198631
|
stagingDir;
|
|
198259
198632
|
deps;
|
|
@@ -198271,8 +198644,12 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198271
198644
|
if (input.signal.aborted) throw new Error("staging aborted after fetch");
|
|
198272
198645
|
await this.deps.extract(tgz, destDir);
|
|
198273
198646
|
const raw = JSON.parse(node_fs.readFileSync(node_path.join(destDir, "package.json"), "utf-8"));
|
|
198274
|
-
if (raw.version !==
|
|
198275
|
-
|
|
198647
|
+
if (typeof raw.version !== "string" || raw.version.length === 0) throw new Error(`staging validation failed: ${input.name} staged a package.json with no version`);
|
|
198648
|
+
if (EXACT_SEMVER.test(input.version) && raw.version !== input.version) throw new Error(`staging validation failed: ${input.name} expected version ${input.version}, got ${raw.version}`);
|
|
198649
|
+
return {
|
|
198650
|
+
stagedPath: destDir,
|
|
198651
|
+
resolvedVersion: raw.version
|
|
198652
|
+
};
|
|
198276
198653
|
}
|
|
198277
198654
|
cleanup(jobId) {
|
|
198278
198655
|
node_fs.rmSync(node_path.join(this.stagingDir, jobId), {
|
|
@@ -238430,7 +238807,7 @@ var require_dist9 = __commonJS({
|
|
|
238430
238807
|
postBufferSec: zod.z.number().min(0).optional()
|
|
238431
238808
|
});
|
|
238432
238809
|
var DEFAULT_EVENTS_BAND_BUFFER_SEC = {
|
|
238433
|
-
preBufferSec:
|
|
238810
|
+
preBufferSec: 15,
|
|
238434
238811
|
postBufferSec: 30
|
|
238435
238812
|
};
|
|
238436
238813
|
var VISIT_MERGE_GAP_MS = DEFAULT_EVENTS_BAND_BUFFER_SEC.postBufferSec * 1e3;
|
|
@@ -239044,12 +239421,17 @@ var require_dist9 = __commonJS({
|
|
|
239044
239421
|
"tflite",
|
|
239045
239422
|
"pt"
|
|
239046
239423
|
];
|
|
239424
|
+
var FAMILY_LABEL = {
|
|
239425
|
+
yolo26: "YOLO26",
|
|
239426
|
+
yolov9: "YOLOv9"
|
|
239427
|
+
};
|
|
239047
239428
|
function familyLabel(family) {
|
|
239048
|
-
return family.toUpperCase();
|
|
239429
|
+
return FAMILY_LABEL[family] ?? family.toUpperCase();
|
|
239049
239430
|
}
|
|
239431
|
+
var RESOLUTION_IN_NAME = /\s*@\s*\d+\s*(?:[x×]\s*\d+)?/gi;
|
|
239050
239432
|
function tierLabel(family, baseName) {
|
|
239051
239433
|
const fl = familyLabel(family);
|
|
239052
|
-
const stripped = baseName.replace(new RegExp(`^${fl}\\s+`, "i"), "").trim();
|
|
239434
|
+
const stripped = baseName.replace(new RegExp(`^${fl}\\s+`, "i"), "").replace(RESOLUTION_IN_NAME, "").trim();
|
|
239053
239435
|
return stripped.length > 0 ? stripped : baseName;
|
|
239054
239436
|
}
|
|
239055
239437
|
function variantLabel(precision, optimization) {
|
|
@@ -239066,9 +239448,11 @@ var require_dist9 = __commonJS({
|
|
|
239066
239448
|
return sizes.length > 0 ? Math.min(...sizes) : 0;
|
|
239067
239449
|
}
|
|
239068
239450
|
var TIER_ORDER = [
|
|
239451
|
+
"t",
|
|
239069
239452
|
"n",
|
|
239070
239453
|
"s",
|
|
239071
239454
|
"m",
|
|
239455
|
+
"c",
|
|
239072
239456
|
"l",
|
|
239073
239457
|
"x"
|
|
239074
239458
|
];
|
|
@@ -239105,7 +239489,7 @@ var require_dist9 = __commonJS({
|
|
|
239105
239489
|
if (a.optimization !== b.optimization) return a.optimization === "fast" ? 1 : -1;
|
|
239106
239490
|
return PRECISION_ORDER.indexOf(a.precision) - PRECISION_ORDER.indexOf(b.precision);
|
|
239107
239491
|
});
|
|
239108
|
-
const base = options.find((o) => o.precision === "fp32" && o.optimization === "standard"
|
|
239492
|
+
const base = options.find((o) => o.precision === "fp32" && o.optimization === "standard") ?? options[0];
|
|
239109
239493
|
if (base === void 0) continue;
|
|
239110
239494
|
const baseEntry = entries2.find((e) => e.id === base.modelId);
|
|
239111
239495
|
tierList.push({
|
|
@@ -239135,15 +239519,9 @@ var require_dist9 = __commonJS({
|
|
|
239135
239519
|
function resolveVariantModelId(models, selection) {
|
|
239136
239520
|
const precision = selection.precision ?? "fp32";
|
|
239137
239521
|
const optimization = selection.optimization ?? "standard";
|
|
239138
|
-
|
|
239139
|
-
|
|
239140
|
-
|
|
239141
|
-
if ((m.group.precision ?? "fp32") !== precision) continue;
|
|
239142
|
-
if ((m.group.optimization ?? "standard") !== optimization) continue;
|
|
239143
|
-
if (m.group.resolution !== selection.resolution) continue;
|
|
239144
|
-
return m.id;
|
|
239145
|
-
}
|
|
239146
|
-
return null;
|
|
239522
|
+
const candidates = models.filter((m) => m.group !== void 0 && m.group.family === selection.family && m.group.tier === selection.tier && (m.group.precision ?? "fp32") === precision && (m.group.optimization ?? "standard") === optimization);
|
|
239523
|
+
if (selection.resolution !== void 0) return candidates.find((m) => m.group?.resolution === selection.resolution)?.id ?? null;
|
|
239524
|
+
return candidates.toSorted((a, b) => (b.group?.resolution ?? Infinity) - (a.group?.resolution ?? Infinity))[0]?.id ?? null;
|
|
239147
239525
|
}
|
|
239148
239526
|
function describeModelVariant(models, modelId) {
|
|
239149
239527
|
const entry = models.find((m) => m.id === modelId);
|
|
@@ -240550,11 +240928,15 @@ export default function block(ctx: AddonContext) {
|
|
|
240550
240928
|
): Promise<void> {
|
|
240551
240929
|
const resolved: Record<string, unknown> = {}
|
|
240552
240930
|
for (const [k, v] of Object.entries(args)) resolved[k] = substitute(v, vars)
|
|
240553
|
-
const client = (ctx.api
|
|
240554
|
-
|
|
240931
|
+
const client: unknown = Reflect.get(ctx.api, capToRouterKey(cap))
|
|
240932
|
+
const proc: unknown =
|
|
240933
|
+
typeof client === 'object' && client !== null ? Reflect.get(client, method) : undefined
|
|
240934
|
+
const mutate: unknown =
|
|
240935
|
+
typeof proc === 'object' && proc !== null ? Reflect.get(proc, 'mutate') : undefined
|
|
240936
|
+
if (typeof mutate !== 'function') {
|
|
240555
240937
|
throw new Error('no such device method: ' + cap + '.' + method)
|
|
240556
240938
|
}
|
|
240557
|
-
await
|
|
240939
|
+
await mutate({ ...resolved, deviceId })
|
|
240558
240940
|
}
|
|
240559
240941
|
|
|
240560
240942
|
async function evaluateConditions(): Promise<boolean> {
|
|
@@ -246401,128 +246783,103 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
246401
246783
|
var AUDIO_MACRO_LABELS = [
|
|
246402
246784
|
{
|
|
246403
246785
|
id: "speech",
|
|
246404
|
-
name: "Speech"
|
|
246405
|
-
icon: "\u{1F5E3}\uFE0F"
|
|
246786
|
+
name: "Speech"
|
|
246406
246787
|
},
|
|
246407
246788
|
{
|
|
246408
246789
|
id: "scream",
|
|
246409
|
-
name: "Scream / Shout"
|
|
246410
|
-
icon: "\u{1F631}"
|
|
246790
|
+
name: "Scream / Shout"
|
|
246411
246791
|
},
|
|
246412
246792
|
{
|
|
246413
246793
|
id: "crying",
|
|
246414
|
-
name: "Crying / Baby"
|
|
246415
|
-
icon: "\u{1F622}"
|
|
246794
|
+
name: "Crying / Baby"
|
|
246416
246795
|
},
|
|
246417
246796
|
{
|
|
246418
246797
|
id: "laughter",
|
|
246419
|
-
name: "Laughter"
|
|
246420
|
-
icon: "\u{1F602}"
|
|
246798
|
+
name: "Laughter"
|
|
246421
246799
|
},
|
|
246422
246800
|
{
|
|
246423
246801
|
id: "music",
|
|
246424
|
-
name: "Music"
|
|
246425
|
-
icon: "\u{1F3B5}"
|
|
246802
|
+
name: "Music"
|
|
246426
246803
|
},
|
|
246427
246804
|
{
|
|
246428
246805
|
id: "dog",
|
|
246429
|
-
name: "Dog"
|
|
246430
|
-
icon: "\u{1F415}"
|
|
246806
|
+
name: "Dog"
|
|
246431
246807
|
},
|
|
246432
246808
|
{
|
|
246433
246809
|
id: "cat",
|
|
246434
|
-
name: "Cat"
|
|
246435
|
-
icon: "\u{1F408}"
|
|
246810
|
+
name: "Cat"
|
|
246436
246811
|
},
|
|
246437
246812
|
{
|
|
246438
246813
|
id: "bird",
|
|
246439
|
-
name: "Bird"
|
|
246440
|
-
icon: "\u{1F426}"
|
|
246814
|
+
name: "Bird"
|
|
246441
246815
|
},
|
|
246442
246816
|
{
|
|
246443
246817
|
id: "animal",
|
|
246444
|
-
name: "Animal (other)"
|
|
246445
|
-
icon: "\u{1F43E}"
|
|
246818
|
+
name: "Animal (other)"
|
|
246446
246819
|
},
|
|
246447
246820
|
{
|
|
246448
246821
|
id: "alarm",
|
|
246449
|
-
name: "Alarm / Siren"
|
|
246450
|
-
icon: "\u{1F6A8}"
|
|
246822
|
+
name: "Alarm / Siren"
|
|
246451
246823
|
},
|
|
246452
246824
|
{
|
|
246453
246825
|
id: "doorbell",
|
|
246454
|
-
name: "Doorbell / Knock"
|
|
246455
|
-
icon: "\u{1F514}"
|
|
246826
|
+
name: "Doorbell / Knock"
|
|
246456
246827
|
},
|
|
246457
246828
|
{
|
|
246458
246829
|
id: "glass_breaking",
|
|
246459
|
-
name: "Glass Breaking"
|
|
246460
|
-
icon: "\u{1F4A5}"
|
|
246830
|
+
name: "Glass Breaking"
|
|
246461
246831
|
},
|
|
246462
246832
|
{
|
|
246463
246833
|
id: "gunshot",
|
|
246464
|
-
name: "Gunshot / Explosion"
|
|
246465
|
-
icon: "\u{1F4A3}"
|
|
246834
|
+
name: "Gunshot / Explosion"
|
|
246466
246835
|
},
|
|
246467
246836
|
{
|
|
246468
246837
|
id: "vehicle",
|
|
246469
|
-
name: "Vehicle"
|
|
246470
|
-
icon: "\u{1F697}"
|
|
246838
|
+
name: "Vehicle"
|
|
246471
246839
|
},
|
|
246472
246840
|
{
|
|
246473
246841
|
id: "siren",
|
|
246474
|
-
name: "Emergency Siren"
|
|
246475
|
-
icon: "\u{1F691}"
|
|
246842
|
+
name: "Emergency Siren"
|
|
246476
246843
|
},
|
|
246477
246844
|
{
|
|
246478
246845
|
id: "fire",
|
|
246479
|
-
name: "Fire / Smoke"
|
|
246480
|
-
icon: "\u{1F525}"
|
|
246846
|
+
name: "Fire / Smoke"
|
|
246481
246847
|
},
|
|
246482
246848
|
{
|
|
246483
246849
|
id: "water",
|
|
246484
|
-
name: "Water"
|
|
246485
|
-
icon: "\u{1F4A7}"
|
|
246850
|
+
name: "Water"
|
|
246486
246851
|
},
|
|
246487
246852
|
{
|
|
246488
246853
|
id: "wind",
|
|
246489
|
-
name: "Wind / Weather"
|
|
246490
|
-
icon: "\u{1F32C}\uFE0F"
|
|
246854
|
+
name: "Wind / Weather"
|
|
246491
246855
|
},
|
|
246492
246856
|
{
|
|
246493
246857
|
id: "door",
|
|
246494
|
-
name: "Door"
|
|
246495
|
-
icon: "\u{1F6AA}"
|
|
246858
|
+
name: "Door"
|
|
246496
246859
|
},
|
|
246497
246860
|
{
|
|
246498
246861
|
id: "footsteps",
|
|
246499
|
-
name: "Footsteps"
|
|
246500
|
-
icon: "\u{1F463}"
|
|
246862
|
+
name: "Footsteps"
|
|
246501
246863
|
},
|
|
246502
246864
|
{
|
|
246503
246865
|
id: "crowd",
|
|
246504
|
-
name: "Crowd / Chatter"
|
|
246505
|
-
icon: "\u{1F465}"
|
|
246866
|
+
name: "Crowd / Chatter"
|
|
246506
246867
|
},
|
|
246507
246868
|
{
|
|
246508
246869
|
id: "telephone",
|
|
246509
|
-
name: "Telephone"
|
|
246510
|
-
icon: "\u{1F4DE}"
|
|
246870
|
+
name: "Telephone"
|
|
246511
246871
|
},
|
|
246512
246872
|
{
|
|
246513
246873
|
id: "engine",
|
|
246514
|
-
name: "Engine / Motor"
|
|
246515
|
-
icon: "\u2699\uFE0F"
|
|
246874
|
+
name: "Engine / Motor"
|
|
246516
246875
|
},
|
|
246517
246876
|
{
|
|
246518
246877
|
id: "tools",
|
|
246519
|
-
name: "Tools / Construction"
|
|
246520
|
-
icon: "\u{1F528}"
|
|
246878
|
+
name: "Tools / Construction"
|
|
246521
246879
|
},
|
|
246522
246880
|
{
|
|
246523
246881
|
id: "silence",
|
|
246524
|
-
name: "Silence"
|
|
246525
|
-
icon: "\u{1F92B}"
|
|
246882
|
+
name: "Silence"
|
|
246526
246883
|
}
|
|
246527
246884
|
];
|
|
246528
246885
|
var YAMNET_TO_MACRO = {
|
|
@@ -249110,6 +249467,35 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
249110
249467
|
*/
|
|
249111
249468
|
hasFace: zod.z.boolean().optional(),
|
|
249112
249469
|
/**
|
|
249470
|
+
* This track has a face row IN THE GALLERY: a crop **and** an embedding — a
|
|
249471
|
+
* face an operator could ASSIGN to an identity.
|
|
249472
|
+
*
|
|
249473
|
+
* The STRICT twin of {@link hasFace}, and the pair only earns its keep
|
|
249474
|
+
* because the two disagree. `hasFace` is stamped at the TOP of the face
|
|
249475
|
+
* branch, before every gate, and means no more than "a face detector produced
|
|
249476
|
+
* a face detail". This one is stamped at the single moment the gallery row
|
|
249477
|
+
* LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
|
|
249478
|
+
* past the embedding-magnitude verdict, the `minFacePx` detection gate, the
|
|
249479
|
+
* candidate gate, the imageless-track drop (no crop was ever captured) and
|
|
249480
|
+
* the crop-store drop. Everything between the detector and that insert can
|
|
249481
|
+
* legitimately refuse the face, so a flag written any earlier promises the
|
|
249482
|
+
* operator something to assign and delivers nothing.
|
|
249483
|
+
*
|
|
249484
|
+
* **Independent of recognition.** A face collected but never auto-matched is
|
|
249485
|
+
* still assignable — it is in fact the face an operator most wants to reach —
|
|
249486
|
+
* so this is NOT gated on `recognizedIdentityId`. Recognition lands in
|
|
249487
|
+
* `subLabel`; this says only that the raw material exists.
|
|
249488
|
+
*
|
|
249489
|
+
* **Set once, never cleared.** A track that produced a gallery row produced
|
|
249490
|
+
* one; deleting the row later is the gallery's business, not this flag's.
|
|
249491
|
+
*
|
|
249492
|
+
* **Absent ≠ false**, the same rule as {@link hasFace}: every row written
|
|
249493
|
+
* before the column omits it, and so does every server that predates the
|
|
249494
|
+
* field. A consumer must test `=== true` and render nothing otherwise —
|
|
249495
|
+
* never infer "no assignable face".
|
|
249496
|
+
*/
|
|
249497
|
+
hasEmbeddedFace: zod.z.boolean().optional(),
|
|
249498
|
+
/**
|
|
249113
249499
|
* This subject CONTAINS a folded rider — a person the rider-pairing step
|
|
249114
249500
|
* ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
|
|
249115
249501
|
* so the passage is tracked once and as a VEHICLE.
|
|
@@ -250870,6 +251256,32 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
250870
251256
|
/** FRAME-space bbox (already mapped back from crop space). */
|
|
250871
251257
|
bbox: NativeCropBboxSchema.optional(),
|
|
250872
251258
|
embedding: zod.z.string().optional(),
|
|
251259
|
+
/**
|
|
251260
|
+
* L2 magnitude of the RAW pre-normalization embedding. Additive and optional
|
|
251261
|
+
* so an older runner that does not send it degrades to "unmeasurable", which
|
|
251262
|
+
* the consuming gate treats as accept — never as reject.
|
|
251263
|
+
*/
|
|
251264
|
+
embeddingMagnitude: zod.z.number().optional(),
|
|
251265
|
+
/**
|
|
251266
|
+
* Model that produced `embedding` — the SAME contract `labelModelId` has for
|
|
251267
|
+
* `label`, and for the same reason: the producing node is the only side that
|
|
251268
|
+
* knows which model actually ran, and re-deriving it downstream is exactly
|
|
251269
|
+
* the inference this model exists to forbid.
|
|
251270
|
+
*
|
|
251271
|
+
* Additive and optional, so a runner that predates it degrades to
|
|
251272
|
+
* "unmeasurable" rather than to a wrong answer — the consumer falls back to
|
|
251273
|
+
* its previous assumption instead of refusing.
|
|
251274
|
+
*
|
|
251275
|
+
* Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
|
|
251276
|
+
* to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
|
|
251277
|
+
* with nothing on the wire it took the id from the GALLERY, so the gate
|
|
251278
|
+
* compared the gallery against itself and could never fire. That turned the
|
|
251279
|
+
* one guard protecting the face feature space into a tautology, and it is
|
|
251280
|
+
* why swapping `face-embedding`'s model would NOT have stranded the enrolled
|
|
251281
|
+
* gallery as the catalog comment predicts — it would have scored the new
|
|
251282
|
+
* model's vectors against the old model's samples in one cosine space.
|
|
251283
|
+
*/
|
|
251284
|
+
embeddingModelId: zod.z.string().optional(),
|
|
250873
251285
|
label: zod.z.string().optional(),
|
|
250874
251286
|
/**
|
|
250875
251287
|
* The tier `label` occupies, copied VERBATIM from the producing step's
|
|
@@ -252694,6 +253106,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
252694
253106
|
isBattery: zod.z.boolean(),
|
|
252695
253107
|
reason: zod.z.enum([
|
|
252696
253108
|
"disabled",
|
|
253109
|
+
"offline",
|
|
252697
253110
|
"sleeping",
|
|
252698
253111
|
"unreachable",
|
|
252699
253112
|
"waking"
|
|
@@ -252784,9 +253197,17 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
252784
253197
|
sleeping: zod.z.boolean(),
|
|
252785
253198
|
/** Current device state rendered over the cached frame. State images
|
|
252786
253199
|
* remain authoritative even when their photographic background is
|
|
252787
|
-
* old; null means the link must carry a current camera frame.
|
|
253200
|
+
* old; null means the link must carry a current camera frame.
|
|
253201
|
+
*
|
|
253202
|
+
* **This is the ONLY source a surface may derive an inactive tile
|
|
253203
|
+
* from.** A client that reads a device's `online`/`disabled` flags and
|
|
253204
|
+
* draws its own verdict will disagree with the pixels it is painting
|
|
253205
|
+
* — the frame comes from here, and so must the sentence over it
|
|
253206
|
+
* (D190). Pair with `ageMs` for the age of the photograph behind the
|
|
253207
|
+
* state. */
|
|
252788
253208
|
stateReason: zod.z.enum([
|
|
252789
253209
|
"disabled",
|
|
253210
|
+
"offline",
|
|
252790
253211
|
"sleeping",
|
|
252791
253212
|
"unreachable",
|
|
252792
253213
|
"waking"
|
|
@@ -273776,6 +274197,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273776
274197
|
function normalizeAudioLabel(value) {
|
|
273777
274198
|
return value.startsWith(AUDIO_KIND_PREFIX) ? value.slice(6) : value;
|
|
273778
274199
|
}
|
|
274200
|
+
function audioKindId(macroId) {
|
|
274201
|
+
return macroId.startsWith(AUDIO_KIND_PREFIX) ? macroId : `${AUDIO_KIND_PREFIX}${macroId}`;
|
|
274202
|
+
}
|
|
273779
274203
|
var NC_AUDIO_DB_MIN = -96;
|
|
273780
274204
|
var NC_AUDIO_DB_MAX = 0;
|
|
273781
274205
|
var NC_AUDIO_DB_STEP = 3;
|
|
@@ -273823,8 +274247,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273823
274247
|
samplingSeconds: clampInt(patch.samplingSeconds ?? base.samplingSeconds, 1, 300)
|
|
273824
274248
|
};
|
|
273825
274249
|
}
|
|
273826
|
-
var UNKNOWN_LABEL_ICON = "\u{1F508}";
|
|
273827
|
-
var ICON_BY_ID = new Map(AUDIO_MACRO_LABELS.flatMap((macro2) => macro2.icon === void 0 ? [] : [[macro2.id, macro2.icon]]));
|
|
273828
274250
|
function audioLabelChoices(taxonomy, selected) {
|
|
273829
274251
|
const fromHub = taxonomy?.audioKinds ?? [];
|
|
273830
274252
|
const choices = fromHub.length > 0 ? fromHub.map((entry) => {
|
|
@@ -273832,13 +274254,13 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273832
274254
|
return {
|
|
273833
274255
|
value: id,
|
|
273834
274256
|
label: entry.label,
|
|
273835
|
-
|
|
274257
|
+
iconId: audioKindId(id),
|
|
273836
274258
|
unknown: false
|
|
273837
274259
|
};
|
|
273838
274260
|
}) : AUDIO_MACRO_LABELS.map((macro2) => ({
|
|
273839
274261
|
value: macro2.id,
|
|
273840
274262
|
label: macro2.name,
|
|
273841
|
-
|
|
274263
|
+
iconId: audioKindId(macro2.id),
|
|
273842
274264
|
unknown: false
|
|
273843
274265
|
}));
|
|
273844
274266
|
const known = new Set(choices.map((choice) => choice.value));
|
|
@@ -273850,7 +274272,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273850
274272
|
choices.push({
|
|
273851
274273
|
value: id,
|
|
273852
274274
|
label: id,
|
|
273853
|
-
|
|
274275
|
+
iconId: audioKindId(id),
|
|
273854
274276
|
unknown: true
|
|
273855
274277
|
});
|
|
273856
274278
|
}
|
|
@@ -274574,6 +274996,74 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
274574
274996
|
if (map !== void 0) return map[String(deviceId)] ?? 0;
|
|
274575
274997
|
return rule.lastGeneratedAt ?? 0;
|
|
274576
274998
|
}
|
|
274999
|
+
var CLUSTER_MODEL_SCOPED_STEPS = [{
|
|
275000
|
+
stepId: "face-embedding",
|
|
275001
|
+
label: "Face recognition model",
|
|
275002
|
+
defaultModelId: "arcface-r100",
|
|
275003
|
+
indexName: "the enrolled face gallery",
|
|
275004
|
+
options: [
|
|
275005
|
+
{
|
|
275006
|
+
id: "arcface-r100",
|
|
275007
|
+
label: "ArcFace (ResNet34)"
|
|
275008
|
+
},
|
|
275009
|
+
{
|
|
275010
|
+
id: "auraface-r100",
|
|
275011
|
+
label: "AuraFace R100"
|
|
275012
|
+
},
|
|
275013
|
+
{
|
|
275014
|
+
id: "inception-resnet-v1",
|
|
275015
|
+
label: "Inception ResNet V1"
|
|
275016
|
+
}
|
|
275017
|
+
]
|
|
275018
|
+
}, {
|
|
275019
|
+
stepId: "clip-embedding",
|
|
275020
|
+
label: "Semantic search model",
|
|
275021
|
+
defaultModelId: "mobileclip-s1",
|
|
275022
|
+
indexName: "the object semantic-search index",
|
|
275023
|
+
options: [{
|
|
275024
|
+
id: "mobileclip-s1",
|
|
275025
|
+
label: "MobileCLIP S1"
|
|
275026
|
+
}, {
|
|
275027
|
+
id: "mobileclip-s2",
|
|
275028
|
+
label: "MobileCLIP S2"
|
|
275029
|
+
}]
|
|
275030
|
+
}];
|
|
275031
|
+
var CLUSTER_MODEL_SECTION_ID = "cluster-step-models";
|
|
275032
|
+
function clusterModelSettingKey(stepId) {
|
|
275033
|
+
return `clusterModel:${stepId}`;
|
|
275034
|
+
}
|
|
275035
|
+
function isClusterScopedStep(stepId) {
|
|
275036
|
+
return CLUSTER_MODEL_SCOPED_STEPS.some((s) => s.stepId === stepId);
|
|
275037
|
+
}
|
|
275038
|
+
var DEFAULT_CLUSTER_STEP_MODELS = Object.freeze(Object.fromEntries(CLUSTER_MODEL_SCOPED_STEPS.map((s) => [s.stepId, s.defaultModelId])));
|
|
275039
|
+
var ChosenModelIdSchema = zod.z.string().min(1);
|
|
275040
|
+
function readClusterStepModels(config) {
|
|
275041
|
+
const out = {};
|
|
275042
|
+
for (const step2 of CLUSTER_MODEL_SCOPED_STEPS) {
|
|
275043
|
+
const parsed = ChosenModelIdSchema.safeParse(config[clusterModelSettingKey(step2.stepId)]);
|
|
275044
|
+
out[step2.stepId] = parsed.success ? parsed.data : step2.defaultModelId;
|
|
275045
|
+
}
|
|
275046
|
+
return out;
|
|
275047
|
+
}
|
|
275048
|
+
function isHydratedField$2(entry) {
|
|
275049
|
+
return typeof entry === "object" && entry !== null && "key" in entry;
|
|
275050
|
+
}
|
|
275051
|
+
function pickClusterStepModels(view) {
|
|
275052
|
+
if (view === null) return DEFAULT_CLUSTER_STEP_MODELS;
|
|
275053
|
+
const flat = {};
|
|
275054
|
+
const wanted = new Set(CLUSTER_MODEL_SCOPED_STEPS.map((s) => clusterModelSettingKey(s.stepId)));
|
|
275055
|
+
for (const section of view.sections) for (const entry of section.fields) {
|
|
275056
|
+
if (!isHydratedField$2(entry) || typeof entry.key !== "string") continue;
|
|
275057
|
+
if (wanted.has(entry.key)) flat[entry.key] = entry.value;
|
|
275058
|
+
}
|
|
275059
|
+
return readClusterStepModels(flat);
|
|
275060
|
+
}
|
|
275061
|
+
function resolveClusterStepModelId(stepId, models) {
|
|
275062
|
+
const step2 = CLUSTER_MODEL_SCOPED_STEPS.find((s) => s.stepId === stepId);
|
|
275063
|
+
if (step2 === void 0) return null;
|
|
275064
|
+
const chosen = models[stepId];
|
|
275065
|
+
return chosen !== void 0 && chosen !== "" ? chosen : step2.defaultModelId;
|
|
275066
|
+
}
|
|
274577
275067
|
var DETAIL_CROP_SECTION_ID = "detail-crop";
|
|
274578
275068
|
var DETAIL_CROP_PADDING_KEY = "detailCropPaddingRatio";
|
|
274579
275069
|
var DETAIL_CROP_SQUARE_KEY = "detailCropSquare";
|
|
@@ -274621,6 +275111,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
274621
275111
|
}
|
|
274622
275112
|
return readDetailCropConvention(flat);
|
|
274623
275113
|
}
|
|
275114
|
+
var FULL_IMAGE_BBOX = Object.freeze({
|
|
275115
|
+
x: 0,
|
|
275116
|
+
y: 0,
|
|
275117
|
+
w: 1,
|
|
275118
|
+
h: 1
|
|
275119
|
+
});
|
|
274624
275120
|
var DETAIL_CROP_PADDING_FIELD = {
|
|
274625
275121
|
min: 0,
|
|
274626
275122
|
max: 1,
|
|
@@ -276356,6 +276852,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276356
276852
|
exports.CAP_NAMES_WITH_STATUS = CAP_NAMES_WITH_STATUS;
|
|
276357
276853
|
exports.CAP_NODE_PIN_CONTEXT_KEY = require_sleep.CAP_NODE_PIN_CONTEXT_KEY;
|
|
276358
276854
|
exports.CAP_PROVIDER_KIND_MAP = CAP_PROVIDER_KIND_MAP;
|
|
276855
|
+
exports.CLUSTER_MODEL_SCOPED_STEPS = CLUSTER_MODEL_SCOPED_STEPS;
|
|
276856
|
+
exports.CLUSTER_MODEL_SECTION_ID = CLUSTER_MODEL_SECTION_ID;
|
|
276359
276857
|
exports.COCO_80_LABELS = COCO_80_LABELS;
|
|
276360
276858
|
exports.COCO_TO_MACRO = COCO_TO_MACRO;
|
|
276361
276859
|
exports.CONNECTION_TEST_TIMEOUT_MS = CONNECTION_TEST_TIMEOUT_MS;
|
|
@@ -276443,6 +276941,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276443
276941
|
exports.DECLARED_INTEGRATION_FIXED_KEY = DECLARED_INTEGRATION_FIXED_KEY;
|
|
276444
276942
|
exports.DEFAULT_ADDON_PLACEMENT = DEFAULT_ADDON_PLACEMENT;
|
|
276445
276943
|
exports.DEFAULT_AUDIO_ANALYZER_CONFIG = DEFAULT_AUDIO_ANALYZER_CONFIG;
|
|
276944
|
+
exports.DEFAULT_CLUSTER_STEP_MODELS = DEFAULT_CLUSTER_STEP_MODELS;
|
|
276446
276945
|
exports.DEFAULT_DECODER_HWACCEL_CONFIG = DEFAULT_DECODER_HWACCEL_CONFIG;
|
|
276447
276946
|
exports.DEFAULT_DETAIL_CROP_CONVENTION = DEFAULT_DETAIL_CROP_CONVENTION;
|
|
276448
276947
|
exports.DEFAULT_EVENTS_BAND_BUFFER_SEC = DEFAULT_EVENTS_BAND_BUFFER_SEC;
|
|
@@ -276561,6 +277060,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276561
277060
|
exports.ExpressionLiteralBindingSchema = ExpressionLiteralBindingSchema;
|
|
276562
277061
|
exports.ExpressionParseError = ExpressionParseError;
|
|
276563
277062
|
exports.ExpressionSourceSchema = ExpressionSourceSchema;
|
|
277063
|
+
exports.FULL_IMAGE_BBOX = FULL_IMAGE_BBOX;
|
|
276564
277064
|
exports.FanControlStatusSchema = FanControlStatusSchema;
|
|
276565
277065
|
exports.FanDirectionSchema = FanDirectionSchema;
|
|
276566
277066
|
exports.FeatureManifestSchema = FeatureManifestSchema;
|
|
@@ -277180,6 +277680,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277180
277680
|
exports.audioAnalyzerCapability = audioAnalyzerCapability;
|
|
277181
277681
|
exports.audioCodecCapability = audioCodecCapability;
|
|
277182
277682
|
exports.audioIsFailClosed = audioIsFailClosed;
|
|
277683
|
+
exports.audioKindId = audioKindId;
|
|
277183
277684
|
exports.audioLabelChoices = audioLabelChoices;
|
|
277184
277685
|
exports.audioMetricsCapability = audioMetricsCapability;
|
|
277185
277686
|
exports.audioModeOf = audioModeOf;
|
|
@@ -277218,6 +277719,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277218
277719
|
exports.classifyStream = classifyStream;
|
|
277219
277720
|
exports.classifyStreams = classifyStreams;
|
|
277220
277721
|
exports.climateControlCapability = climateControlCapability;
|
|
277722
|
+
exports.clusterModelSettingKey = clusterModelSettingKey;
|
|
277221
277723
|
exports.collectHydratedFieldEntries = require_sleep.collectHydratedFieldEntries;
|
|
277222
277724
|
exports.collectHydratedFieldValues = require_sleep.collectHydratedFieldValues;
|
|
277223
277725
|
exports.colorCapability = colorCapability;
|
|
@@ -277336,6 +277838,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277336
277838
|
exports.isAudioRule = isAudioRule;
|
|
277337
277839
|
exports.isBaseConditionKey = isBaseConditionKey;
|
|
277338
277840
|
exports.isBatteryPresenceFault = isBatteryPresenceFault;
|
|
277841
|
+
exports.isClusterScopedStep = isClusterScopedStep;
|
|
277339
277842
|
exports.isCollectionArrayMethod = isCollectionArrayMethod;
|
|
277340
277843
|
exports.isDeployableToAgent = isDeployableToAgent;
|
|
277341
277844
|
exports.isDetectionMacroClass = isDetectionMacroClass;
|
|
@@ -277419,6 +277922,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277419
277922
|
exports.patchAudio = patchAudio;
|
|
277420
277923
|
exports.petFeederCapability = petFeederCapability;
|
|
277421
277924
|
exports.pickAccessoryControl = pickAccessoryControl;
|
|
277925
|
+
exports.pickClusterStepModels = pickClusterStepModels;
|
|
277422
277926
|
exports.pickDetailCropConvention = pickDetailCropConvention;
|
|
277423
277927
|
exports.pickNativeLeaseOverride = pickNativeLeaseOverride;
|
|
277424
277928
|
exports.pickPreferredRtspEntry = pickPreferredRtspEntry;
|
|
@@ -277442,6 +277946,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277442
277946
|
exports.ptzAutotrackCapability = ptzAutotrackCapability;
|
|
277443
277947
|
exports.ptzCapability = ptzCapability;
|
|
277444
277948
|
exports.pythonScriptForBackend = pythonScriptForBackend;
|
|
277949
|
+
exports.readClusterStepModels = readClusterStepModels;
|
|
277445
277950
|
exports.readDetailCropConvention = readDetailCropConvention;
|
|
277446
277951
|
exports.readDeviceStateFrom = readDeviceStateFrom;
|
|
277447
277952
|
exports.readNativeLeaseOverride = readNativeLeaseOverride;
|
|
@@ -277459,6 +277964,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277459
277964
|
exports.resolveAddonPlacement = resolveAddonPlacement;
|
|
277460
277965
|
exports.resolveAddonRuntime = resolveAddonRuntime;
|
|
277461
277966
|
exports.resolveCapMount = require_sleep.resolveCapMount;
|
|
277967
|
+
exports.resolveClusterStepModelId = resolveClusterStepModelId;
|
|
277462
277968
|
exports.resolveDetectionRuntime = resolveDetectionRuntime;
|
|
277463
277969
|
exports.resolveDeviceControlKind = resolveDeviceControlKind;
|
|
277464
277970
|
exports.resolveDeviceProfile = resolveDeviceProfile;
|
|
@@ -277768,22 +278274,20 @@ var require_update_availability_emitter = __commonJS({
|
|
|
277768
278274
|
this.persistIfDirty();
|
|
277769
278275
|
if (!latestChanged)
|
|
277770
278276
|
return;
|
|
277771
|
-
this.emitList(target);
|
|
278277
|
+
this.emitList(target, scope);
|
|
277772
278278
|
}
|
|
277773
278279
|
/** Publish a partial candidate set without treating omissions as up-to-date. */
|
|
277774
278280
|
publishCandidates(candidates) {
|
|
277775
|
-
|
|
277776
|
-
const targets = /* @__PURE__ */ new Set();
|
|
278281
|
+
const changed = /* @__PURE__ */ new Map();
|
|
277777
278282
|
for (const candidate of candidates) {
|
|
277778
|
-
|
|
277779
|
-
|
|
277780
|
-
|
|
278283
|
+
if (!this.note(candidate))
|
|
278284
|
+
continue;
|
|
278285
|
+
const scope = scopeOf(candidate);
|
|
278286
|
+
changed.set(`${candidate.target}:${scope}`, { target: candidate.target, scope });
|
|
277781
278287
|
}
|
|
277782
278288
|
this.persistIfDirty();
|
|
277783
|
-
|
|
277784
|
-
|
|
277785
|
-
for (const target of targets)
|
|
277786
|
-
this.emitList(target);
|
|
278289
|
+
for (const { target, scope } of changed.values())
|
|
278290
|
+
this.emitList(target, scope);
|
|
277787
278291
|
}
|
|
277788
278292
|
note(candidate) {
|
|
277789
278293
|
const key = this.key(candidate);
|
|
@@ -277809,20 +278313,20 @@ var require_update_availability_emitter = __commonJS({
|
|
|
277809
278313
|
held: Object.fromEntries(this.held)
|
|
277810
278314
|
});
|
|
277811
278315
|
}
|
|
277812
|
-
|
|
277813
|
-
|
|
278316
|
+
/** Announce ONE scope's full list. `scope` is the node id, `hub` for the hub. */
|
|
278317
|
+
emitList(target, scope) {
|
|
278318
|
+
const list = [...this.held.values()].filter((candidate) => candidate.target === target && scopeOf(candidate) === scope);
|
|
277814
278319
|
if (list.length === 0)
|
|
277815
278320
|
return;
|
|
277816
278321
|
const packages = list.map((candidate) => ({
|
|
277817
278322
|
packageName: candidate.packageName,
|
|
277818
278323
|
currentVersion: candidate.currentVersion,
|
|
277819
278324
|
latestVersion: candidate.latestVersion,
|
|
277820
|
-
|
|
278325
|
+
nodeId: scope
|
|
277821
278326
|
}));
|
|
277822
|
-
const nodeIds = unique(packages.map((pkg) => pkg.nodeId).filter((id) => id !== void 0));
|
|
277823
278327
|
const head = list[0];
|
|
277824
278328
|
this.eventBus.emit({
|
|
277825
|
-
id: `update.available:${target}:${packages.map((pkg) => `${pkg.
|
|
278329
|
+
id: `update.available:${target}:${scope}:${packages.map((pkg) => `${pkg.packageName}@${pkg.latestVersion}`).toSorted().join(",")}`,
|
|
277826
278330
|
timestamp: /* @__PURE__ */ new Date(),
|
|
277827
278331
|
source: this.source,
|
|
277828
278332
|
category: types_1.EventCategory.UpdateAvailable,
|
|
@@ -277832,24 +278336,24 @@ var require_update_availability_emitter = __commonJS({
|
|
|
277832
278336
|
currentVersion: head.currentVersion,
|
|
277833
278337
|
latestVersion: head.latestVersion,
|
|
277834
278338
|
packages,
|
|
277835
|
-
|
|
277836
|
-
|
|
278339
|
+
nodeId: scope,
|
|
278340
|
+
nodeIds: [scope]
|
|
277837
278341
|
}
|
|
277838
278342
|
});
|
|
277839
278343
|
}
|
|
277840
278344
|
key(candidate) {
|
|
277841
|
-
return `${candidate.target}:${candidate
|
|
278345
|
+
return `${candidate.target}:${scopeOf(candidate)}:${candidate.packageName}`;
|
|
277842
278346
|
}
|
|
277843
278347
|
};
|
|
277844
278348
|
exports.UpdateAvailabilityEmitter = UpdateAvailabilityEmitter;
|
|
278349
|
+
function scopeOf(candidate) {
|
|
278350
|
+
return candidate.nodeId ?? "hub";
|
|
278351
|
+
}
|
|
277845
278352
|
function stampNode(candidate, nodeId) {
|
|
277846
278353
|
if (nodeId === void 0 || candidate.nodeId !== void 0)
|
|
277847
278354
|
return candidate;
|
|
277848
278355
|
return { ...candidate, nodeId };
|
|
277849
278356
|
}
|
|
277850
|
-
function unique(values) {
|
|
277851
|
-
return [...new Set(values)];
|
|
277852
|
-
}
|
|
277853
278357
|
}
|
|
277854
278358
|
});
|
|
277855
278359
|
|
|
@@ -279204,7 +279708,13 @@ var require_server_root = __commonJS({
|
|
|
279204
279708
|
fromVersion: runningVersion
|
|
279205
279709
|
}
|
|
279206
279710
|
});
|
|
279207
|
-
this.restartServerFn(`${RESTART_REASON_PREFIX}: ${this.spec.packageName}@${target}
|
|
279711
|
+
this.restartServerFn(`${RESTART_REASON_PREFIX}: ${this.spec.packageName}@${target}`, {
|
|
279712
|
+
packageName: this.spec.packageName,
|
|
279713
|
+
...runningVersion !== null ? {
|
|
279714
|
+
fromVersion: runningVersion
|
|
279715
|
+
} : {},
|
|
279716
|
+
toVersion: target
|
|
279717
|
+
});
|
|
279208
279718
|
return {
|
|
279209
279719
|
accepted: true,
|
|
279210
279720
|
targetVersion: target,
|
|
@@ -279489,6 +279999,47 @@ var require_server_root = __commonJS({
|
|
|
279489
279999
|
}
|
|
279490
280000
|
});
|
|
279491
280001
|
|
|
280002
|
+
// ../../server/backend/dist/core/updates/restart-marker.js
|
|
280003
|
+
var require_restart_marker = __commonJS({
|
|
280004
|
+
"../../server/backend/dist/core/updates/restart-marker.js"(exports) {
|
|
280005
|
+
"use strict";
|
|
280006
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
280007
|
+
exports.buildRestartMarker = buildRestartMarker;
|
|
280008
|
+
exports.isServerUpdateReason = isServerUpdateReason;
|
|
280009
|
+
exports.versionFromReason = versionFromReason;
|
|
280010
|
+
var SERVER_UPDATE_PREFIX = "server-update:";
|
|
280011
|
+
var ROOT_PACKAGE_NAME = "@camstack/server";
|
|
280012
|
+
function buildRestartMarker(input) {
|
|
280013
|
+
const { requestedBy, update, requestedAt } = input;
|
|
280014
|
+
const toVersion = update?.toVersion ?? versionFromReason(requestedBy);
|
|
280015
|
+
const isUpdate = update !== void 0 || isServerUpdateReason(requestedBy) && toVersion !== void 0;
|
|
280016
|
+
if (!isUpdate) {
|
|
280017
|
+
return {
|
|
280018
|
+
kind: "manual",
|
|
280019
|
+
requestedAt,
|
|
280020
|
+
...requestedBy !== void 0 ? { requestedBy } : {}
|
|
280021
|
+
};
|
|
280022
|
+
}
|
|
280023
|
+
return {
|
|
280024
|
+
kind: "framework-update",
|
|
280025
|
+
requestedAt,
|
|
280026
|
+
...requestedBy !== void 0 ? { requestedBy } : {},
|
|
280027
|
+
packageName: update?.packageName ?? ROOT_PACKAGE_NAME,
|
|
280028
|
+
...update?.fromVersion !== void 0 ? { fromVersion: update.fromVersion } : {},
|
|
280029
|
+
...toVersion !== void 0 ? { toVersion } : {}
|
|
280030
|
+
};
|
|
280031
|
+
}
|
|
280032
|
+
function isServerUpdateReason(requestedBy) {
|
|
280033
|
+
return requestedBy !== void 0 && requestedBy.startsWith(SERVER_UPDATE_PREFIX);
|
|
280034
|
+
}
|
|
280035
|
+
function versionFromReason(requestedBy) {
|
|
280036
|
+
if (requestedBy === void 0)
|
|
280037
|
+
return void 0;
|
|
280038
|
+
return /@([^@\s]+)$/.exec(requestedBy)?.[1];
|
|
280039
|
+
}
|
|
280040
|
+
}
|
|
280041
|
+
});
|
|
280042
|
+
|
|
279492
280043
|
// ../../server/backend/dist/agent/agent-update-service.js
|
|
279493
280044
|
var require_agent_update_service = __commonJS({
|
|
279494
280045
|
"../../server/backend/dist/agent/agent-update-service.js"(exports) {
|
|
@@ -279546,6 +280097,7 @@ var require_agent_update_service = __commonJS({
|
|
|
279546
280097
|
var update_availability_emitter_js_1 = require_update_availability_emitter();
|
|
279547
280098
|
var update_availability_store_js_1 = require_update_availability_store();
|
|
279548
280099
|
var index_js_1 = require_server_root();
|
|
280100
|
+
var restart_marker_js_1 = require_restart_marker();
|
|
279549
280101
|
exports.AGENT_RUNTIME_ADDON_ID = "agent-runtime";
|
|
279550
280102
|
exports.AGENT_RESTART_GRACE_MS = 2e3;
|
|
279551
280103
|
function isRunningInContainer(existsSyncFn) {
|
|
@@ -279609,8 +280161,8 @@ var require_agent_update_service = __commonJS({
|
|
|
279609
280161
|
seedDir: "CAMSTACK_SEED_SERVER_DIR"
|
|
279610
280162
|
},
|
|
279611
280163
|
logger: options.logger,
|
|
279612
|
-
restartServer: (requestedBy) => {
|
|
279613
|
-
writeAgentRestartMarker(options.dataDir, requestedBy);
|
|
280164
|
+
restartServer: (requestedBy, update) => {
|
|
280165
|
+
writeAgentRestartMarker(options.dataDir, requestedBy, update);
|
|
279614
280166
|
restartAgent(requestedBy);
|
|
279615
280167
|
},
|
|
279616
280168
|
dataDir: options.dataDir,
|
|
@@ -279688,18 +280240,13 @@ var require_agent_update_service = __commonJS({
|
|
|
279688
280240
|
}
|
|
279689
280241
|
};
|
|
279690
280242
|
exports.AgentUpdateService = AgentUpdateService;
|
|
279691
|
-
function writeAgentRestartMarker(dataDir, requestedBy) {
|
|
279692
|
-
const toVersion = /@([^@\s]+)$/.exec(requestedBy)?.[1];
|
|
280243
|
+
function writeAgentRestartMarker(dataDir, requestedBy, update) {
|
|
279693
280244
|
try {
|
|
279694
|
-
(0, system_1.writePendingRestart)(dataDir, {
|
|
279695
|
-
kind: requestedBy.startsWith("server-update:") ? "framework-update" : "manual",
|
|
280245
|
+
(0, system_1.writePendingRestart)(dataDir, (0, restart_marker_js_1.buildRestartMarker)({
|
|
279696
280246
|
requestedAt: Date.now(),
|
|
279697
280247
|
requestedBy,
|
|
279698
|
-
...
|
|
279699
|
-
|
|
279700
|
-
...toVersion !== void 0 ? { toVersion } : {}
|
|
279701
|
-
} : {}
|
|
279702
|
-
});
|
|
280248
|
+
...update !== void 0 ? { update } : {}
|
|
280249
|
+
}));
|
|
279703
280250
|
} catch {
|
|
279704
280251
|
}
|
|
279705
280252
|
}
|
|
@@ -388570,6 +389117,7 @@ var require_addon_package_service = __commonJS({
|
|
|
388570
389117
|
var types_1 = require_dist9();
|
|
388571
389118
|
var update_availability_emitter_js_1 = require_update_availability_emitter();
|
|
388572
389119
|
var update_check_scheduler_js_1 = require_update_check_scheduler();
|
|
389120
|
+
var restart_marker_js_1 = require_restart_marker();
|
|
388573
389121
|
var package_dir_utils_js_1 = require_package_dir_utils();
|
|
388574
389122
|
var execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
388575
389123
|
exports.SYSTEM_PACKAGE = "@camstack/system";
|
|
@@ -389497,26 +390045,24 @@ var require_addon_package_service = __commonJS({
|
|
|
389497
390045
|
/**
|
|
389498
390046
|
* Gracefully restart the server process.
|
|
389499
390047
|
*
|
|
389500
|
-
* Writes
|
|
389501
|
-
*
|
|
389502
|
-
*
|
|
390048
|
+
* Writes the restart marker so the post-boot service can emit
|
|
390049
|
+
* `system.restart-completed` on the next boot (powers the admin-UI
|
|
390050
|
+
* "Server restarted" toast AND the operator's "Server aggiornato"
|
|
390051
|
+
* notification). `update` is passed by `RootUpdateService` for an
|
|
390052
|
+
* update-driven restart and is the ONLY source of the version being left
|
|
390053
|
+
* behind — see `updates/restart-marker.ts`. Emits `system.restarting`
|
|
389503
390054
|
* with the same payload so subscribers can show a reconnect overlay
|
|
389504
390055
|
* immediately. Then delegates to `scheduleSelfRestart` which picks
|
|
389505
390056
|
* the right exit path (Electron `app.relaunch` vs supervisor-driven
|
|
389506
390057
|
* `process.exit`).
|
|
389507
390058
|
*/
|
|
389508
|
-
restartServer(requestedBy) {
|
|
390059
|
+
restartServer(requestedBy, update) {
|
|
389509
390060
|
this.logger.info("Server restart requested -- initiating graceful shutdown");
|
|
389510
|
-
const
|
|
389511
|
-
const payload = {
|
|
389512
|
-
kind: isServerUpdateRequest(requestedBy) ? "framework-update" : "manual",
|
|
390061
|
+
const payload = (0, restart_marker_js_1.buildRestartMarker)({
|
|
389513
390062
|
requestedAt: Date.now(),
|
|
389514
390063
|
...requestedBy !== void 0 ? { requestedBy } : {},
|
|
389515
|
-
...
|
|
389516
|
-
|
|
389517
|
-
...toVersion !== void 0 ? { toVersion } : {}
|
|
389518
|
-
} : {}
|
|
389519
|
-
};
|
|
390064
|
+
...update !== void 0 ? { update } : {}
|
|
390065
|
+
});
|
|
389520
390066
|
try {
|
|
389521
390067
|
(0, system_1.writePendingRestart)(this.resolveDataDir(), payload);
|
|
389522
390068
|
} catch (err) {
|
|
@@ -390007,22 +390553,23 @@ var require_addon_package_service = __commonJS({
|
|
|
390007
390553
|
* re-announces them on the next good poll.
|
|
390008
390554
|
*/
|
|
390009
390555
|
publishAddonAvailability(updates, failures, nodeId) {
|
|
390556
|
+
const scope = nodeId ?? this.resolveNodeId();
|
|
390010
390557
|
const candidates = updates.map((update) => ({
|
|
390011
390558
|
target: "addon",
|
|
390012
390559
|
packageName: update.name,
|
|
390013
390560
|
currentVersion: update.currentVersion,
|
|
390014
390561
|
latestVersion: update.latestVersion,
|
|
390015
|
-
|
|
390562
|
+
nodeId: scope
|
|
390016
390563
|
}));
|
|
390017
390564
|
if (failures > 0) {
|
|
390018
390565
|
this.logger.warn("Addon update check incomplete \u2014 availability published as partial", {
|
|
390019
|
-
tags: { nodeId:
|
|
390566
|
+
tags: { nodeId: scope },
|
|
390020
390567
|
meta: { failures, published: candidates.length }
|
|
390021
390568
|
});
|
|
390022
390569
|
this.updateAvailability.publishCandidates(candidates);
|
|
390023
390570
|
return;
|
|
390024
390571
|
}
|
|
390025
|
-
this.updateAvailability.publishSnapshot("addon", candidates,
|
|
390572
|
+
this.updateAvailability.publishSnapshot("addon", candidates, scope);
|
|
390026
390573
|
}
|
|
390027
390574
|
/** Fetch npm search results for camstack addon packages (cached 5 min) */
|
|
390028
390575
|
async fetchSearchFromNpm() {
|
|
@@ -390172,15 +390719,6 @@ var require_addon_package_service = __commonJS({
|
|
|
390172
390719
|
}
|
|
390173
390720
|
};
|
|
390174
390721
|
exports.AddonPackageService = AddonPackageService;
|
|
390175
|
-
function isServerUpdateRequest(requestedBy) {
|
|
390176
|
-
return requestedBy !== void 0 && requestedBy.startsWith("server-update:");
|
|
390177
|
-
}
|
|
390178
|
-
function toVersionFromRestartReason(requestedBy) {
|
|
390179
|
-
if (requestedBy === void 0)
|
|
390180
|
-
return void 0;
|
|
390181
|
-
const match = /@([^@\s]+)$/.exec(requestedBy);
|
|
390182
|
-
return match?.[1];
|
|
390183
|
-
}
|
|
390184
390722
|
async function extractTgzStripped(tgz, destDir) {
|
|
390185
390723
|
fs.mkdirSync(destDir, { recursive: true });
|
|
390186
390724
|
const tmpFile = path.join(os.tmpdir(), `camstack-tgz-${(0, node_crypto_1.randomUUID)()}.tgz`);
|
|
@@ -401689,14 +402227,6 @@ var require_integration_visibility = __commonJS({
|
|
|
401689
402227
|
function createFilteredIntegrationRegistry(deps) {
|
|
401690
402228
|
const { raw, logger } = deps;
|
|
401691
402229
|
const isInstalled = (addonId) => deps.isAddonInstalled((0, types_1.bareAddonId)(addonId));
|
|
401692
|
-
let activeIntegrationIds = null;
|
|
401693
|
-
const ensureActiveIds = async () => {
|
|
401694
|
-
if (activeIntegrationIds)
|
|
401695
|
-
return activeIntegrationIds;
|
|
401696
|
-
const all = await raw.listIntegrations();
|
|
401697
|
-
activeIntegrationIds = new Set(all.filter((i) => isInstalled(i.addonId)).map((i) => i.id));
|
|
401698
|
-
return activeIntegrationIds;
|
|
401699
|
-
};
|
|
401700
402230
|
const logHidden = (integration, phase) => {
|
|
401701
402231
|
logger.warn("integration hidden \u2014 its addon is not installed, so this read answers null even though the row exists", {
|
|
401702
402232
|
tags: { integrationId: integration.id, addonId: integration.addonId },
|
|
@@ -401742,39 +402272,7 @@ var require_integration_visibility = __commonJS({
|
|
|
401742
402272
|
// Integration settings: passthrough (already gated by getIntegration)
|
|
401743
402273
|
getIntegrationSettings: (id) => raw.getIntegrationSettings(id),
|
|
401744
402274
|
setIntegrationSetting: (id, key, value) => raw.setIntegrationSetting(id, key, value),
|
|
401745
|
-
setIntegrationSettings: (id, settings) => raw.setIntegrationSettings(id, settings)
|
|
401746
|
-
// Devices: filter out devices belonging to hidden integrations
|
|
401747
|
-
createDevice: (input) => raw.createDevice(input),
|
|
401748
|
-
getDevice: async (id) => {
|
|
401749
|
-
const d = await raw.getDevice(id);
|
|
401750
|
-
if (!d)
|
|
401751
|
-
return null;
|
|
401752
|
-
const ids = await ensureActiveIds();
|
|
401753
|
-
return ids.has(d.integrationId) ? d : null;
|
|
401754
|
-
},
|
|
401755
|
-
getDeviceByStableId: async (stableId) => {
|
|
401756
|
-
const d = await raw.getDeviceByStableId(stableId);
|
|
401757
|
-
if (!d)
|
|
401758
|
-
return null;
|
|
401759
|
-
const ids = await ensureActiveIds();
|
|
401760
|
-
return ids.has(d.integrationId) ? d : null;
|
|
401761
|
-
},
|
|
401762
|
-
listDevices: async (integrationId) => {
|
|
401763
|
-
const devices = await raw.listDevices(integrationId);
|
|
401764
|
-
const ids = await ensureActiveIds();
|
|
401765
|
-
return devices.filter((d) => ids.has(d.integrationId));
|
|
401766
|
-
},
|
|
401767
|
-
listCameras: async () => {
|
|
401768
|
-
const cameras = await raw.listCameras();
|
|
401769
|
-
const ids = await ensureActiveIds();
|
|
401770
|
-
return cameras.filter((d) => ids.has(d.integrationId));
|
|
401771
|
-
},
|
|
401772
|
-
updateDevice: (id, updates) => raw.updateDevice(id, updates),
|
|
401773
|
-
deleteDevice: (id) => raw.deleteDevice(id),
|
|
401774
|
-
// Device settings: passthrough
|
|
401775
|
-
getDeviceSettings: (id) => raw.getDeviceSettings(id),
|
|
401776
|
-
setDeviceSetting: (id, key, value) => raw.setDeviceSetting(id, key, value),
|
|
401777
|
-
setDeviceSettings: (id, settings) => raw.setDeviceSettings(id, settings)
|
|
402275
|
+
setIntegrationSettings: (id, settings) => raw.setIntegrationSettings(id, settings)
|
|
401778
402276
|
};
|
|
401779
402277
|
}
|
|
401780
402278
|
}
|
|
@@ -406355,6 +406853,7 @@ var require_moleculer_service = __commonJS({
|
|
|
406355
406853
|
"use strict";
|
|
406356
406854
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
406357
406855
|
exports.MoleculerService = void 0;
|
|
406856
|
+
exports.childOwnerToken = childOwnerToken;
|
|
406358
406857
|
exports.buildChildUdsManifest = buildChildUdsManifest;
|
|
406359
406858
|
var node_crypto_1 = __require("crypto");
|
|
406360
406859
|
var system_1 = require_dist3();
|
|
@@ -406381,6 +406880,21 @@ var require_moleculer_service = __commonJS({
|
|
|
406381
406880
|
* `$hub.registerNode`. Populated by `onRegisterNode` in `hubDeps`.
|
|
406382
406881
|
*/
|
|
406383
406882
|
nodeRegistry = new system_1.HubNodeRegistry();
|
|
406883
|
+
/**
|
|
406884
|
+
* nodeId → the owner token of the INCARNATION whose manifest currently backs
|
|
406885
|
+
* that node's capability registrations.
|
|
406886
|
+
*
|
|
406887
|
+
* A node id is not unique over time: `resolveRunnerId` is deterministic, so a
|
|
406888
|
+
* runner replaced by an addon update reconnects as the same `hub/<runner>`
|
|
406889
|
+
* node. Without an incarnation the predecessor's teardown and the successor's
|
|
406890
|
+
* registration are the same `(cap, addonId)` key, and the teardown wins by
|
|
406891
|
+
* arriving last — `recording`, `storage-evictable` and `recording-export`
|
|
406892
|
+
* were unregistered from a healthy process for 7 hours on 2026-08-20.
|
|
406893
|
+
* Populated only for transports that can observe an incarnation (UDS
|
|
406894
|
+
* children); remote nodes stay ownerless and behave exactly as before.
|
|
406895
|
+
* See `docs/decisions/adr-0188-an-unregister-carries-proof-of-ownership.md`.
|
|
406896
|
+
*/
|
|
406897
|
+
nodeOwners = /* @__PURE__ */ new Map();
|
|
406384
406898
|
/**
|
|
406385
406899
|
* Fixed-period agent-readiness snapshot sweep (D8 reconcile) — repairs
|
|
406386
406900
|
* agent-origin readiness deltas lost while the agent stayed connected.
|
|
@@ -406702,14 +407216,18 @@ var require_moleculer_service = __commonJS({
|
|
|
406702
407216
|
const hubNodeId = this.brokerSafe.nodeID;
|
|
406703
407217
|
const childNodeId = `${hubNodeId}/${child.childId}`;
|
|
406704
407218
|
const params = buildChildUdsManifest(childNodeId, child.childId, child.caps);
|
|
406705
|
-
this.onRegisterNode(params);
|
|
406706
|
-
logger.info("UDS child registered \u2014 manifest applied", {
|
|
407219
|
+
this.onRegisterNode(params, childOwnerToken(childNodeId, child.incarnation));
|
|
407220
|
+
logger.info("UDS child registered \u2014 manifest applied", {
|
|
407221
|
+
meta: { nodeId: childNodeId, incarnation: child.incarnation }
|
|
407222
|
+
});
|
|
406707
407223
|
});
|
|
406708
|
-
registry.onChildGone((childId) => {
|
|
407224
|
+
registry.onChildGone((childId, incarnation) => {
|
|
406709
407225
|
const hubNodeId = this.brokerSafe.nodeID;
|
|
406710
407226
|
const childNodeId = `${hubNodeId}/${childId}`;
|
|
406711
|
-
logger.info("UDS child gone \u2014 removing from registry", {
|
|
406712
|
-
|
|
407227
|
+
logger.info("UDS child gone \u2014 removing from registry", {
|
|
407228
|
+
meta: { childId, incarnation }
|
|
407229
|
+
});
|
|
407230
|
+
this.removeNodeFromRegistry(childNodeId, childOwnerToken(childNodeId, incarnation));
|
|
406713
407231
|
});
|
|
406714
407232
|
registry.onChildLog((childId, entry) => {
|
|
406715
407233
|
this.logging.writeFromWorker((0, system_1.udsChildLogToWorkerEntry)(childId, entry));
|
|
@@ -406893,10 +407411,15 @@ var require_moleculer_service = __commonJS({
|
|
|
406893
407411
|
* CapabilityRegistry update is a diff (atomic replace) rather than an
|
|
406894
407412
|
* unconditional re-register — see `applyNodeManifest` for the rationale.
|
|
406895
407413
|
*/
|
|
406896
|
-
onRegisterNode(params) {
|
|
407414
|
+
onRegisterNode(params, owner) {
|
|
406897
407415
|
const previousManifest = this.nodeRegistry.getNodeManifest(params.nodeId);
|
|
407416
|
+
const previousOwner = this.nodeOwners.get(params.nodeId);
|
|
406898
407417
|
this.nodeRegistry.registerNode(params);
|
|
406899
|
-
|
|
407418
|
+
if (owner === void 0)
|
|
407419
|
+
this.nodeOwners.delete(params.nodeId);
|
|
407420
|
+
else
|
|
407421
|
+
this.nodeOwners.set(params.nodeId, owner);
|
|
407422
|
+
this.applyNodeManifest(params, previousManifest, owner, previousOwner);
|
|
406900
407423
|
const { nodeId } = params;
|
|
406901
407424
|
if (nodeId !== "hub" && !nodeId.includes("/") && this.onAgentRegisteredCb) {
|
|
406902
407425
|
this.onAgentRegisteredCb(nodeId);
|
|
@@ -406924,7 +407447,7 @@ var require_moleculer_service = __commonJS({
|
|
|
406924
407447
|
* already; this method handles only `params.addons` (system caps).
|
|
406925
407448
|
* Native-cap wiring into device-manager is done in a later task.
|
|
406926
407449
|
*/
|
|
406927
|
-
applyNodeManifest(params, previousManifest) {
|
|
407450
|
+
applyNodeManifest(params, previousManifest, owner, previousOwner) {
|
|
406928
407451
|
const { nodeId, addons } = params;
|
|
406929
407452
|
const hubNodeId = this.brokerSafe.nodeID;
|
|
406930
407453
|
const isLocalChild = nodeId.startsWith(hubNodeId + "/");
|
|
@@ -406955,9 +407478,16 @@ var require_moleculer_service = __commonJS({
|
|
|
406955
407478
|
for (const [key, { addonId, capName }] of previous) {
|
|
406956
407479
|
if (desired.has(key))
|
|
406957
407480
|
continue;
|
|
406958
|
-
registry.unregisterProvider(capName, registryKeyFor(addonId));
|
|
407481
|
+
registry.unregisterProvider(capName, registryKeyFor(addonId), previousOwner);
|
|
406959
407482
|
this.nodeCallFns.delete(`${nodeId}::${capName}`);
|
|
406960
407483
|
}
|
|
407484
|
+
if (owner !== void 0 && previousOwner !== void 0 && owner !== previousOwner) {
|
|
407485
|
+
for (const [key, { addonId, capName }] of desired) {
|
|
407486
|
+
if (!previous.has(key))
|
|
407487
|
+
continue;
|
|
407488
|
+
registry.reassignProviderOwner(capName, registryKeyFor(addonId), owner);
|
|
407489
|
+
}
|
|
407490
|
+
}
|
|
406961
407491
|
for (const [key, { addonId, capName, capDef }] of desired) {
|
|
406962
407492
|
if (previous.has(key))
|
|
406963
407493
|
continue;
|
|
@@ -406982,7 +407512,7 @@ var require_moleculer_service = __commonJS({
|
|
|
406982
407512
|
for (const methodName of Object.keys((0, system_1.expandCapMethods)(capDef))) {
|
|
406983
407513
|
proxy[methodName] = (methodParams) => callFn(methodName, methodParams);
|
|
406984
407514
|
}
|
|
406985
|
-
registry.registerProvider(capName, registryKey, proxy);
|
|
407515
|
+
registry.registerProvider(capName, registryKey, proxy, owner);
|
|
406986
407516
|
if (capDef.mode === "singleton" && isLocalChild) {
|
|
406987
407517
|
const activeKey = registry.getSingletonAddonId(capName);
|
|
406988
407518
|
if (activeKey === null || activeKey.includes("@")) {
|
|
@@ -407015,7 +407545,14 @@ var require_moleculer_service = __commonJS({
|
|
|
407015
407545
|
* Unregisters every cap the node's last manifest declared and emits
|
|
407016
407546
|
* synthetic readiness-down events for each.
|
|
407017
407547
|
*/
|
|
407018
|
-
removeNodeFromRegistry(nodeId) {
|
|
407548
|
+
removeNodeFromRegistry(nodeId, owner) {
|
|
407549
|
+
const currentOwner = this.nodeOwners.get(nodeId);
|
|
407550
|
+
if (currentOwner !== void 0 && currentOwner !== owner) {
|
|
407551
|
+
this.logger.info("Node teardown ignored \u2014 node id reclaimed by a newer incarnation", {
|
|
407552
|
+
meta: { nodeId, currentOwner, presentedOwner: owner ?? null }
|
|
407553
|
+
});
|
|
407554
|
+
return;
|
|
407555
|
+
}
|
|
407019
407556
|
const manifest = this.nodeRegistry.getNodeManifest(nodeId);
|
|
407020
407557
|
if (!manifest)
|
|
407021
407558
|
return;
|
|
@@ -407029,7 +407566,7 @@ var require_moleculer_service = __commonJS({
|
|
|
407029
407566
|
const registryKey = isLocalChild ? addonId : `${addonId}@${nodeId}`;
|
|
407030
407567
|
if (registry) {
|
|
407031
407568
|
for (const capName of capabilities) {
|
|
407032
|
-
registry.unregisterProvider(capName, registryKey);
|
|
407569
|
+
registry.unregisterProvider(capName, registryKey, owner);
|
|
407033
407570
|
}
|
|
407034
407571
|
}
|
|
407035
407572
|
for (const capName of capabilities) {
|
|
@@ -407050,6 +407587,7 @@ var require_moleculer_service = __commonJS({
|
|
|
407050
407587
|
}
|
|
407051
407588
|
}
|
|
407052
407589
|
}
|
|
407590
|
+
this.nodeOwners.delete(nodeId);
|
|
407053
407591
|
this.nodeRegistry.removeNode(nodeId);
|
|
407054
407592
|
}
|
|
407055
407593
|
findCallFn(nodeId, capabilityName) {
|
|
@@ -407213,6 +407751,9 @@ var require_moleculer_service = __commonJS({
|
|
|
407213
407751
|
}
|
|
407214
407752
|
};
|
|
407215
407753
|
exports.MoleculerService = MoleculerService;
|
|
407754
|
+
function childOwnerToken(childNodeId, incarnation) {
|
|
407755
|
+
return `${childNodeId}#${incarnation}`;
|
|
407756
|
+
}
|
|
407216
407757
|
function buildChildUdsManifest(nodeId, childId, caps) {
|
|
407217
407758
|
const capsByAddon = /* @__PURE__ */ new Map();
|
|
407218
407759
|
for (const cap of caps) {
|
|
@@ -408573,7 +409114,7 @@ var require_manual_boot = __commonJS({
|
|
|
408573
409114
|
const replEngineService = new repl_engine_service_1.ReplEngineService(addonRegistryService, eventBusService, loggingService);
|
|
408574
409115
|
const serverUpdateService = new server_update_service_1.ServerUpdateService({
|
|
408575
409116
|
logger: loggingService.createLogger("ServerUpdate"),
|
|
408576
|
-
restartServer: (requestedBy) => addonPackageService.restartServer(requestedBy),
|
|
409117
|
+
restartServer: (requestedBy, update) => addonPackageService.restartServer(requestedBy, update),
|
|
408577
409118
|
eventBus: eventBusService,
|
|
408578
409119
|
updateAvailabilityStore: new update_availability_store_js_1.FileUpdateAvailabilityStore(availabilityDataDir, "server-update", loggingService.createLogger("UpdateAvailability"))
|
|
408579
409120
|
});
|