camstack 1.2.34 → 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,8 +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
|
|
60577
|
-
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);
|
|
60578
61068
|
if (pctx.registry) {
|
|
60579
61069
|
const liveEntries = addonId ? pctx.registry.getAllForAddon(addonId).map((device) => ({
|
|
60580
61070
|
addonId,
|
|
@@ -60582,7 +61072,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
60582
61072
|
})) : pctx.registry.getAllWithAddonId();
|
|
60583
61073
|
for (const { addonId: aid, device } of liveEntries) {
|
|
60584
61074
|
const key = String(device.id);
|
|
60585
|
-
const
|
|
61075
|
+
const row = rowById.get(device.id);
|
|
61076
|
+
const info = toDeviceInfo(aid, device, row?.metadata ?? null, row?.meta ?? null);
|
|
60586
61077
|
seen.add(key);
|
|
60587
61078
|
if (camerasOnly && !info.isCamera) continue;
|
|
60588
61079
|
results.push(slim ? {
|
|
@@ -60592,39 +61083,36 @@ var require_device_manager_addon = __commonJS({
|
|
|
60592
61083
|
} : info);
|
|
60593
61084
|
}
|
|
60594
61085
|
}
|
|
60595
|
-
const
|
|
60596
|
-
|
|
60597
|
-
|
|
60598
|
-
|
|
60599
|
-
|
|
60600
|
-
const m = metaByAddonStable.get(`${aid}\0${stableId}`);
|
|
60601
|
-
const key = String(m.id);
|
|
60602
|
-
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;
|
|
60603
61091
|
const persistedType = m.type;
|
|
60604
61092
|
if (camerasOnly && persistedType !== require_dist10.DeviceType.Camera) continue;
|
|
60605
61093
|
const persistedConfig = slim ? {} : await pctx.settings.readDeviceStore(m.id);
|
|
60606
|
-
const metadata = slim ? null :
|
|
61094
|
+
const metadata = slim ? null : row.metadata;
|
|
60607
61095
|
results.push({
|
|
60608
61096
|
id: m.id,
|
|
60609
61097
|
stableId,
|
|
60610
61098
|
addonId: aid,
|
|
60611
61099
|
type: persistedType,
|
|
60612
|
-
name: m
|
|
60613
|
-
location: m
|
|
60614
|
-
disabled: m
|
|
60615
|
-
parentDeviceId: m
|
|
60616
|
-
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),
|
|
60617
61105
|
online: pctx.host.resolveDeviceOnline(m.id, pctx.registry !== null),
|
|
60618
61106
|
probed: pctx.host.resolveDeviceProbed(m.id),
|
|
60619
|
-
features: persistedFeatures(m
|
|
61107
|
+
features: persistedFeatures(m.features),
|
|
60620
61108
|
isCamera: persistedType === require_dist10.DeviceType.Camera,
|
|
60621
61109
|
config: persistedConfig ?? {},
|
|
60622
61110
|
metadata,
|
|
60623
|
-
...m
|
|
60624
|
-
...m
|
|
60625
|
-
...m
|
|
60626
|
-
...m
|
|
60627
|
-
...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 } : {},
|
|
60628
61116
|
...(() => {
|
|
60629
61117
|
if (slim) return {};
|
|
60630
61118
|
const si = buildSourceInfoFromConfig(persistedConfig ?? {}, stableId, aid);
|
|
@@ -60639,19 +61127,15 @@ var require_device_manager_addon = __commonJS({
|
|
|
60639
61127
|
if (pctx.registry) {
|
|
60640
61128
|
const found = resolveDeviceById(pctx.registry, deviceId);
|
|
60641
61129
|
if (found) {
|
|
60642
|
-
const
|
|
60643
|
-
|
|
60644
|
-
const metadata2 = map[key2] ?? null;
|
|
60645
|
-
const metaRow = metaMap[key2] ?? null;
|
|
60646
|
-
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);
|
|
60647
61132
|
}
|
|
60648
61133
|
}
|
|
60649
|
-
const
|
|
60650
|
-
if (
|
|
60651
|
-
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;
|
|
60652
61138
|
const persistedConfig = await pctx.settings.readDeviceStore(m.id);
|
|
60653
|
-
const key = String(deviceId);
|
|
60654
|
-
const metadata = (await pctx.metaStore.readMetadataMap())[key] ?? null;
|
|
60655
61139
|
const sourceInfoGetDevice = buildSourceInfoFromConfig(persistedConfig ?? {}, stableId, aid);
|
|
60656
61140
|
return {
|
|
60657
61141
|
id: deviceId,
|
|
@@ -60690,30 +61174,25 @@ var require_device_manager_addon = __commonJS({
|
|
|
60690
61174
|
}
|
|
60691
61175
|
const results = [];
|
|
60692
61176
|
const seen = /* @__PURE__ */ new Set();
|
|
60693
|
-
const
|
|
60694
|
-
|
|
60695
|
-
|
|
60696
|
-
pctx.metaStore.readMetadataMap()
|
|
60697
|
-
]);
|
|
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);
|
|
60698
61180
|
if (pctx.registry) {
|
|
60699
61181
|
const liveChildren = pctx.registry.getChildren(parentDeviceId);
|
|
60700
61182
|
for (const device of liveChildren) {
|
|
60701
61183
|
const key = String(device.id);
|
|
60702
|
-
const
|
|
60703
|
-
|
|
60704
|
-
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));
|
|
60705
61186
|
seen.add(key);
|
|
60706
61187
|
}
|
|
60707
61188
|
}
|
|
60708
|
-
const
|
|
60709
|
-
|
|
60710
|
-
|
|
60711
|
-
for (const childStableId of persistedChildren) {
|
|
60712
|
-
const m = ownerMetaByStableId.get(childStableId);
|
|
61189
|
+
for (const row of childRows) {
|
|
61190
|
+
const m = row.meta;
|
|
61191
|
+
const childStableId = m.stableId;
|
|
60713
61192
|
const key = String(m.id);
|
|
60714
61193
|
if (seen.has(key)) continue;
|
|
60715
61194
|
const persistedConfig = await pctx.settings.readDeviceStore(m.id);
|
|
60716
|
-
const metadata =
|
|
61195
|
+
const metadata = row.metadata;
|
|
60717
61196
|
const sourceInfoChild = buildSourceInfoFromConfig(persistedConfig ?? {}, childStableId, ownerAddonId);
|
|
60718
61197
|
results.push({
|
|
60719
61198
|
id: m.id,
|
|
@@ -60857,16 +61336,10 @@ var require_device_manager_addon = __commonJS({
|
|
|
60857
61336
|
}
|
|
60858
61337
|
async function removeByIntegration(pctx, input) {
|
|
60859
61338
|
const { integrationId } = input;
|
|
60860
|
-
const
|
|
60861
|
-
const parentKeys = Object.keys(meta).filter((key) => {
|
|
60862
|
-
const m = meta[key];
|
|
60863
|
-
return m !== void 0 && m.integrationId === integrationId && m.parentDeviceId === null;
|
|
60864
|
-
});
|
|
61339
|
+
const parents = (await pctx.metaStore.rows.listByIntegration(integrationId)).filter((row) => row.meta.parentDeviceId === null);
|
|
60865
61340
|
let removed = 0;
|
|
60866
|
-
for (const
|
|
60867
|
-
|
|
60868
|
-
if (!m) continue;
|
|
60869
|
-
await pctx.provider.remove({ deviceId: m.id });
|
|
61341
|
+
for (const parent of parents) {
|
|
61342
|
+
await pctx.provider.remove({ deviceId: parent.meta.id });
|
|
60870
61343
|
removed++;
|
|
60871
61344
|
}
|
|
60872
61345
|
return { removed };
|
|
@@ -61255,204 +61728,6 @@ var require_device_manager_addon = __commonJS({
|
|
|
61255
61728
|
driverPatch
|
|
61256
61729
|
};
|
|
61257
61730
|
}
|
|
61258
|
-
function wireRemoteNativeCapSync(ctx, remoteNativeCaps) {
|
|
61259
|
-
const localNodeId = ctx.kernel.localNodeId ?? "hub";
|
|
61260
|
-
ctx.eventBus.subscribe({ category: require_dist10.EventCategory.DeviceBindingsChanged }, (event) => {
|
|
61261
|
-
const { deviceId, capName, reason, addonId, nodeId } = event.data;
|
|
61262
|
-
if (nodeId === localNodeId) return;
|
|
61263
|
-
if (reason === "native-registered") {
|
|
61264
|
-
let perDevice = remoteNativeCaps.get(deviceId);
|
|
61265
|
-
if (!perDevice) {
|
|
61266
|
-
perDevice = /* @__PURE__ */ new Map();
|
|
61267
|
-
remoteNativeCaps.set(deviceId, perDevice);
|
|
61268
|
-
}
|
|
61269
|
-
perDevice.set(capName, {
|
|
61270
|
-
addonId,
|
|
61271
|
-
nodeId
|
|
61272
|
-
});
|
|
61273
|
-
} else if (reason === "native-unregistered") {
|
|
61274
|
-
const perDevice = remoteNativeCaps.get(deviceId);
|
|
61275
|
-
if (!perDevice) return;
|
|
61276
|
-
perDevice.delete(capName);
|
|
61277
|
-
if (perDevice.size === 0) remoteNativeCaps.delete(deviceId);
|
|
61278
|
-
}
|
|
61279
|
-
});
|
|
61280
|
-
const cluster = ctx.kernel.cluster;
|
|
61281
|
-
if (cluster) cluster.broker.localBus.on("$node.disconnected", (payload) => {
|
|
61282
|
-
const gone = payload.node.id;
|
|
61283
|
-
const emptyDevices = [];
|
|
61284
|
-
for (const [deviceId, perDevice] of remoteNativeCaps) {
|
|
61285
|
-
const toDelete = [];
|
|
61286
|
-
for (const [capName, entry] of perDevice) if (entry.nodeId === gone) toDelete.push(capName);
|
|
61287
|
-
for (const capName of toDelete) perDevice.delete(capName);
|
|
61288
|
-
if (perDevice.size === 0) emptyDevices.push(deviceId);
|
|
61289
|
-
}
|
|
61290
|
-
for (const deviceId of emptyDevices) remoteNativeCaps.delete(deviceId);
|
|
61291
|
-
});
|
|
61292
|
-
}
|
|
61293
|
-
async function readBindingsStore(deps) {
|
|
61294
|
-
return { deviceBindings: (await deps.ctx.settings.readAddonStore()).deviceBindings ?? {} };
|
|
61295
|
-
}
|
|
61296
|
-
async function writeBindingsStore(deps, next) {
|
|
61297
|
-
await deps.ctx.settings.writeAddonStore({ deviceBindings: next.deviceBindings });
|
|
61298
|
-
}
|
|
61299
|
-
function resolveWrapperNodeId(_wrapperAddonId) {
|
|
61300
|
-
return "hub";
|
|
61301
|
-
}
|
|
61302
|
-
function toRoutableProviderNodeId(nodeId) {
|
|
61303
|
-
const slash = nodeId.indexOf("/");
|
|
61304
|
-
return slash === -1 ? nodeId : nodeId.slice(0, slash);
|
|
61305
|
-
}
|
|
61306
|
-
function resolveRemoteNativeCapFromRegistry(deps, capName, deviceId) {
|
|
61307
|
-
const clusterCaps = deps.ctx.kernel.listClusterNativeCapsForDevice?.(deviceId) ?? deps.ctx.kernel.listClusterNativeCaps?.();
|
|
61308
|
-
if (!clusterCaps) return null;
|
|
61309
|
-
for (const entry of clusterCaps) if (entry.capName === capName && entry.deviceId === deviceId && entry.addonId) return {
|
|
61310
|
-
addonId: entry.addonId,
|
|
61311
|
-
nodeId: entry.nodeId
|
|
61312
|
-
};
|
|
61313
|
-
return null;
|
|
61314
|
-
}
|
|
61315
|
-
function resolveDeviceType(deps, rawStore, deviceId) {
|
|
61316
|
-
const persisted = rawStore.deviceMeta?.[String(deviceId)]?.type;
|
|
61317
|
-
if (typeof persisted === "string" && persisted.length > 0) return persisted;
|
|
61318
|
-
const live = deps.ctx.kernel?.deviceRegistry?.getById(deviceId)?.type;
|
|
61319
|
-
return typeof live === "string" && live.length > 0 ? live : void 0;
|
|
61320
|
-
}
|
|
61321
|
-
function resolveDevicePresence(deps, rawStore, deviceId) {
|
|
61322
|
-
if (deps.ctx.kernel?.deviceRegistry?.getById(deviceId)) return "present";
|
|
61323
|
-
const meta = rawStore.deviceMeta;
|
|
61324
|
-
if (meta && Object.keys(meta).length > 0) return meta[String(deviceId)] === void 0 ? "absent" : "present";
|
|
61325
|
-
return "unknown";
|
|
61326
|
-
}
|
|
61327
|
-
function capAppliesToDeviceType(def, deviceType) {
|
|
61328
|
-
if (deviceType === void 0) return true;
|
|
61329
|
-
const declared = def?.deviceTypes;
|
|
61330
|
-
if (!declared || declared.length === 0) return true;
|
|
61331
|
-
return declared.some((t) => t === deviceType);
|
|
61332
|
-
}
|
|
61333
|
-
async function getBindings(deps, input) {
|
|
61334
|
-
const storeKey = String(input.deviceId);
|
|
61335
|
-
const rawStore = await deps.ctx.settings.readAddonStore();
|
|
61336
|
-
const perDevice = (rawStore.deviceBindings ?? {})[storeKey] ?? {};
|
|
61337
|
-
const deviceType = resolveDeviceType(deps, rawStore, input.deviceId);
|
|
61338
|
-
const presence = resolveDevicePresence(deps, rawStore, input.deviceId);
|
|
61339
|
-
const entries = [];
|
|
61340
|
-
const seenCaps = /* @__PURE__ */ new Set();
|
|
61341
|
-
const resolveRemote = (capName) => deps.remoteNativeCaps.get(input.deviceId)?.get(capName) ?? resolveRemoteNativeCapFromRegistry(deps, capName, input.deviceId);
|
|
61342
|
-
for (const [capName, { wrapperAddonId }] of Object.entries(perDevice)) {
|
|
61343
|
-
const hubLocalNative = deps.capabilityRegistry?.getNativeAddonId(capName, input.deviceId) ?? null;
|
|
61344
|
-
const remoteNative = resolveRemote(capName);
|
|
61345
|
-
const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? "";
|
|
61346
|
-
const nativeNodeId = hubLocalNative ? deps.ctx.kernel.localNodeId ?? "hub" : remoteNative?.nodeId ?? deps.ctx.kernel.localNodeId ?? "hub";
|
|
61347
|
-
if (wrapperAddonId === null && !nativeAddonId) {
|
|
61348
|
-
seenCaps.add(capName);
|
|
61349
|
-
continue;
|
|
61350
|
-
}
|
|
61351
|
-
entries.push({
|
|
61352
|
-
capName,
|
|
61353
|
-
kind: wrapperAddonId ? "wrapped" : "native",
|
|
61354
|
-
providerAddonId: wrapperAddonId ?? nativeAddonId,
|
|
61355
|
-
providerNodeId: wrapperAddonId ? resolveWrapperNodeId(wrapperAddonId) : toRoutableProviderNodeId(nativeNodeId),
|
|
61356
|
-
nativeAddonId
|
|
61357
|
-
});
|
|
61358
|
-
seenCaps.add(capName);
|
|
61359
|
-
}
|
|
61360
|
-
if (presence === "absent") deps.ctx.logger.debug("bindings requested for absent device \u2014 returning none", { tags: { deviceId: input.deviceId } });
|
|
61361
|
-
else if (deps.capabilityRegistry) {
|
|
61362
|
-
const skippedForType = [];
|
|
61363
|
-
for (const capName of deps.capabilityRegistry.getCapsWithDefaultWrapper()) {
|
|
61364
|
-
if (seenCaps.has(capName)) continue;
|
|
61365
|
-
if (!capAppliesToDeviceType(deps.capabilityRegistry.getDefinition(capName), deviceType)) {
|
|
61366
|
-
skippedForType.push(capName);
|
|
61367
|
-
continue;
|
|
61368
|
-
}
|
|
61369
|
-
const defaultWrapperAddonId = deps.capabilityRegistry.getDefaultWrapperForCap(capName);
|
|
61370
|
-
if (!defaultWrapperAddonId) continue;
|
|
61371
|
-
const hubLocalNative = deps.capabilityRegistry.getNativeAddonId(capName, input.deviceId) ?? null;
|
|
61372
|
-
const remoteNative = resolveRemote(capName);
|
|
61373
|
-
const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? "";
|
|
61374
|
-
entries.push({
|
|
61375
|
-
capName,
|
|
61376
|
-
kind: "wrapped",
|
|
61377
|
-
providerAddonId: defaultWrapperAddonId,
|
|
61378
|
-
providerNodeId: resolveWrapperNodeId(defaultWrapperAddonId),
|
|
61379
|
-
nativeAddonId
|
|
61380
|
-
});
|
|
61381
|
-
seenCaps.add(capName);
|
|
61382
|
-
}
|
|
61383
|
-
if (skippedForType.length > 0) deps.ctx.logger.debug("getBindings: default wrappers skipped \u2014 deviceTypes mismatch", {
|
|
61384
|
-
tags: { deviceId: input.deviceId },
|
|
61385
|
-
meta: {
|
|
61386
|
-
deviceType,
|
|
61387
|
-
skipped: skippedForType
|
|
61388
|
-
}
|
|
61389
|
-
});
|
|
61390
|
-
}
|
|
61391
|
-
if (deps.capabilityRegistry) for (const capName of deps.capabilityRegistry.getNativeCapsForDevice(input.deviceId)) {
|
|
61392
|
-
if (seenCaps.has(capName)) continue;
|
|
61393
|
-
const nativeAddonId = deps.capabilityRegistry.getNativeAddonId(capName, input.deviceId) ?? "";
|
|
61394
|
-
entries.push({
|
|
61395
|
-
capName,
|
|
61396
|
-
kind: "native",
|
|
61397
|
-
providerAddonId: nativeAddonId,
|
|
61398
|
-
providerNodeId: deps.ctx.kernel.localNodeId ?? "hub",
|
|
61399
|
-
nativeAddonId
|
|
61400
|
-
});
|
|
61401
|
-
seenCaps.add(capName);
|
|
61402
|
-
}
|
|
61403
|
-
const pushFed = deps.remoteNativeCaps.get(input.deviceId);
|
|
61404
|
-
if (pushFed) for (const [capName, info] of pushFed) {
|
|
61405
|
-
if (seenCaps.has(capName)) continue;
|
|
61406
|
-
entries.push({
|
|
61407
|
-
capName,
|
|
61408
|
-
kind: "native",
|
|
61409
|
-
providerAddonId: info.addonId,
|
|
61410
|
-
providerNodeId: toRoutableProviderNodeId(info.nodeId),
|
|
61411
|
-
nativeAddonId: info.addonId
|
|
61412
|
-
});
|
|
61413
|
-
seenCaps.add(capName);
|
|
61414
|
-
}
|
|
61415
|
-
const clusterCaps = deps.ctx.kernel.listClusterNativeCapsForDevice?.(input.deviceId) ?? deps.ctx.kernel.listClusterNativeCaps?.();
|
|
61416
|
-
if (clusterCaps) for (const entry of clusterCaps) {
|
|
61417
|
-
if (entry.deviceId !== input.deviceId) continue;
|
|
61418
|
-
if (seenCaps.has(entry.capName)) continue;
|
|
61419
|
-
if (!entry.addonId) continue;
|
|
61420
|
-
const localNodeId = deps.ctx.kernel.localNodeId ?? "hub";
|
|
61421
|
-
if (entry.nodeId === localNodeId) continue;
|
|
61422
|
-
entries.push({
|
|
61423
|
-
capName: entry.capName,
|
|
61424
|
-
kind: "native",
|
|
61425
|
-
providerAddonId: entry.addonId,
|
|
61426
|
-
providerNodeId: toRoutableProviderNodeId(entry.nodeId),
|
|
61427
|
-
nativeAddonId: entry.addonId
|
|
61428
|
-
});
|
|
61429
|
-
seenCaps.add(entry.capName);
|
|
61430
|
-
}
|
|
61431
|
-
return {
|
|
61432
|
-
deviceId: input.deviceId,
|
|
61433
|
-
entries
|
|
61434
|
-
};
|
|
61435
|
-
}
|
|
61436
|
-
async function getAllBindings(deps) {
|
|
61437
|
-
const store = await deps.ctx.settings.readAddonStore();
|
|
61438
|
-
const ids = /* @__PURE__ */ new Set();
|
|
61439
|
-
for (const key of Object.keys(store.deviceMeta ?? {})) {
|
|
61440
|
-
const id = Number(key);
|
|
61441
|
-
if (Number.isInteger(id)) ids.add(id);
|
|
61442
|
-
}
|
|
61443
|
-
const registered = deps.ctx.kernel?.deviceRegistry?.getAll() ?? [];
|
|
61444
|
-
for (const device of registered) ids.add(device.id);
|
|
61445
|
-
if (ids.size === 0) {
|
|
61446
|
-
deps.ctx.logger.warn("getAllBindings found no devices \u2014 warm boot will see nothing", { meta: { hasRegistry: deps.ctx.kernel?.deviceRegistry !== void 0 } });
|
|
61447
|
-
return [];
|
|
61448
|
-
}
|
|
61449
|
-
const out = [];
|
|
61450
|
-
for (const deviceId of [...ids].sort((a, b) => a - b)) out.push(await getBindings(deps, { deviceId }));
|
|
61451
|
-
return out;
|
|
61452
|
-
}
|
|
61453
|
-
async function lookupPersistedStableId(deps, deviceId) {
|
|
61454
|
-
return ((await deps.ctx.settings.readAddonStore()).deviceMeta ?? {})[String(deviceId)]?.stableId;
|
|
61455
|
-
}
|
|
61456
61731
|
async function getDeviceAggregate(deps, deviceId, kind) {
|
|
61457
61732
|
const registry = deps.capabilityRegistry;
|
|
61458
61733
|
if (!registry) {
|
|
@@ -62009,7 +62284,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62009
62284
|
if (wireable) caps.push(wireable);
|
|
62010
62285
|
}
|
|
62011
62286
|
if (input.includeSynthesizable === true) {
|
|
62012
|
-
const deviceType = (
|
|
62287
|
+
const deviceType = (await deps.bindingsDeps.rows.get(deviceId))?.meta.type;
|
|
62013
62288
|
if (deviceType !== void 0) for (const def of require_dist10.ALL_CAPABILITY_DEFINITIONS) {
|
|
62014
62289
|
if (seen.has(def.name)) continue;
|
|
62015
62290
|
if (def.scope !== "device" || def.kind === "wrapper") continue;
|
|
@@ -62097,20 +62372,10 @@ var require_device_manager_addon = __commonJS({
|
|
|
62097
62372
|
return chain;
|
|
62098
62373
|
}
|
|
62099
62374
|
};
|
|
62100
|
-
async function stampIntegrationId(metaStore,
|
|
62375
|
+
async function stampIntegrationId(metaStore, ctx, deviceId, integrationId) {
|
|
62101
62376
|
await metaStore.withMetaWriteLock(async () => {
|
|
62102
|
-
|
|
62103
|
-
|
|
62104
|
-
const { meta: m } = persisted;
|
|
62105
|
-
const key = String(deviceId);
|
|
62106
|
-
const allMeta = await metaStore.readMeta();
|
|
62107
|
-
await settings.writeAddonStore({ deviceMeta: {
|
|
62108
|
-
...allMeta,
|
|
62109
|
-
[key]: {
|
|
62110
|
-
...m,
|
|
62111
|
-
integrationId
|
|
62112
|
-
}
|
|
62113
|
-
} });
|
|
62377
|
+
if (!await metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] stampIntegrationId: unknown device id=${deviceId}`);
|
|
62378
|
+
await metaStore.rows.patch(deviceId, { integrationId });
|
|
62114
62379
|
});
|
|
62115
62380
|
ctx.eventBus.emit({
|
|
62116
62381
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62130,70 +62395,54 @@ var require_device_manager_addon = __commonJS({
|
|
|
62130
62395
|
async function allocateDeviceId(pctx, input) {
|
|
62131
62396
|
const { addonId, stableId } = input;
|
|
62132
62397
|
return await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62133
|
-
const
|
|
62134
|
-
|
|
62135
|
-
if (existing) return { id: existing.id };
|
|
62398
|
+
const existing = await pctx.metaStore.rows.findByStableId(addonId, stableId);
|
|
62399
|
+
if (existing) return { id: existing.meta.id };
|
|
62136
62400
|
const id = await pctx.metaStore.allocateNextDeviceId();
|
|
62137
|
-
await pctx.
|
|
62138
|
-
|
|
62139
|
-
|
|
62140
|
-
|
|
62141
|
-
|
|
62142
|
-
|
|
62143
|
-
|
|
62144
|
-
|
|
62145
|
-
|
|
62146
|
-
|
|
62147
|
-
|
|
62148
|
-
|
|
62149
|
-
}
|
|
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
|
+
});
|
|
62150
62414
|
return { id };
|
|
62151
62415
|
});
|
|
62152
62416
|
}
|
|
62153
62417
|
async function registerDevice(pctx, input) {
|
|
62154
62418
|
const { addonId, stableId, id, type, name, parentDeviceId, features, config } = input;
|
|
62155
|
-
const key = String(id);
|
|
62156
62419
|
const featuresArr = Array.isArray(features) ? [...features] : [];
|
|
62157
62420
|
const { isFirstRegistration, exportFingerprint, fingerprintChanged } = await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62158
|
-
const
|
|
62159
|
-
const
|
|
62160
|
-
const
|
|
62161
|
-
if (!wasInIndex) await pctx.settings.writeAddonStore({ deviceIndex: {
|
|
62162
|
-
...index,
|
|
62163
|
-
[addonId]: [...existing, stableId]
|
|
62164
|
-
} });
|
|
62165
|
-
const meta = await pctx.metaStore.readMeta();
|
|
62166
|
-
const existingMeta = meta[key];
|
|
62421
|
+
const existingRow = await pctx.metaStore.rows.get(id);
|
|
62422
|
+
const existingMeta = existingRow?.meta;
|
|
62423
|
+
const wasRegistered = existingRow?.registered ?? false;
|
|
62167
62424
|
const wasUserNamed = existingMeta?.userNamed ?? (existingMeta !== void 0 && existingMeta.name !== stableId);
|
|
62168
62425
|
const resolvedName = wasUserNamed && existingMeta !== void 0 ? existingMeta.name : name;
|
|
62169
|
-
const isFirst = !existingMeta || !
|
|
62426
|
+
const isFirst = !existingMeta || !wasRegistered;
|
|
62170
62427
|
const fingerprint = (0, _camstack_types_node.canonicalDeviceFingerprint)({
|
|
62171
62428
|
deviceId: id,
|
|
62172
62429
|
deviceType: type,
|
|
62173
62430
|
features: featuresArr
|
|
62174
62431
|
});
|
|
62175
|
-
await pctx.
|
|
62176
|
-
|
|
62177
|
-
|
|
62178
|
-
|
|
62179
|
-
|
|
62180
|
-
|
|
62181
|
-
|
|
62182
|
-
|
|
62183
|
-
|
|
62184
|
-
|
|
62185
|
-
|
|
62186
|
-
|
|
62187
|
-
|
|
62188
|
-
|
|
62189
|
-
...existingMeta?.role !== void 0 ? { role: existingMeta.role } : {},
|
|
62190
|
-
...existingMeta?.display !== void 0 ? { display: existingMeta.display } : {},
|
|
62191
|
-
parentDeviceId,
|
|
62192
|
-
id,
|
|
62193
|
-
features: featuresArr,
|
|
62194
|
-
exportFingerprint: fingerprint
|
|
62195
|
-
}
|
|
62196
|
-
} });
|
|
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
|
+
});
|
|
62197
62446
|
return {
|
|
62198
62447
|
isFirstRegistration: isFirst,
|
|
62199
62448
|
exportFingerprint: fingerprint,
|
|
@@ -62253,26 +62502,9 @@ var require_device_manager_addon = __commonJS({
|
|
|
62253
62502
|
const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
|
|
62254
62503
|
if (!persisted) return;
|
|
62255
62504
|
const { addonId, stableId, meta: persistedMeta } = persisted;
|
|
62256
|
-
const key = String(deviceId);
|
|
62257
62505
|
const deviceName = persistedMeta.name;
|
|
62258
62506
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62259
|
-
|
|
62260
|
-
const remaining = (index[addonId] ?? []).filter((sid) => sid !== stableId);
|
|
62261
|
-
const updatedIndex = remaining.length > 0 ? {
|
|
62262
|
-
...index,
|
|
62263
|
-
[addonId]: remaining
|
|
62264
|
-
} : (() => {
|
|
62265
|
-
const { [addonId]: _removed, ...rest } = index;
|
|
62266
|
-
return rest;
|
|
62267
|
-
})();
|
|
62268
|
-
await pctx.settings.writeAddonStore({ deviceIndex: updatedIndex });
|
|
62269
|
-
const { [key]: _removedMeta, ...restMeta } = await pctx.metaStore.readMeta();
|
|
62270
|
-
await pctx.settings.writeAddonStore({ deviceMeta: restMeta });
|
|
62271
|
-
const map = await pctx.metaStore.readMetadataMap();
|
|
62272
|
-
if (key in map) {
|
|
62273
|
-
const { [key]: _removedMetadata, ...restMap } = map;
|
|
62274
|
-
await pctx.settings.writeAddonStore({ deviceMetadata: restMap });
|
|
62275
|
-
}
|
|
62507
|
+
await pctx.metaStore.rows.remove(deviceId);
|
|
62276
62508
|
});
|
|
62277
62509
|
await pctx.settings.clearDeviceStore(deviceId);
|
|
62278
62510
|
await pctx.settings.clearDeviceRuntimeState(deviceId);
|
|
@@ -62316,11 +62548,10 @@ var require_device_manager_addon = __commonJS({
|
|
|
62316
62548
|
}
|
|
62317
62549
|
async function loadMeta(pctx, input) {
|
|
62318
62550
|
const { deviceId } = input;
|
|
62319
|
-
const
|
|
62320
|
-
if (
|
|
62321
|
-
const {
|
|
62322
|
-
const
|
|
62323
|
-
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;
|
|
62324
62555
|
return {
|
|
62325
62556
|
id: m.id,
|
|
62326
62557
|
stableId,
|
|
@@ -62343,32 +62574,26 @@ var require_device_manager_addon = __commonJS({
|
|
|
62343
62574
|
const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
|
|
62344
62575
|
if (!persisted) throw new Error(`[device-manager] setName: unknown device id=${deviceId}`);
|
|
62345
62576
|
const { meta: m } = persisted;
|
|
62346
|
-
const key = String(deviceId);
|
|
62347
62577
|
const oldName = m.name;
|
|
62348
|
-
|
|
62349
|
-
|
|
62350
|
-
|
|
62351
|
-
|
|
62352
|
-
|
|
62353
|
-
|
|
62354
|
-
|
|
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
|
+
});
|
|
62355
62595
|
}
|
|
62356
|
-
};
|
|
62357
|
-
if (oldName.length > 0 && oldName !== name) for (const [childKey, childMeta] of Object.entries(allMeta)) {
|
|
62358
|
-
if (childKey === key) continue;
|
|
62359
|
-
if (!(childMeta.parentDeviceId === deviceId || childMeta.linkDeviceId === deviceId)) continue;
|
|
62360
|
-
if (childMeta.name !== oldName && !childMeta.name.startsWith(`${oldName} `)) continue;
|
|
62361
|
-
const childName = childMeta.name === oldName ? name : `${name}${childMeta.name.slice(oldName.length)}`;
|
|
62362
|
-
nextMeta[childKey] = {
|
|
62363
|
-
...childMeta,
|
|
62364
|
-
name: childName
|
|
62365
|
-
};
|
|
62366
|
-
cascaded.push({
|
|
62367
|
-
id: Number(childKey),
|
|
62368
|
-
name: childName
|
|
62369
|
-
});
|
|
62370
62596
|
}
|
|
62371
|
-
await pctx.settings.writeAddonStore({ deviceMeta: nextMeta });
|
|
62372
62597
|
});
|
|
62373
62598
|
pctx.host.ctx.eventBus.emit({
|
|
62374
62599
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62402,18 +62627,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62402
62627
|
async function setLocation(pctx, input) {
|
|
62403
62628
|
const { deviceId, location } = input;
|
|
62404
62629
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62405
|
-
|
|
62406
|
-
|
|
62407
|
-
const { meta: m } = persisted;
|
|
62408
|
-
const key = String(deviceId);
|
|
62409
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62410
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62411
|
-
...allMeta,
|
|
62412
|
-
[key]: {
|
|
62413
|
-
...m,
|
|
62414
|
-
location
|
|
62415
|
-
}
|
|
62416
|
-
} });
|
|
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 });
|
|
62417
62632
|
});
|
|
62418
62633
|
pctx.host.ctx.eventBus.emit({
|
|
62419
62634
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62433,18 +62648,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62433
62648
|
async function setType(pctx, input) {
|
|
62434
62649
|
const { deviceId, type } = input;
|
|
62435
62650
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62436
|
-
|
|
62437
|
-
|
|
62438
|
-
const { meta: m } = persisted;
|
|
62439
|
-
const key = String(deviceId);
|
|
62440
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62441
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62442
|
-
...allMeta,
|
|
62443
|
-
[key]: {
|
|
62444
|
-
...m,
|
|
62445
|
-
type
|
|
62446
|
-
}
|
|
62447
|
-
} });
|
|
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 });
|
|
62448
62653
|
});
|
|
62449
62654
|
pctx.host.ctx.eventBus.emit({
|
|
62450
62655
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62467,18 +62672,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62467
62672
|
async function setLinkDeviceId(pctx, input) {
|
|
62468
62673
|
const { deviceId, linkDeviceId } = input;
|
|
62469
62674
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62470
|
-
|
|
62471
|
-
|
|
62472
|
-
const { meta: m } = persisted;
|
|
62473
|
-
const key = String(deviceId);
|
|
62474
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62475
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62476
|
-
...allMeta,
|
|
62477
|
-
[key]: {
|
|
62478
|
-
...m,
|
|
62479
|
-
linkDeviceId
|
|
62480
|
-
}
|
|
62481
|
-
} });
|
|
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 });
|
|
62482
62677
|
});
|
|
62483
62678
|
pctx.host.ctx.eventBus.emit({
|
|
62484
62679
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62498,18 +62693,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62498
62693
|
async function setPrimaryChildEntityId(pctx, input) {
|
|
62499
62694
|
const { deviceId, primaryChildEntityId } = input;
|
|
62500
62695
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62501
|
-
|
|
62502
|
-
|
|
62503
|
-
const { meta: m } = persisted;
|
|
62504
|
-
const key = String(deviceId);
|
|
62505
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62506
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62507
|
-
...allMeta,
|
|
62508
|
-
[key]: {
|
|
62509
|
-
...m,
|
|
62510
|
-
primaryChildEntityId
|
|
62511
|
-
}
|
|
62512
|
-
} });
|
|
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 });
|
|
62513
62698
|
});
|
|
62514
62699
|
pctx.host.ctx.eventBus.emit({
|
|
62515
62700
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62529,18 +62714,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62529
62714
|
async function setChildLayout(pctx, input) {
|
|
62530
62715
|
const { deviceId, childLayout } = input;
|
|
62531
62716
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62532
|
-
|
|
62533
|
-
|
|
62534
|
-
const { meta: m } = persisted;
|
|
62535
|
-
const key = String(deviceId);
|
|
62536
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62537
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62538
|
-
...allMeta,
|
|
62539
|
-
[key]: {
|
|
62540
|
-
...m,
|
|
62541
|
-
childLayout
|
|
62542
|
-
}
|
|
62543
|
-
} });
|
|
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 });
|
|
62544
62719
|
});
|
|
62545
62720
|
pctx.host.ctx.eventBus.emit({
|
|
62546
62721
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62560,18 +62735,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62560
62735
|
async function setRole(pctx, input) {
|
|
62561
62736
|
const { deviceId, role } = input;
|
|
62562
62737
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62563
|
-
|
|
62564
|
-
|
|
62565
|
-
const { meta: m } = persisted;
|
|
62566
|
-
const key = String(deviceId);
|
|
62567
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62568
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62569
|
-
...allMeta,
|
|
62570
|
-
[key]: {
|
|
62571
|
-
...m,
|
|
62572
|
-
role
|
|
62573
|
-
}
|
|
62574
|
-
} });
|
|
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 });
|
|
62575
62740
|
});
|
|
62576
62741
|
pctx.host.ctx.eventBus.emit({
|
|
62577
62742
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62602,19 +62767,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62602
62767
|
const { deviceId, display } = input;
|
|
62603
62768
|
const normalized = display === null ? null : normalizeDisplayOverride(display);
|
|
62604
62769
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62605
|
-
|
|
62606
|
-
|
|
62607
|
-
const { meta: m } = persisted;
|
|
62608
|
-
const key = String(deviceId);
|
|
62609
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62610
|
-
const nextRow = normalized === null ? (({ display: _drop, ...rest }) => rest)(m) : {
|
|
62611
|
-
...m,
|
|
62612
|
-
display: normalized
|
|
62613
|
-
};
|
|
62614
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62615
|
-
...allMeta,
|
|
62616
|
-
[key]: nextRow
|
|
62617
|
-
} });
|
|
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 });
|
|
62618
62772
|
});
|
|
62619
62773
|
pctx.host.ctx.eventBus.emit({
|
|
62620
62774
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62644,24 +62798,15 @@ var require_device_manager_addon = __commonJS({
|
|
|
62644
62798
|
async function applyInitialMeta(pctx, input) {
|
|
62645
62799
|
const { deviceId, name, location, type, integrationId, linkDeviceId, role } = input;
|
|
62646
62800
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62647
|
-
|
|
62648
|
-
|
|
62649
|
-
const { meta: m } = persisted;
|
|
62650
|
-
const key = String(deviceId);
|
|
62651
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62652
|
-
const merged = {
|
|
62653
|
-
...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, {
|
|
62654
62803
|
...name !== void 0 ? { name } : {},
|
|
62655
62804
|
...location !== void 0 ? { location } : {},
|
|
62656
62805
|
...type !== void 0 ? { type } : {},
|
|
62657
62806
|
...integrationId !== void 0 ? { integrationId } : {},
|
|
62658
62807
|
...linkDeviceId !== void 0 ? { linkDeviceId } : {},
|
|
62659
62808
|
...role !== void 0 ? { role } : {}
|
|
62660
|
-
};
|
|
62661
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62662
|
-
...allMeta,
|
|
62663
|
-
[key]: merged
|
|
62664
|
-
} });
|
|
62809
|
+
});
|
|
62665
62810
|
});
|
|
62666
62811
|
const emitMetaChanged = (field, value) => {
|
|
62667
62812
|
pctx.host.ctx.eventBus.emit({
|
|
@@ -62689,10 +62834,9 @@ var require_device_manager_addon = __commonJS({
|
|
|
62689
62834
|
async function setMetadata(pctx, input) {
|
|
62690
62835
|
const { deviceId, patch } = input;
|
|
62691
62836
|
const result = await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62692
|
-
|
|
62693
|
-
|
|
62694
|
-
const
|
|
62695
|
-
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 ?? {} };
|
|
62696
62840
|
let changed = false;
|
|
62697
62841
|
for (const [k, v] of Object.entries(patch)) if (v === null) {
|
|
62698
62842
|
if (k in next) {
|
|
@@ -62705,10 +62849,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62705
62849
|
}
|
|
62706
62850
|
if (!changed) return { changed: false };
|
|
62707
62851
|
const hasFields = Object.keys(next).length > 0;
|
|
62708
|
-
|
|
62709
|
-
if (hasFields) updatedMap[key] = next;
|
|
62710
|
-
else delete updatedMap[key];
|
|
62711
|
-
await pctx.settings.writeAddonStore({ deviceMetadata: updatedMap });
|
|
62852
|
+
await pctx.metaStore.rows.patch(deviceId, { metadata: hasFields ? next : null });
|
|
62712
62853
|
return {
|
|
62713
62854
|
changed: true,
|
|
62714
62855
|
finalMeta: hasFields ? next : null
|
|
@@ -62736,15 +62877,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62736
62877
|
const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
|
|
62737
62878
|
if (!persisted) throw new Error(`[device-manager] setDisabled: unknown device id=${deviceId}`);
|
|
62738
62879
|
const { meta: m } = persisted;
|
|
62739
|
-
|
|
62740
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62741
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62742
|
-
...allMeta,
|
|
62743
|
-
[key]: {
|
|
62744
|
-
...m,
|
|
62745
|
-
disabled
|
|
62746
|
-
}
|
|
62747
|
-
} });
|
|
62880
|
+
await pctx.metaStore.rows.patch(deviceId, { disabled });
|
|
62748
62881
|
return {
|
|
62749
62882
|
changed: (m.disabled ?? false) !== disabled,
|
|
62750
62883
|
integrationId: m.integrationId ?? ""
|
|
@@ -62787,9 +62920,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62787
62920
|
return data;
|
|
62788
62921
|
}
|
|
62789
62922
|
async function listLocations(pctx) {
|
|
62790
|
-
const
|
|
62791
|
-
const meta = store.deviceMeta ?? {};
|
|
62792
|
-
const locations = store.locations ?? [];
|
|
62923
|
+
const locations = (await pctx.metaStore.readStore()).locations ?? [];
|
|
62793
62924
|
const seen = /* @__PURE__ */ new Map();
|
|
62794
62925
|
const consider = (raw) => {
|
|
62795
62926
|
if (typeof raw !== "string") return;
|
|
@@ -62799,38 +62930,34 @@ var require_device_manager_addon = __commonJS({
|
|
|
62799
62930
|
if (!seen.has(key)) seen.set(key, trimmed);
|
|
62800
62931
|
};
|
|
62801
62932
|
for (const label of locations) consider(label);
|
|
62802
|
-
for (const
|
|
62933
|
+
for (const row of await pctx.metaStore.rows.listAll()) consider(row.meta.location);
|
|
62803
62934
|
return [...seen.values()].toSorted((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
|
|
62804
62935
|
}
|
|
62805
62936
|
async function addLocation(pctx, input) {
|
|
62806
62937
|
const trimmed = input.name.trim();
|
|
62807
62938
|
if (trimmed.length === 0) throw new Error("[device-manager] addLocation: name must be non-empty");
|
|
62808
|
-
const current = (await pctx.
|
|
62939
|
+
const current = (await pctx.metaStore.readStore()).locations ?? [];
|
|
62809
62940
|
if (current.some((l) => l.toLowerCase() === trimmed.toLowerCase())) return;
|
|
62810
62941
|
await pctx.settings.writeAddonStore({ locations: [...current, trimmed] });
|
|
62811
62942
|
}
|
|
62812
62943
|
async function removeLocation(pctx, input) {
|
|
62813
62944
|
const trimmed = input.name.trim();
|
|
62814
62945
|
if (trimmed.length === 0) return;
|
|
62815
|
-
const
|
|
62816
|
-
const current = store.locations ?? [];
|
|
62946
|
+
const current = (await pctx.metaStore.readStore()).locations ?? [];
|
|
62817
62947
|
const remaining = current.filter((l) => l.toLowerCase() !== trimmed.toLowerCase());
|
|
62818
62948
|
if (remaining.length !== current.length) await pctx.settings.writeAddonStore({ locations: remaining });
|
|
62819
62949
|
if (input.cascade !== true) return;
|
|
62820
|
-
const
|
|
62821
|
-
|
|
62822
|
-
|
|
62823
|
-
|
|
62824
|
-
|
|
62825
|
-
|
|
62826
|
-
|
|
62827
|
-
|
|
62828
|
-
|
|
62829
|
-
|
|
62830
|
-
|
|
62831
|
-
}
|
|
62832
|
-
if (cleared.length === 0) return;
|
|
62833
|
-
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
|
+
});
|
|
62834
62961
|
for (const deviceId of cleared) pctx.host.ctx.eventBus.emit({
|
|
62835
62962
|
id: (0, node_crypto.randomUUID)(),
|
|
62836
62963
|
timestamp: /* @__PURE__ */ new Date(),
|
|
@@ -62846,40 +62973,65 @@ var require_device_manager_addon = __commonJS({
|
|
|
62846
62973
|
}
|
|
62847
62974
|
});
|
|
62848
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
|
+
}
|
|
62849
62989
|
var DeviceMetaStore = class {
|
|
62850
62990
|
settings;
|
|
62851
62991
|
registry;
|
|
62992
|
+
rows;
|
|
62852
62993
|
/** Synchronous ownership cache, keyed by NUMERIC deviceId → owning addonId.
|
|
62853
|
-
* The persisted
|
|
62994
|
+
* The persisted row store is authoritative but reads are async; hub-side
|
|
62854
62995
|
* callers (e.g. `CapabilityRegistry.getNativeProvider` fallback) need
|
|
62855
62996
|
* ownership without awaiting. Kept in sync with every register/remove and
|
|
62856
62997
|
* warmed from persistence on boot. */
|
|
62857
62998
|
idToAddonId = /* @__PURE__ */ new Map();
|
|
62858
|
-
/** Serialises every read-modify-write of
|
|
62859
|
-
*
|
|
62860
|
-
* 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. */
|
|
62861
63001
|
metaWriteChain = Promise.resolve();
|
|
62862
|
-
constructor(settings, registry) {
|
|
63002
|
+
constructor(settings, registry, rows) {
|
|
62863
63003
|
this.settings = settings;
|
|
62864
63004
|
this.registry = registry;
|
|
63005
|
+
this.rows = rows;
|
|
62865
63006
|
}
|
|
63007
|
+
/** The read currently in flight, or null. Never a settled value — see
|
|
63008
|
+
* {@link readStore}. */
|
|
63009
|
+
inFlightRead = null;
|
|
63010
|
+
/**
|
|
63011
|
+
* The addon's own settings row set — `nextDeviceId`, `roleDisplayDefaults`,
|
|
63012
|
+
* `locations`. NOT the fleet: no device has lived here since the flatten.
|
|
63013
|
+
*
|
|
63014
|
+
* **Concurrent callers join the read already in flight.** This is not a
|
|
63015
|
+
* cache and nothing survives settlement: a caller that awaited the running
|
|
63016
|
+
* promise could not have observed anything older than its result, so the
|
|
63017
|
+
* only thing that changes is cost.
|
|
63018
|
+
*
|
|
63019
|
+
* A rejection is NOT latched: the slot is cleared before the promise
|
|
63020
|
+
* settles either way, so a failed read costs the joiners that one failure
|
|
63021
|
+
* and the next caller reaches the store again.
|
|
63022
|
+
*/
|
|
62866
63023
|
readStore = async () => {
|
|
62867
|
-
|
|
62868
|
-
|
|
62869
|
-
|
|
62870
|
-
|
|
62871
|
-
|
|
62872
|
-
|
|
62873
|
-
|
|
62874
|
-
|
|
62875
|
-
|
|
62876
|
-
|
|
62877
|
-
|
|
62878
|
-
* (`registerDevice` / `setName` / `setLocation` / `setDisabled`).
|
|
62879
|
-
* Single-writer per row eliminates the "writer X clobbers writer
|
|
62880
|
-
* Y's field" bug class — `setMetadata` is the only producer. */
|
|
62881
|
-
readMetadataMap = async () => {
|
|
62882
|
-
return (await this.readStore()).deviceMetadata ?? {};
|
|
63024
|
+
const existing = this.inFlightRead;
|
|
63025
|
+
if (existing !== null) return existing;
|
|
63026
|
+
const read = (async () => {
|
|
63027
|
+
try {
|
|
63028
|
+
return decodeAddonStore(await this.settings.readAddonStore());
|
|
63029
|
+
} finally {
|
|
63030
|
+
this.inFlightRead = null;
|
|
63031
|
+
}
|
|
63032
|
+
})();
|
|
63033
|
+
this.inFlightRead = read;
|
|
63034
|
+
return read;
|
|
62883
63035
|
};
|
|
62884
63036
|
withMetaWriteLock = async (fn) => {
|
|
62885
63037
|
const previous = this.metaWriteChain;
|
|
@@ -62897,31 +63049,36 @@ var require_device_manager_addon = __commonJS({
|
|
|
62897
63049
|
release();
|
|
62898
63050
|
}
|
|
62899
63051
|
};
|
|
63052
|
+
/** The whole persisted row for one device, or `null`. */
|
|
63053
|
+
getRow = async (deviceId) => this.rows.get(deviceId);
|
|
62900
63054
|
/**
|
|
62901
63055
|
* Resolve a numeric deviceId to the owning `(addonId, stableId)` pair.
|
|
62902
|
-
*
|
|
63056
|
+
* Reads the device's own row — live IDevice lookup (hub registry) is handled
|
|
62903
63057
|
* separately per call site so callers can decide whether to route to
|
|
62904
63058
|
* an in-process driver or to the cross-process `device-ops` bridge.
|
|
62905
63059
|
* Returns null when no device with that id is known to the hub.
|
|
62906
63060
|
*/
|
|
62907
63061
|
resolvePersistedById = async (deviceId) => {
|
|
62908
|
-
const
|
|
62909
|
-
if (
|
|
63062
|
+
const row = await this.rows.get(deviceId);
|
|
63063
|
+
if (row === null) return null;
|
|
62910
63064
|
return {
|
|
62911
|
-
addonId:
|
|
62912
|
-
stableId:
|
|
62913
|
-
meta:
|
|
63065
|
+
addonId: row.meta.addonId,
|
|
63066
|
+
stableId: row.meta.stableId,
|
|
63067
|
+
meta: row.meta
|
|
62914
63068
|
};
|
|
62915
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
|
+
};
|
|
62916
63074
|
/** Direct children of a device: the union of the live registry's children
|
|
62917
|
-
* and the persisted
|
|
63075
|
+
* and the persisted rows whose `parentDeviceId` is `parentId`, deduplicated
|
|
62918
63076
|
* and excluding self. Shared by the `remove` cascade and the `resetToSource`
|
|
62919
63077
|
* resync purge (#19). */
|
|
62920
63078
|
directChildIds = async (parentId) => {
|
|
62921
63079
|
const ids = /* @__PURE__ */ new Set();
|
|
62922
63080
|
if (this.registry) for (const c of this.registry.getChildren(parentId)) ids.add(c.id);
|
|
62923
|
-
const
|
|
62924
|
-
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);
|
|
62925
63082
|
ids.delete(parentId);
|
|
62926
63083
|
return [...ids];
|
|
62927
63084
|
};
|
|
@@ -62931,6 +63088,476 @@ var require_device_manager_addon = __commonJS({
|
|
|
62931
63088
|
return current;
|
|
62932
63089
|
};
|
|
62933
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
|
+
};
|
|
62934
63561
|
function persistableBlob(blob, policyFor) {
|
|
62935
63562
|
const out = {};
|
|
62936
63563
|
for (const [capName, slice] of Object.entries(blob)) {
|
|
@@ -63409,12 +64036,21 @@ var require_device_manager_addon = __commonJS({
|
|
|
63409
64036
|
}
|
|
63410
64037
|
}))).filter((id) => id !== null);
|
|
63411
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;
|
|
63412
64045
|
/** Build the dependency context the extracted binding resolvers consume. */
|
|
63413
64046
|
get bindingsDeps() {
|
|
64047
|
+
const rows = this.deviceRows;
|
|
64048
|
+
if (rows === null) throw new Error("[device-manager] device row store not initialized");
|
|
63414
64049
|
return {
|
|
63415
64050
|
ctx: this.ctx,
|
|
63416
64051
|
capabilityRegistry: this.capabilityRegistry,
|
|
63417
|
-
remoteNativeCaps: this.remoteNativeCaps
|
|
64052
|
+
remoteNativeCaps: this.remoteNativeCaps,
|
|
64053
|
+
rows
|
|
63418
64054
|
};
|
|
63419
64055
|
}
|
|
63420
64056
|
async getBindings(input) {
|
|
@@ -63470,17 +64106,23 @@ var require_device_manager_addon = __commonJS({
|
|
|
63470
64106
|
if (!ops) throw new Error(`[device-manager] device-ops native provider not found for '${deviceId}'`);
|
|
63471
64107
|
return ops;
|
|
63472
64108
|
};
|
|
63473
|
-
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);
|
|
63474
64120
|
this.stateMirrorImpl = new DeviceStateMirror(this.ctx);
|
|
63475
64121
|
const stateMirror = this.stateMirrorImpl;
|
|
63476
|
-
const readMeta = metaStore.readMeta;
|
|
63477
64122
|
const resolvePersistedById = metaStore.resolvePersistedById;
|
|
63478
64123
|
const idToAddonId = metaStore.idToAddonId;
|
|
63479
|
-
|
|
63480
|
-
|
|
63481
|
-
for (const m of Object.values(meta)) idToAddonId.set(m.id, m.addonId);
|
|
63482
|
-
}
|
|
63483
|
-
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);
|
|
63484
64126
|
const pctx = {
|
|
63485
64127
|
host: this.providerHost,
|
|
63486
64128
|
metaStore,
|
|
@@ -63723,7 +64365,7 @@ var require_hub_forwarder = __commonJS({
|
|
|
63723
64365
|
[Symbol.toStringTag]: { value: "Module" }
|
|
63724
64366
|
});
|
|
63725
64367
|
require_chunk_Cek0wNdY();
|
|
63726
|
-
var require_dist10 =
|
|
64368
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
63727
64369
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
63728
64370
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
63729
64371
|
var HubForwarderDestination = class {
|
|
@@ -63860,7 +64502,7 @@ var require_liveness_monitor_addon = __commonJS({
|
|
|
63860
64502
|
"use strict";
|
|
63861
64503
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
63862
64504
|
require_chunk_Cek0wNdY();
|
|
63863
|
-
var require_dist10 =
|
|
64505
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
63864
64506
|
var NO_DEVICES = "liveness:no-devices";
|
|
63865
64507
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
63866
64508
|
var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
|
|
@@ -64050,7 +64692,7 @@ var require_local_auth_addon = __commonJS({
|
|
|
64050
64692
|
[Symbol.toStringTag]: { value: "Module" }
|
|
64051
64693
|
});
|
|
64052
64694
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
64053
|
-
var require_dist10 =
|
|
64695
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
64054
64696
|
var node_crypto = __require("crypto");
|
|
64055
64697
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
64056
64698
|
var crypto$1 = __require("crypto");
|
|
@@ -71734,7 +72376,7 @@ var require_loki_logging = __commonJS({
|
|
|
71734
72376
|
[Symbol.toStringTag]: { value: "Module" }
|
|
71735
72377
|
});
|
|
71736
72378
|
require_chunk_Cek0wNdY();
|
|
71737
|
-
var require_dist10 =
|
|
72379
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
71738
72380
|
function sanitizeLabelName(raw) {
|
|
71739
72381
|
const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
|
|
71740
72382
|
return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
|
|
@@ -72299,7 +72941,7 @@ var require_native_metrics_addon = __commonJS({
|
|
|
72299
72941
|
[Symbol.toStringTag]: { value: "Module" }
|
|
72300
72942
|
});
|
|
72301
72943
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
72302
|
-
var require_dist10 =
|
|
72944
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
72303
72945
|
var node_child_process = __require("child_process");
|
|
72304
72946
|
var node_util = __require("util");
|
|
72305
72947
|
var node_os = __require("os");
|
|
@@ -73241,7 +73883,7 @@ var require_filesystem_storage_addon = __commonJS({
|
|
|
73241
73883
|
[Symbol.toStringTag]: { value: "Module" }
|
|
73242
73884
|
});
|
|
73243
73885
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
73244
|
-
var require_dist10 =
|
|
73886
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
73245
73887
|
var node_crypto = __require("crypto");
|
|
73246
73888
|
var node_fs_promises = __require("fs/promises");
|
|
73247
73889
|
var node_path = __require("path");
|
|
@@ -74357,7 +74999,8 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74357
74999
|
[Symbol.toStringTag]: { value: "Module" }
|
|
74358
75000
|
});
|
|
74359
75001
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
74360
|
-
var require_dist10 =
|
|
75002
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
75003
|
+
var require_retired_settings_keys = require_retired_settings_keys_CYDGGkyM();
|
|
74361
75004
|
var node_crypto = __require("crypto");
|
|
74362
75005
|
var node_fs = __require("fs");
|
|
74363
75006
|
var node_module = __require("module");
|
|
@@ -74366,123 +75009,47 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74366
75009
|
var SQLITE_PAGE_CACHE_KIB = -65536;
|
|
74367
75010
|
var SQLITE_MMAP_SIZE_BYTES = 268435456;
|
|
74368
75011
|
var SQLITE_JOURNAL_SIZE_LIMIT_BYTES = 67108864;
|
|
74369
|
-
|
|
74370
|
-
|
|
74371
|
-
|
|
74372
|
-
|
|
74373
|
-
var RETIRED_SETTINGS_KEYS = [{
|
|
74374
|
-
namespace: "detection-pipeline",
|
|
74375
|
-
collection: "addon-settings",
|
|
74376
|
-
row: "root",
|
|
74377
|
-
keys: ["pipelineSteps", "pipelineEngine"],
|
|
74378
|
-
perNodeKeys: [
|
|
74379
|
-
"engineRuntime",
|
|
74380
|
-
"engineBackend",
|
|
74381
|
-
"engineDevice",
|
|
74382
|
-
"probedBestEngine"
|
|
74383
|
-
],
|
|
74384
|
-
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)"
|
|
74385
75016
|
}, {
|
|
74386
|
-
|
|
74387
|
-
|
|
74388
|
-
|
|
74389
|
-
keys: [],
|
|
74390
|
-
perNodeKeys: [],
|
|
74391
|
-
mapValueKeys: ["deviceLinks"],
|
|
74392
|
-
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`"
|
|
74393
75020
|
}];
|
|
74394
|
-
function
|
|
74395
|
-
return {
|
|
74396
|
-
|
|
74397
|
-
|
|
74398
|
-
namespace: spec.namespace,
|
|
74399
|
-
collection: spec.collection,
|
|
74400
|
-
key: spec.row
|
|
74401
|
-
}));
|
|
74402
|
-
},
|
|
74403
|
-
async write(spec, value) {
|
|
74404
|
-
await backend.set({
|
|
74405
|
-
namespace: spec.namespace,
|
|
74406
|
-
collection: spec.collection,
|
|
74407
|
-
key: spec.row,
|
|
74408
|
-
value
|
|
74409
|
-
});
|
|
74410
|
-
}
|
|
74411
|
-
};
|
|
74412
|
-
}
|
|
74413
|
-
function planNestedRetiredKeyPurge(blob, spec) {
|
|
74414
|
-
const nestedKeys = spec.mapValueKeys ?? [];
|
|
74415
|
-
if (nestedKeys.length === 0) return [];
|
|
74416
|
-
const found = [];
|
|
74417
|
-
for (const [mapKey, value] of Object.entries(blob)) {
|
|
74418
|
-
const inner = require_dist10.asJsonObject(value);
|
|
74419
|
-
if (inner === null) continue;
|
|
74420
|
-
for (const retired of nestedKeys) if (retired in inner) found.push(`${mapKey}.${retired}`);
|
|
74421
|
-
}
|
|
74422
|
-
return found;
|
|
74423
|
-
}
|
|
74424
|
-
function planRetiredKeyPurge(blob, spec) {
|
|
74425
|
-
const exact = new Set(spec.keys);
|
|
74426
|
-
const found = [];
|
|
74427
|
-
for (const key of Object.keys(blob)) {
|
|
74428
|
-
if (exact.has(key)) {
|
|
74429
|
-
found.push(key);
|
|
74430
|
-
continue;
|
|
74431
|
-
}
|
|
74432
|
-
for (const perNode of spec.perNodeKeys) if (key === perNode || key.startsWith(`${perNode}@`)) {
|
|
74433
|
-
found.push(key);
|
|
74434
|
-
break;
|
|
74435
|
-
}
|
|
74436
|
-
}
|
|
74437
|
-
return found;
|
|
75021
|
+
function retiredCollectionStoreOf(backend) {
|
|
75022
|
+
return { async drop(collection) {
|
|
75023
|
+
return backend.dropRetiredTable(collection);
|
|
75024
|
+
} };
|
|
74438
75025
|
}
|
|
74439
|
-
async function
|
|
74440
|
-
if (!settingsStoreIsAuthoritativeHere(env)) return [];
|
|
75026
|
+
async function purgeRetiredCollections(store, logger, specs = RETIRED_COLLECTIONS) {
|
|
74441
75027
|
const results = [];
|
|
74442
75028
|
for (const spec of specs) {
|
|
74443
|
-
let
|
|
75029
|
+
let dropped;
|
|
74444
75030
|
try {
|
|
74445
|
-
|
|
75031
|
+
dropped = await store.drop(spec.collection);
|
|
74446
75032
|
} catch (err) {
|
|
74447
|
-
logger.warn("retired
|
|
74448
|
-
addon: spec.
|
|
75033
|
+
logger.warn("retired collection \u2014 drop failed, table left in place", { meta: {
|
|
75034
|
+
addon: spec.owner,
|
|
74449
75035
|
collection: spec.collection,
|
|
74450
|
-
row: spec.row,
|
|
74451
75036
|
error: err instanceof Error ? err.message : String(err)
|
|
74452
75037
|
} });
|
|
74453
75038
|
continue;
|
|
74454
75039
|
}
|
|
74455
|
-
if (
|
|
74456
|
-
const doomed = planRetiredKeyPurge(blob, spec);
|
|
74457
|
-
const doomedNested = planNestedRetiredKeyPurge(blob, spec);
|
|
74458
|
-
if (doomed.length === 0 && doomedNested.length === 0) continue;
|
|
74459
|
-
const doomedSet = new Set(doomed);
|
|
74460
|
-
const nestedByMapKey = /* @__PURE__ */ new Map();
|
|
74461
|
-
for (const path of doomedNested) {
|
|
74462
|
-
const cut = path.indexOf(".");
|
|
74463
|
-
const mapKey = path.slice(0, cut);
|
|
74464
|
-
const inner = path.slice(cut + 1);
|
|
74465
|
-
const bucket = nestedByMapKey.get(mapKey) ?? /* @__PURE__ */ new Set();
|
|
74466
|
-
bucket.add(inner);
|
|
74467
|
-
nestedByMapKey.set(mapKey, bucket);
|
|
74468
|
-
}
|
|
74469
|
-
const next = Object.fromEntries(Object.entries(blob).filter(([key]) => !doomedSet.has(key)).map(([key, value]) => {
|
|
74470
|
-
const retired = nestedByMapKey.get(key);
|
|
74471
|
-
if (retired === void 0) return [key, value];
|
|
74472
|
-
const inner = require_dist10.asJsonObject(value);
|
|
74473
|
-
if (inner === null) return [key, value];
|
|
74474
|
-
return [key, Object.fromEntries(Object.entries(inner).filter(([k]) => !retired.has(k)))];
|
|
74475
|
-
}));
|
|
74476
|
-
await store.write(spec, next);
|
|
75040
|
+
if (dropped === null) continue;
|
|
74477
75041
|
const result = {
|
|
74478
|
-
addon: spec.namespace,
|
|
74479
75042
|
collection: spec.collection,
|
|
74480
|
-
|
|
74481
|
-
|
|
74482
|
-
nested: doomedNested
|
|
75043
|
+
owner: spec.owner,
|
|
75044
|
+
rows: dropped.rows
|
|
74483
75045
|
};
|
|
74484
75046
|
results.push(result);
|
|
74485
|
-
|
|
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: {
|
|
74486
75053
|
...result,
|
|
74487
75054
|
reason: spec.reason
|
|
74488
75055
|
} });
|
|
@@ -74542,6 +75109,20 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74542
75109
|
params
|
|
74543
75110
|
};
|
|
74544
75111
|
}
|
|
75112
|
+
var MAX_ASCII = 127;
|
|
75113
|
+
function prefixRange(prefix) {
|
|
75114
|
+
if (prefix.length === 0) return null;
|
|
75115
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
75116
|
+
const code = prefix.charCodeAt(i);
|
|
75117
|
+
if (code === 0 || code > MAX_ASCII) return null;
|
|
75118
|
+
}
|
|
75119
|
+
const last = prefix.charCodeAt(prefix.length - 1);
|
|
75120
|
+
if (last >= MAX_ASCII) return null;
|
|
75121
|
+
return {
|
|
75122
|
+
lo: prefix,
|
|
75123
|
+
hi: prefix.slice(0, -1) + String.fromCharCode(last + 1)
|
|
75124
|
+
};
|
|
75125
|
+
}
|
|
74545
75126
|
var SETTINGS_QUERY_DEFAULT_ROW_CAP = 2e3;
|
|
74546
75127
|
var SETTINGS_QUERY_HARD_ROW_CAP = 2e4;
|
|
74547
75128
|
function resolveRowBound(requested) {
|
|
@@ -74562,19 +75143,8 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74562
75143
|
function isImposedBound(source) {
|
|
74563
75144
|
return source !== "caller";
|
|
74564
75145
|
}
|
|
74565
|
-
|
|
74566
|
-
|
|
74567
|
-
if (prefix.length === 0) return null;
|
|
74568
|
-
for (let i = 0; i < prefix.length; i++) {
|
|
74569
|
-
const code = prefix.charCodeAt(i);
|
|
74570
|
-
if (code === 0 || code > MAX_ASCII) return null;
|
|
74571
|
-
}
|
|
74572
|
-
const last = prefix.charCodeAt(prefix.length - 1);
|
|
74573
|
-
if (last >= MAX_ASCII) return null;
|
|
74574
|
-
return {
|
|
74575
|
-
lo: prefix,
|
|
74576
|
-
hi: prefix.slice(0, -1) + String.fromCharCode(last + 1)
|
|
74577
|
-
};
|
|
75146
|
+
function isCountRow(raw) {
|
|
75147
|
+
return raw !== null && typeof raw === "object" && typeof Reflect.get(raw, "n") === "number";
|
|
74578
75148
|
}
|
|
74579
75149
|
function parseRowData(raw) {
|
|
74580
75150
|
return require_dist10.asJsonObject(require_dist10.parseJsonUnknown(raw)) ?? {};
|
|
@@ -74958,10 +75528,33 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74958
75528
|
const rows = this.getDb().prepare('SELECT id, data FROM "system-settings"').all();
|
|
74959
75529
|
return Object.fromEntries(rows.map((r) => [r.id, JSON.parse(r.data)]));
|
|
74960
75530
|
}
|
|
74961
|
-
/**
|
|
75531
|
+
/**
|
|
75532
|
+
* Get all settings for an addon.
|
|
75533
|
+
*
|
|
75534
|
+
* Selected by the SAME key range {@link setAllAddon} deletes and re-inserts
|
|
75535
|
+
* — `prefixWhere("<addonId>.")` — and for the same reason `getAllScoped`
|
|
75536
|
+
* uses it: `"<addonId>.<key>"` is the PRIMARY KEY, so the range is a
|
|
75537
|
+
* `SEARCH … USING INDEX` while anything else is a full `SCAN`.
|
|
75538
|
+
*
|
|
75539
|
+
* It used to select on `json_extract(data, '$.addonId')`, which is a second
|
|
75540
|
+
* authority for "this addon's rows" and disagreed with the writer in both
|
|
75541
|
+
* directions: a row inside the JSON's idea of the addon but outside the key
|
|
75542
|
+
* range was READ and never DELETED (a config key that could not be cleared),
|
|
75543
|
+
* and `json_extract` was evaluated on EVERY row — so a four-key addon paid
|
|
75544
|
+
* for the whole table and one unparseable neighbour aborted the statement,
|
|
75545
|
+
* taking out every addon's config read at once.
|
|
75546
|
+
*
|
|
75547
|
+
* The cost was not theoretical. On the live hub `addon-settings` is 627 KB
|
|
75548
|
+
* in 24 rows, 625 KB of it device-manager's three fleet blobs; a V8 profile
|
|
75549
|
+
* of a boot (2026-08-19) had hub-main's JS thread 99.9% busy with **64% of
|
|
75550
|
+
* it inside this method**, which is what put a ~45 s queue in front of every
|
|
75551
|
+
* runner's first store read. Measured on that table: 0.947 ms → ~0.02 ms for
|
|
75552
|
+
* a four-key addon, 2.90 ms → 2.13 ms for device-manager's own.
|
|
75553
|
+
*/
|
|
74962
75554
|
getAllAddon(addonId) {
|
|
74963
75555
|
this.requireDeclared("addon-settings");
|
|
74964
|
-
const
|
|
75556
|
+
const where = this.prefixWhere(`${addonId}.`);
|
|
75557
|
+
const rows = this.getDb().prepare(`SELECT id, data FROM "addon-settings" WHERE ${where.sql}`).all(...where.params);
|
|
74965
75558
|
if (rows.length === 0) return {};
|
|
74966
75559
|
const result = {};
|
|
74967
75560
|
for (const row of rows) {
|
|
@@ -75237,6 +75830,31 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
75237
75830
|
}
|
|
75238
75831
|
this.structuredTables.set(table, fingerprint);
|
|
75239
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
|
+
}
|
|
75240
75858
|
async tableInsert(table, row) {
|
|
75241
75859
|
const keys = Object.keys(row);
|
|
75242
75860
|
const cols = keys.map((k) => `"${k}"`).join(", ");
|
|
@@ -75747,10 +76365,11 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
75747
76365
|
this.backend = new SqliteSettingsBackend(dbPath, { ...require_dist10.RUNTIME_DEFAULTS }, this.ctx.logger.child("Query"));
|
|
75748
76366
|
await this.backend.initialize();
|
|
75749
76367
|
try {
|
|
75750
|
-
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"));
|
|
75751
76369
|
} catch (err) {
|
|
75752
76370
|
this.ctx.logger.warn("Retired-key purge failed", { meta: { error: require_dist10.errMsg(err) } });
|
|
75753
76371
|
}
|
|
76372
|
+
await purgeRetiredCollections(retiredCollectionStoreOf(this.backend), this.ctx.logger.child("RetiredCollections"));
|
|
75754
76373
|
const vectorIndex = this.createVectorIndex(this.backend);
|
|
75755
76374
|
await vectorIndex.loadIndexRegistry();
|
|
75756
76375
|
this.vectorIndex = vectorIndex;
|
|
@@ -75925,7 +76544,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
75925
76544
|
[Symbol.toStringTag]: { value: "Module" }
|
|
75926
76545
|
});
|
|
75927
76546
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
75928
|
-
var require_dist10 =
|
|
76547
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
75929
76548
|
var node_crypto = __require("crypto");
|
|
75930
76549
|
var node_fs_promises = __require("fs/promises");
|
|
75931
76550
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -77804,7 +78423,7 @@ var require_system_config_addon = __commonJS({
|
|
|
77804
78423
|
[Symbol.toStringTag]: { value: "Module" }
|
|
77805
78424
|
});
|
|
77806
78425
|
require_chunk_Cek0wNdY();
|
|
77807
|
-
var require_dist10 =
|
|
78426
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
77808
78427
|
var SECTION_TITLES = {
|
|
77809
78428
|
server: "Server",
|
|
77810
78429
|
auth: "Authentication"
|
|
@@ -95865,7 +96484,7 @@ var require_winston_logging = __commonJS({
|
|
|
95865
96484
|
[Symbol.toStringTag]: { value: "Module" }
|
|
95866
96485
|
});
|
|
95867
96486
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
95868
|
-
var require_dist10 =
|
|
96487
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
95869
96488
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
95870
96489
|
var node_path = __require("path");
|
|
95871
96490
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -107335,9 +107954,9 @@ var require_dist2 = __commonJS({
|
|
|
107335
107954
|
}
|
|
107336
107955
|
});
|
|
107337
107956
|
|
|
107338
|
-
// ../system/dist/manifest-python-deps-
|
|
107339
|
-
var
|
|
107340
|
-
"../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) {
|
|
107341
107960
|
"use strict";
|
|
107342
107961
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
107343
107962
|
var node_crypto = __require("crypto");
|
|
@@ -111314,6 +111933,8 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111314
111933
|
};
|
|
111315
111934
|
goneHandler = () => {
|
|
111316
111935
|
};
|
|
111936
|
+
/** Source of {@link RegisteredChild.incarnation}; one bump per accepted connection. */
|
|
111937
|
+
nextIncarnation = 1;
|
|
111317
111938
|
eventHandler = null;
|
|
111318
111939
|
logHandler = null;
|
|
111319
111940
|
readinessHandler = null;
|
|
@@ -111588,14 +112209,20 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111588
112209
|
listChildren() {
|
|
111589
112210
|
return [...this.children.values()].map((e) => ({
|
|
111590
112211
|
childId: e.childId,
|
|
111591
|
-
caps: e.caps
|
|
112212
|
+
caps: e.caps,
|
|
112213
|
+
incarnation: e.incarnation
|
|
111592
112214
|
}));
|
|
111593
112215
|
}
|
|
111594
112216
|
/** Register the (single) child-registered handler. Only one handler is active at a time. */
|
|
111595
112217
|
onChildRegistered(handler) {
|
|
111596
112218
|
this.registeredHandler = handler;
|
|
111597
112219
|
}
|
|
111598
|
-
/**
|
|
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
|
+
*/
|
|
111599
112226
|
onChildGone(handler) {
|
|
111600
112227
|
this.goneHandler = handler;
|
|
111601
112228
|
}
|
|
@@ -111680,6 +112307,7 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111680
112307
|
}
|
|
111681
112308
|
onConnection(channel) {
|
|
111682
112309
|
let childId = null;
|
|
112310
|
+
const incarnation = this.nextIncarnation++;
|
|
111683
112311
|
channel.onEvent((body) => {
|
|
111684
112312
|
const msg = body;
|
|
111685
112313
|
if (msg.kind === "event") {
|
|
@@ -111708,16 +112336,24 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111708
112336
|
if (childId !== null && childId !== msg.childId) throw new Error(`child attempted to change identity from "${childId}" to "${msg.childId}"`);
|
|
111709
112337
|
childId = msg.childId;
|
|
111710
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
|
+
});
|
|
111711
112345
|
this.children.set(msg.childId, {
|
|
111712
112346
|
childId: msg.childId,
|
|
111713
112347
|
channel,
|
|
111714
112348
|
caps: msg.caps,
|
|
111715
|
-
eventPatterns
|
|
112349
|
+
eventPatterns,
|
|
112350
|
+
incarnation
|
|
111716
112351
|
});
|
|
111717
112352
|
this.logPatternSet(msg.childId, eventPatterns);
|
|
111718
112353
|
this.registeredHandler({
|
|
111719
112354
|
childId: msg.childId,
|
|
111720
|
-
caps: msg.caps
|
|
112355
|
+
caps: msg.caps,
|
|
112356
|
+
incarnation
|
|
111721
112357
|
});
|
|
111722
112358
|
return { ok: true };
|
|
111723
112359
|
}
|
|
@@ -111752,11 +112388,21 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111752
112388
|
throw new Error(`unknown child request kind: ${msg.kind}`);
|
|
111753
112389
|
});
|
|
111754
112390
|
channel.onClose(() => {
|
|
111755
|
-
if (childId
|
|
111756
|
-
|
|
111757
|
-
|
|
111758
|
-
|
|
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;
|
|
111759
112401
|
}
|
|
112402
|
+
this.children.delete(childId);
|
|
112403
|
+
this.childEventStats.delete(childId);
|
|
112404
|
+
this.loggedPatternSet.delete(childId);
|
|
112405
|
+
this.goneHandler(childId, incarnation);
|
|
111760
112406
|
});
|
|
111761
112407
|
}
|
|
111762
112408
|
};
|
|
@@ -118114,7 +118760,7 @@ var require_dist3 = __commonJS({
|
|
|
118114
118760
|
"use strict";
|
|
118115
118761
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
118116
118762
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
118117
|
-
var require_dist10 =
|
|
118763
|
+
var require_dist10 = require_dist_B2CtwVW2();
|
|
118118
118764
|
var require_builtins_alerts_alerts_addon = require_alerts_addon();
|
|
118119
118765
|
require_alerts();
|
|
118120
118766
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
@@ -118139,7 +118785,7 @@ var require_dist3 = __commonJS({
|
|
|
118139
118785
|
require_system_config();
|
|
118140
118786
|
var require_builtins_winston_logging_index = require_winston_logging();
|
|
118141
118787
|
var require_file_data_plane = require_file_data_plane_DUHPHa_Y();
|
|
118142
|
-
var require_manifest_python_deps =
|
|
118788
|
+
var require_manifest_python_deps = require_manifest_python_deps_MA_BZGBz();
|
|
118143
118789
|
var require_resource_monitor = require_resource_monitor_CdnzxBLP();
|
|
118144
118790
|
var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
|
|
118145
118791
|
var zod = require_zod();
|
|
@@ -120035,13 +120681,9 @@ var require_dist3 = __commonJS({
|
|
|
120035
120681
|
return {};
|
|
120036
120682
|
}
|
|
120037
120683
|
var integrationCounter = 0;
|
|
120038
|
-
var deviceCounter = 0;
|
|
120039
120684
|
function nextIntegrationId() {
|
|
120040
120685
|
return `int_${String(++integrationCounter).padStart(4, "0")}`;
|
|
120041
120686
|
}
|
|
120042
|
-
function nextDeviceId() {
|
|
120043
|
-
return `dev_${String(++deviceCounter).padStart(4, "0")}`;
|
|
120044
|
-
}
|
|
120045
120687
|
var INTEGRATIONS_COLUMNS = [
|
|
120046
120688
|
{
|
|
120047
120689
|
name: "id",
|
|
@@ -120108,99 +120750,8 @@ var require_dist3 = __commonJS({
|
|
|
120108
120750
|
defaultValue: "string"
|
|
120109
120751
|
}
|
|
120110
120752
|
];
|
|
120111
|
-
var DEVICES_COLUMNS = [
|
|
120112
|
-
{
|
|
120113
|
-
name: "id",
|
|
120114
|
-
type: "TEXT",
|
|
120115
|
-
primaryKey: true
|
|
120116
|
-
},
|
|
120117
|
-
{
|
|
120118
|
-
name: "integration_id",
|
|
120119
|
-
type: "TEXT",
|
|
120120
|
-
notNull: true
|
|
120121
|
-
},
|
|
120122
|
-
{
|
|
120123
|
-
name: "stable_id",
|
|
120124
|
-
type: "TEXT",
|
|
120125
|
-
notNull: true,
|
|
120126
|
-
unique: true
|
|
120127
|
-
},
|
|
120128
|
-
{
|
|
120129
|
-
name: "type",
|
|
120130
|
-
type: "TEXT",
|
|
120131
|
-
notNull: true,
|
|
120132
|
-
defaultValue: "camera"
|
|
120133
|
-
},
|
|
120134
|
-
{
|
|
120135
|
-
name: "name",
|
|
120136
|
-
type: "TEXT",
|
|
120137
|
-
notNull: true
|
|
120138
|
-
},
|
|
120139
|
-
{
|
|
120140
|
-
name: "enabled",
|
|
120141
|
-
type: "INTEGER",
|
|
120142
|
-
notNull: true,
|
|
120143
|
-
defaultValue: 1
|
|
120144
|
-
},
|
|
120145
|
-
{
|
|
120146
|
-
name: "info",
|
|
120147
|
-
type: "JSON",
|
|
120148
|
-
notNull: true,
|
|
120149
|
-
defaultValue: "{}"
|
|
120150
|
-
},
|
|
120151
|
-
{
|
|
120152
|
-
name: "created_at",
|
|
120153
|
-
type: "INTEGER",
|
|
120154
|
-
notNull: true
|
|
120155
|
-
},
|
|
120156
|
-
{
|
|
120157
|
-
name: "updated_at",
|
|
120158
|
-
type: "INTEGER",
|
|
120159
|
-
notNull: true
|
|
120160
|
-
}
|
|
120161
|
-
];
|
|
120162
|
-
var DEVICES_INDEXES = [
|
|
120163
|
-
{
|
|
120164
|
-
name: "idx_devices_integration",
|
|
120165
|
-
columns: ["integration_id"]
|
|
120166
|
-
},
|
|
120167
|
-
{
|
|
120168
|
-
name: "idx_devices_stable",
|
|
120169
|
-
columns: ["stable_id"],
|
|
120170
|
-
unique: true
|
|
120171
|
-
},
|
|
120172
|
-
{
|
|
120173
|
-
name: "idx_devices_type",
|
|
120174
|
-
columns: ["type"]
|
|
120175
|
-
}
|
|
120176
|
-
];
|
|
120177
|
-
var DEVICE_SETTINGS_COLUMNS = [
|
|
120178
|
-
{
|
|
120179
|
-
name: "device_id",
|
|
120180
|
-
type: "TEXT",
|
|
120181
|
-
notNull: true
|
|
120182
|
-
},
|
|
120183
|
-
{
|
|
120184
|
-
name: "key",
|
|
120185
|
-
type: "TEXT",
|
|
120186
|
-
notNull: true
|
|
120187
|
-
},
|
|
120188
|
-
{
|
|
120189
|
-
name: "value",
|
|
120190
|
-
type: "TEXT",
|
|
120191
|
-
notNull: true
|
|
120192
|
-
},
|
|
120193
|
-
{
|
|
120194
|
-
name: "value_type",
|
|
120195
|
-
type: "TEXT",
|
|
120196
|
-
notNull: true,
|
|
120197
|
-
defaultValue: "string"
|
|
120198
|
-
}
|
|
120199
|
-
];
|
|
120200
120753
|
var INTEGRATIONS = "integrations";
|
|
120201
120754
|
var INTEGRATION_SETTINGS = "integration_settings";
|
|
120202
|
-
var DEVICES = "devices";
|
|
120203
|
-
var DEVICE_SETTINGS = "device_settings_kv";
|
|
120204
120755
|
var IntegrationRegistry = class IntegrationRegistry2 {
|
|
120205
120756
|
store;
|
|
120206
120757
|
constructor(backend) {
|
|
@@ -120216,15 +120767,6 @@ var require_dist3 = __commonJS({
|
|
|
120216
120767
|
collection: INTEGRATION_SETTINGS,
|
|
120217
120768
|
columns: INTEGRATION_SETTINGS_COLUMNS
|
|
120218
120769
|
});
|
|
120219
|
-
await this.store.declareCollection({
|
|
120220
|
-
collection: DEVICES,
|
|
120221
|
-
columns: DEVICES_COLUMNS,
|
|
120222
|
-
indexes: DEVICES_INDEXES
|
|
120223
|
-
});
|
|
120224
|
-
await this.store.declareCollection({
|
|
120225
|
-
collection: DEVICE_SETTINGS,
|
|
120226
|
-
columns: DEVICE_SETTINGS_COLUMNS
|
|
120227
|
-
});
|
|
120228
120770
|
const ints = await this.store.query({
|
|
120229
120771
|
collection: INTEGRATIONS,
|
|
120230
120772
|
filter: {
|
|
@@ -120239,20 +120781,6 @@ var require_dist3 = __commonJS({
|
|
|
120239
120781
|
const num = parseInt(ints[0].id.replace("int_", ""), 10);
|
|
120240
120782
|
if (!isNaN(num)) integrationCounter = num;
|
|
120241
120783
|
}
|
|
120242
|
-
const devs = await this.store.query({
|
|
120243
|
-
collection: DEVICES,
|
|
120244
|
-
filter: {
|
|
120245
|
-
orderBy: {
|
|
120246
|
-
field: "id",
|
|
120247
|
-
direction: "desc"
|
|
120248
|
-
},
|
|
120249
|
-
limit: 1
|
|
120250
|
-
}
|
|
120251
|
-
});
|
|
120252
|
-
if (devs[0]) {
|
|
120253
|
-
const num = parseInt(devs[0].id.replace("dev_", ""), 10);
|
|
120254
|
-
if (!isNaN(num)) deviceCounter = num;
|
|
120255
|
-
}
|
|
120256
120784
|
}
|
|
120257
120785
|
async createIntegration(input) {
|
|
120258
120786
|
const id = nextIntegrationId();
|
|
@@ -120336,15 +120864,6 @@ var require_dist3 = __commonJS({
|
|
|
120336
120864
|
async deleteIntegration(id) {
|
|
120337
120865
|
const integration = await this.getIntegration(id);
|
|
120338
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`);
|
|
120339
|
-
const devices = await this.listDevices(id);
|
|
120340
|
-
for (const d of devices) await this.store.deleteWhere({
|
|
120341
|
-
collection: DEVICE_SETTINGS,
|
|
120342
|
-
filter: { where: { device_id: d.id } }
|
|
120343
|
-
});
|
|
120344
|
-
await this.store.deleteWhere({
|
|
120345
|
-
collection: DEVICES,
|
|
120346
|
-
filter: { where: { integration_id: id } }
|
|
120347
|
-
});
|
|
120348
120867
|
await this.store.deleteWhere({
|
|
120349
120868
|
collection: INTEGRATION_SETTINGS,
|
|
120350
120869
|
filter: { where: { integration_id: id } }
|
|
@@ -120389,139 +120908,6 @@ var require_dist3 = __commonJS({
|
|
|
120389
120908
|
async setIntegrationSettings(integrationId, settings) {
|
|
120390
120909
|
for (const [key, value] of Object.entries(settings)) await this.setIntegrationSetting(integrationId, key, value);
|
|
120391
120910
|
}
|
|
120392
|
-
async createDevice(input) {
|
|
120393
|
-
const id = nextDeviceId();
|
|
120394
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
120395
|
-
await this.store.insert({
|
|
120396
|
-
collection: DEVICES,
|
|
120397
|
-
record: {
|
|
120398
|
-
id,
|
|
120399
|
-
data: {
|
|
120400
|
-
integration_id: input.integrationId,
|
|
120401
|
-
stable_id: input.stableId,
|
|
120402
|
-
type: input.type,
|
|
120403
|
-
name: input.name,
|
|
120404
|
-
enabled: input.enabled !== false ? 1 : 0,
|
|
120405
|
-
info: input.info ?? {},
|
|
120406
|
-
created_at: now,
|
|
120407
|
-
updated_at: now
|
|
120408
|
-
}
|
|
120409
|
-
}
|
|
120410
|
-
});
|
|
120411
|
-
if (input.settings) await this.setDeviceSettings(id, input.settings);
|
|
120412
|
-
return {
|
|
120413
|
-
id,
|
|
120414
|
-
integrationId: input.integrationId,
|
|
120415
|
-
stableId: input.stableId,
|
|
120416
|
-
type: input.type,
|
|
120417
|
-
name: input.name,
|
|
120418
|
-
enabled: input.enabled !== false,
|
|
120419
|
-
info: input.info ?? {},
|
|
120420
|
-
createdAt: now,
|
|
120421
|
-
updatedAt: now
|
|
120422
|
-
};
|
|
120423
|
-
}
|
|
120424
|
-
async getDevice(id) {
|
|
120425
|
-
const rows = await this.store.query({
|
|
120426
|
-
collection: DEVICES,
|
|
120427
|
-
filter: {
|
|
120428
|
-
where: { id },
|
|
120429
|
-
limit: 1
|
|
120430
|
-
}
|
|
120431
|
-
});
|
|
120432
|
-
return rows[0] ? this.mapDevice(rows[0].data) : null;
|
|
120433
|
-
}
|
|
120434
|
-
async getDeviceByStableId(stableId) {
|
|
120435
|
-
const rows = await this.store.query({
|
|
120436
|
-
collection: DEVICES,
|
|
120437
|
-
filter: {
|
|
120438
|
-
where: { stable_id: stableId },
|
|
120439
|
-
limit: 1
|
|
120440
|
-
}
|
|
120441
|
-
});
|
|
120442
|
-
return rows[0] ? this.mapDevice(rows[0].data) : null;
|
|
120443
|
-
}
|
|
120444
|
-
async listDevices(integrationId) {
|
|
120445
|
-
return (await this.store.query({
|
|
120446
|
-
collection: DEVICES,
|
|
120447
|
-
filter: {
|
|
120448
|
-
...integrationId ? { where: { integration_id: integrationId } } : {},
|
|
120449
|
-
orderBy: {
|
|
120450
|
-
field: "created_at",
|
|
120451
|
-
direction: "asc"
|
|
120452
|
-
}
|
|
120453
|
-
}
|
|
120454
|
-
})).map((r) => this.mapDevice(r.data));
|
|
120455
|
-
}
|
|
120456
|
-
async listCameras() {
|
|
120457
|
-
return (await this.store.query({
|
|
120458
|
-
collection: DEVICES,
|
|
120459
|
-
filter: {
|
|
120460
|
-
where: { type: "camera" },
|
|
120461
|
-
orderBy: {
|
|
120462
|
-
field: "created_at",
|
|
120463
|
-
direction: "asc"
|
|
120464
|
-
}
|
|
120465
|
-
}
|
|
120466
|
-
})).map((r) => this.mapDevice(r.data));
|
|
120467
|
-
}
|
|
120468
|
-
async updateDevice(id, updates) {
|
|
120469
|
-
const updateRow = { updated_at: Math.floor(Date.now() / 1e3) };
|
|
120470
|
-
if (updates.name !== void 0) updateRow["name"] = updates.name;
|
|
120471
|
-
if (updates.enabled !== void 0) updateRow["enabled"] = updates.enabled ? 1 : 0;
|
|
120472
|
-
if (updates.info !== void 0) updateRow["info"] = updates.info;
|
|
120473
|
-
await this.store.updateWhere({
|
|
120474
|
-
collection: DEVICES,
|
|
120475
|
-
filter: { where: { id } },
|
|
120476
|
-
data: updateRow
|
|
120477
|
-
});
|
|
120478
|
-
return this.getDevice(id);
|
|
120479
|
-
}
|
|
120480
|
-
async deleteDevice(id) {
|
|
120481
|
-
await this.store.deleteWhere({
|
|
120482
|
-
collection: DEVICE_SETTINGS,
|
|
120483
|
-
filter: { where: { device_id: id } }
|
|
120484
|
-
});
|
|
120485
|
-
await this.store.deleteWhere({
|
|
120486
|
-
collection: DEVICES,
|
|
120487
|
-
filter: { where: { id } }
|
|
120488
|
-
});
|
|
120489
|
-
return true;
|
|
120490
|
-
}
|
|
120491
|
-
async getDeviceSettings(deviceId) {
|
|
120492
|
-
const result = {};
|
|
120493
|
-
const rows = await this.store.query({
|
|
120494
|
-
collection: DEVICE_SETTINGS,
|
|
120495
|
-
filter: { where: { device_id: deviceId } }
|
|
120496
|
-
});
|
|
120497
|
-
for (const row of rows) result[String(row.data["key"])] = deserializeSetting(row.data["value"], String(row.data["value_type"]));
|
|
120498
|
-
return result;
|
|
120499
|
-
}
|
|
120500
|
-
async setDeviceSetting(deviceId, key, value) {
|
|
120501
|
-
const s = serializeSetting(value);
|
|
120502
|
-
await this.store.deleteWhere({
|
|
120503
|
-
collection: DEVICE_SETTINGS,
|
|
120504
|
-
filter: { where: {
|
|
120505
|
-
device_id: deviceId,
|
|
120506
|
-
key
|
|
120507
|
-
} }
|
|
120508
|
-
});
|
|
120509
|
-
await this.store.insert({
|
|
120510
|
-
collection: DEVICE_SETTINGS,
|
|
120511
|
-
record: {
|
|
120512
|
-
id: `${deviceId}:${key}`,
|
|
120513
|
-
data: {
|
|
120514
|
-
device_id: deviceId,
|
|
120515
|
-
key,
|
|
120516
|
-
value: s.value,
|
|
120517
|
-
value_type: s.valueType
|
|
120518
|
-
}
|
|
120519
|
-
}
|
|
120520
|
-
});
|
|
120521
|
-
}
|
|
120522
|
-
async setDeviceSettings(deviceId, settings) {
|
|
120523
|
-
for (const [key, value] of Object.entries(settings)) await this.setDeviceSetting(deviceId, key, value);
|
|
120524
|
-
}
|
|
120525
120911
|
mapIntegration(row) {
|
|
120526
120912
|
return {
|
|
120527
120913
|
id: String(row["id"]),
|
|
@@ -120533,19 +120919,6 @@ var require_dist3 = __commonJS({
|
|
|
120533
120919
|
updatedAt: Number(row["updated_at"])
|
|
120534
120920
|
};
|
|
120535
120921
|
}
|
|
120536
|
-
mapDevice(row) {
|
|
120537
|
-
return {
|
|
120538
|
-
id: String(row["id"]),
|
|
120539
|
-
integrationId: String(row["integration_id"]),
|
|
120540
|
-
stableId: String(row["stable_id"]),
|
|
120541
|
-
type: String(row["type"]),
|
|
120542
|
-
name: String(row["name"]),
|
|
120543
|
-
enabled: row["enabled"] === 1,
|
|
120544
|
-
info: readInfo(row["info"]),
|
|
120545
|
-
createdAt: Number(row["created_at"]),
|
|
120546
|
-
updatedAt: Number(row["updated_at"])
|
|
120547
|
-
};
|
|
120548
|
-
}
|
|
120549
120922
|
};
|
|
120550
120923
|
var execFileAsync$2 = (0, node_util.promisify)(node_child_process.execFile);
|
|
120551
120924
|
var NATIVE_SCAN_DEPTH = 4;
|
|
@@ -123039,6 +123412,7 @@ var require_dist3 = __commonJS({
|
|
|
123039
123412
|
const state = {
|
|
123040
123413
|
definition,
|
|
123041
123414
|
providers: /* @__PURE__ */ new Map(),
|
|
123415
|
+
providerOwners: /* @__PURE__ */ new Map(),
|
|
123042
123416
|
activeAddonId: null,
|
|
123043
123417
|
router: null,
|
|
123044
123418
|
disabledProviders: /* @__PURE__ */ new Set()
|
|
@@ -123096,8 +123470,15 @@ var require_dist3 = __commonJS({
|
|
|
123096
123470
|
* Double-register of the same pair is always a programmer error —
|
|
123097
123471
|
* two legitimately different addons implementing the same cap keep
|
|
123098
123472
|
* using different `addonId`s and the user picks one via `configReader`.
|
|
123099
|
-
|
|
123100
|
-
|
|
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) {
|
|
123101
123482
|
let state = this.capabilities.get(capabilityName);
|
|
123102
123483
|
if (!state) {
|
|
123103
123484
|
const colonIdx = capabilityName.indexOf(":");
|
|
@@ -123129,6 +123510,7 @@ var require_dist3 = __commonJS({
|
|
|
123129
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).`);
|
|
123130
123511
|
}
|
|
123131
123512
|
state.providers.set(addonId, provider);
|
|
123513
|
+
if (owner !== void 0) state.providerOwners.set(addonId, owner);
|
|
123132
123514
|
if (state.definition.mode === "singleton") {
|
|
123133
123515
|
const userChoice = this.configReader?.(capabilityName);
|
|
123134
123516
|
const preferred = state.definition.preferredProvider;
|
|
@@ -123159,7 +123541,8 @@ var require_dist3 = __commonJS({
|
|
|
123159
123541
|
tags: { addonId },
|
|
123160
123542
|
meta: {
|
|
123161
123543
|
capability: capabilityName,
|
|
123162
|
-
mode: state.definition.mode
|
|
123544
|
+
mode: state.definition.mode,
|
|
123545
|
+
...owner === void 0 ? {} : { owner }
|
|
123163
123546
|
}
|
|
123164
123547
|
});
|
|
123165
123548
|
this.emitEvent("capability:provider-registered", {
|
|
@@ -123206,11 +123589,70 @@ var require_dist3 = __commonJS({
|
|
|
123206
123589
|
const declarers = this.manifestDeclarers.get(capabilityName);
|
|
123207
123590
|
return declarers ? [...declarers] : [];
|
|
123208
123591
|
}
|
|
123209
|
-
/**
|
|
123210
|
-
|
|
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) {
|
|
123211
123640
|
const state = this.capabilities.get(capabilityName);
|
|
123212
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
|
+
}
|
|
123213
123654
|
state.providers.delete(addonId);
|
|
123655
|
+
state.providerOwners.delete(addonId);
|
|
123214
123656
|
if (state.definition.mode === "singleton" && state.activeAddonId === addonId) {
|
|
123215
123657
|
const userChoice = this.configReader?.(capabilityName);
|
|
123216
123658
|
const preferred = state.definition.preferredProvider;
|
|
@@ -198184,6 +198626,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198184
198626
|
function safeSegment(name2) {
|
|
198185
198627
|
return name2.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
198186
198628
|
}
|
|
198629
|
+
var EXACT_SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
198187
198630
|
var StagingArea = class {
|
|
198188
198631
|
stagingDir;
|
|
198189
198632
|
deps;
|
|
@@ -198201,8 +198644,12 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198201
198644
|
if (input.signal.aborted) throw new Error("staging aborted after fetch");
|
|
198202
198645
|
await this.deps.extract(tgz, destDir);
|
|
198203
198646
|
const raw = JSON.parse(node_fs.readFileSync(node_path.join(destDir, "package.json"), "utf-8"));
|
|
198204
|
-
if (raw.version !==
|
|
198205
|
-
|
|
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
|
+
};
|
|
198206
198653
|
}
|
|
198207
198654
|
cleanup(jobId) {
|
|
198208
198655
|
node_fs.rmSync(node_path.join(this.stagingDir, jobId), {
|
|
@@ -238360,7 +238807,7 @@ var require_dist9 = __commonJS({
|
|
|
238360
238807
|
postBufferSec: zod.z.number().min(0).optional()
|
|
238361
238808
|
});
|
|
238362
238809
|
var DEFAULT_EVENTS_BAND_BUFFER_SEC = {
|
|
238363
|
-
preBufferSec:
|
|
238810
|
+
preBufferSec: 15,
|
|
238364
238811
|
postBufferSec: 30
|
|
238365
238812
|
};
|
|
238366
238813
|
var VISIT_MERGE_GAP_MS = DEFAULT_EVENTS_BAND_BUFFER_SEC.postBufferSec * 1e3;
|
|
@@ -238974,12 +239421,17 @@ var require_dist9 = __commonJS({
|
|
|
238974
239421
|
"tflite",
|
|
238975
239422
|
"pt"
|
|
238976
239423
|
];
|
|
239424
|
+
var FAMILY_LABEL = {
|
|
239425
|
+
yolo26: "YOLO26",
|
|
239426
|
+
yolov9: "YOLOv9"
|
|
239427
|
+
};
|
|
238977
239428
|
function familyLabel(family) {
|
|
238978
|
-
return family.toUpperCase();
|
|
239429
|
+
return FAMILY_LABEL[family] ?? family.toUpperCase();
|
|
238979
239430
|
}
|
|
239431
|
+
var RESOLUTION_IN_NAME = /\s*@\s*\d+\s*(?:[x×]\s*\d+)?/gi;
|
|
238980
239432
|
function tierLabel(family, baseName) {
|
|
238981
239433
|
const fl = familyLabel(family);
|
|
238982
|
-
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();
|
|
238983
239435
|
return stripped.length > 0 ? stripped : baseName;
|
|
238984
239436
|
}
|
|
238985
239437
|
function variantLabel(precision, optimization) {
|
|
@@ -238996,9 +239448,11 @@ var require_dist9 = __commonJS({
|
|
|
238996
239448
|
return sizes.length > 0 ? Math.min(...sizes) : 0;
|
|
238997
239449
|
}
|
|
238998
239450
|
var TIER_ORDER = [
|
|
239451
|
+
"t",
|
|
238999
239452
|
"n",
|
|
239000
239453
|
"s",
|
|
239001
239454
|
"m",
|
|
239455
|
+
"c",
|
|
239002
239456
|
"l",
|
|
239003
239457
|
"x"
|
|
239004
239458
|
];
|
|
@@ -239035,7 +239489,7 @@ var require_dist9 = __commonJS({
|
|
|
239035
239489
|
if (a.optimization !== b.optimization) return a.optimization === "fast" ? 1 : -1;
|
|
239036
239490
|
return PRECISION_ORDER.indexOf(a.precision) - PRECISION_ORDER.indexOf(b.precision);
|
|
239037
239491
|
});
|
|
239038
|
-
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];
|
|
239039
239493
|
if (base === void 0) continue;
|
|
239040
239494
|
const baseEntry = entries2.find((e) => e.id === base.modelId);
|
|
239041
239495
|
tierList.push({
|
|
@@ -239065,15 +239519,9 @@ var require_dist9 = __commonJS({
|
|
|
239065
239519
|
function resolveVariantModelId(models, selection) {
|
|
239066
239520
|
const precision = selection.precision ?? "fp32";
|
|
239067
239521
|
const optimization = selection.optimization ?? "standard";
|
|
239068
|
-
|
|
239069
|
-
|
|
239070
|
-
|
|
239071
|
-
if ((m.group.precision ?? "fp32") !== precision) continue;
|
|
239072
|
-
if ((m.group.optimization ?? "standard") !== optimization) continue;
|
|
239073
|
-
if (m.group.resolution !== selection.resolution) continue;
|
|
239074
|
-
return m.id;
|
|
239075
|
-
}
|
|
239076
|
-
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;
|
|
239077
239525
|
}
|
|
239078
239526
|
function describeModelVariant(models, modelId) {
|
|
239079
239527
|
const entry = models.find((m) => m.id === modelId);
|
|
@@ -240480,11 +240928,15 @@ export default function block(ctx: AddonContext) {
|
|
|
240480
240928
|
): Promise<void> {
|
|
240481
240929
|
const resolved: Record<string, unknown> = {}
|
|
240482
240930
|
for (const [k, v] of Object.entries(args)) resolved[k] = substitute(v, vars)
|
|
240483
|
-
const client = (ctx.api
|
|
240484
|
-
|
|
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') {
|
|
240485
240937
|
throw new Error('no such device method: ' + cap + '.' + method)
|
|
240486
240938
|
}
|
|
240487
|
-
await
|
|
240939
|
+
await mutate({ ...resolved, deviceId })
|
|
240488
240940
|
}
|
|
240489
240941
|
|
|
240490
240942
|
async function evaluateConditions(): Promise<boolean> {
|
|
@@ -246331,128 +246783,103 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
246331
246783
|
var AUDIO_MACRO_LABELS = [
|
|
246332
246784
|
{
|
|
246333
246785
|
id: "speech",
|
|
246334
|
-
name: "Speech"
|
|
246335
|
-
icon: "\u{1F5E3}\uFE0F"
|
|
246786
|
+
name: "Speech"
|
|
246336
246787
|
},
|
|
246337
246788
|
{
|
|
246338
246789
|
id: "scream",
|
|
246339
|
-
name: "Scream / Shout"
|
|
246340
|
-
icon: "\u{1F631}"
|
|
246790
|
+
name: "Scream / Shout"
|
|
246341
246791
|
},
|
|
246342
246792
|
{
|
|
246343
246793
|
id: "crying",
|
|
246344
|
-
name: "Crying / Baby"
|
|
246345
|
-
icon: "\u{1F622}"
|
|
246794
|
+
name: "Crying / Baby"
|
|
246346
246795
|
},
|
|
246347
246796
|
{
|
|
246348
246797
|
id: "laughter",
|
|
246349
|
-
name: "Laughter"
|
|
246350
|
-
icon: "\u{1F602}"
|
|
246798
|
+
name: "Laughter"
|
|
246351
246799
|
},
|
|
246352
246800
|
{
|
|
246353
246801
|
id: "music",
|
|
246354
|
-
name: "Music"
|
|
246355
|
-
icon: "\u{1F3B5}"
|
|
246802
|
+
name: "Music"
|
|
246356
246803
|
},
|
|
246357
246804
|
{
|
|
246358
246805
|
id: "dog",
|
|
246359
|
-
name: "Dog"
|
|
246360
|
-
icon: "\u{1F415}"
|
|
246806
|
+
name: "Dog"
|
|
246361
246807
|
},
|
|
246362
246808
|
{
|
|
246363
246809
|
id: "cat",
|
|
246364
|
-
name: "Cat"
|
|
246365
|
-
icon: "\u{1F408}"
|
|
246810
|
+
name: "Cat"
|
|
246366
246811
|
},
|
|
246367
246812
|
{
|
|
246368
246813
|
id: "bird",
|
|
246369
|
-
name: "Bird"
|
|
246370
|
-
icon: "\u{1F426}"
|
|
246814
|
+
name: "Bird"
|
|
246371
246815
|
},
|
|
246372
246816
|
{
|
|
246373
246817
|
id: "animal",
|
|
246374
|
-
name: "Animal (other)"
|
|
246375
|
-
icon: "\u{1F43E}"
|
|
246818
|
+
name: "Animal (other)"
|
|
246376
246819
|
},
|
|
246377
246820
|
{
|
|
246378
246821
|
id: "alarm",
|
|
246379
|
-
name: "Alarm / Siren"
|
|
246380
|
-
icon: "\u{1F6A8}"
|
|
246822
|
+
name: "Alarm / Siren"
|
|
246381
246823
|
},
|
|
246382
246824
|
{
|
|
246383
246825
|
id: "doorbell",
|
|
246384
|
-
name: "Doorbell / Knock"
|
|
246385
|
-
icon: "\u{1F514}"
|
|
246826
|
+
name: "Doorbell / Knock"
|
|
246386
246827
|
},
|
|
246387
246828
|
{
|
|
246388
246829
|
id: "glass_breaking",
|
|
246389
|
-
name: "Glass Breaking"
|
|
246390
|
-
icon: "\u{1F4A5}"
|
|
246830
|
+
name: "Glass Breaking"
|
|
246391
246831
|
},
|
|
246392
246832
|
{
|
|
246393
246833
|
id: "gunshot",
|
|
246394
|
-
name: "Gunshot / Explosion"
|
|
246395
|
-
icon: "\u{1F4A3}"
|
|
246834
|
+
name: "Gunshot / Explosion"
|
|
246396
246835
|
},
|
|
246397
246836
|
{
|
|
246398
246837
|
id: "vehicle",
|
|
246399
|
-
name: "Vehicle"
|
|
246400
|
-
icon: "\u{1F697}"
|
|
246838
|
+
name: "Vehicle"
|
|
246401
246839
|
},
|
|
246402
246840
|
{
|
|
246403
246841
|
id: "siren",
|
|
246404
|
-
name: "Emergency Siren"
|
|
246405
|
-
icon: "\u{1F691}"
|
|
246842
|
+
name: "Emergency Siren"
|
|
246406
246843
|
},
|
|
246407
246844
|
{
|
|
246408
246845
|
id: "fire",
|
|
246409
|
-
name: "Fire / Smoke"
|
|
246410
|
-
icon: "\u{1F525}"
|
|
246846
|
+
name: "Fire / Smoke"
|
|
246411
246847
|
},
|
|
246412
246848
|
{
|
|
246413
246849
|
id: "water",
|
|
246414
|
-
name: "Water"
|
|
246415
|
-
icon: "\u{1F4A7}"
|
|
246850
|
+
name: "Water"
|
|
246416
246851
|
},
|
|
246417
246852
|
{
|
|
246418
246853
|
id: "wind",
|
|
246419
|
-
name: "Wind / Weather"
|
|
246420
|
-
icon: "\u{1F32C}\uFE0F"
|
|
246854
|
+
name: "Wind / Weather"
|
|
246421
246855
|
},
|
|
246422
246856
|
{
|
|
246423
246857
|
id: "door",
|
|
246424
|
-
name: "Door"
|
|
246425
|
-
icon: "\u{1F6AA}"
|
|
246858
|
+
name: "Door"
|
|
246426
246859
|
},
|
|
246427
246860
|
{
|
|
246428
246861
|
id: "footsteps",
|
|
246429
|
-
name: "Footsteps"
|
|
246430
|
-
icon: "\u{1F463}"
|
|
246862
|
+
name: "Footsteps"
|
|
246431
246863
|
},
|
|
246432
246864
|
{
|
|
246433
246865
|
id: "crowd",
|
|
246434
|
-
name: "Crowd / Chatter"
|
|
246435
|
-
icon: "\u{1F465}"
|
|
246866
|
+
name: "Crowd / Chatter"
|
|
246436
246867
|
},
|
|
246437
246868
|
{
|
|
246438
246869
|
id: "telephone",
|
|
246439
|
-
name: "Telephone"
|
|
246440
|
-
icon: "\u{1F4DE}"
|
|
246870
|
+
name: "Telephone"
|
|
246441
246871
|
},
|
|
246442
246872
|
{
|
|
246443
246873
|
id: "engine",
|
|
246444
|
-
name: "Engine / Motor"
|
|
246445
|
-
icon: "\u2699\uFE0F"
|
|
246874
|
+
name: "Engine / Motor"
|
|
246446
246875
|
},
|
|
246447
246876
|
{
|
|
246448
246877
|
id: "tools",
|
|
246449
|
-
name: "Tools / Construction"
|
|
246450
|
-
icon: "\u{1F528}"
|
|
246878
|
+
name: "Tools / Construction"
|
|
246451
246879
|
},
|
|
246452
246880
|
{
|
|
246453
246881
|
id: "silence",
|
|
246454
|
-
name: "Silence"
|
|
246455
|
-
icon: "\u{1F92B}"
|
|
246882
|
+
name: "Silence"
|
|
246456
246883
|
}
|
|
246457
246884
|
];
|
|
246458
246885
|
var YAMNET_TO_MACRO = {
|
|
@@ -249040,6 +249467,35 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
249040
249467
|
*/
|
|
249041
249468
|
hasFace: zod.z.boolean().optional(),
|
|
249042
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
|
+
/**
|
|
249043
249499
|
* This subject CONTAINS a folded rider — a person the rider-pairing step
|
|
249044
249500
|
* ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
|
|
249045
249501
|
* so the passage is tracked once and as a VEHICLE.
|
|
@@ -250800,6 +251256,32 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
250800
251256
|
/** FRAME-space bbox (already mapped back from crop space). */
|
|
250801
251257
|
bbox: NativeCropBboxSchema.optional(),
|
|
250802
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(),
|
|
250803
251285
|
label: zod.z.string().optional(),
|
|
250804
251286
|
/**
|
|
250805
251287
|
* The tier `label` occupies, copied VERBATIM from the producing step's
|
|
@@ -252624,6 +253106,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
252624
253106
|
isBattery: zod.z.boolean(),
|
|
252625
253107
|
reason: zod.z.enum([
|
|
252626
253108
|
"disabled",
|
|
253109
|
+
"offline",
|
|
252627
253110
|
"sleeping",
|
|
252628
253111
|
"unreachable",
|
|
252629
253112
|
"waking"
|
|
@@ -252714,9 +253197,17 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
252714
253197
|
sleeping: zod.z.boolean(),
|
|
252715
253198
|
/** Current device state rendered over the cached frame. State images
|
|
252716
253199
|
* remain authoritative even when their photographic background is
|
|
252717
|
-
* 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. */
|
|
252718
253208
|
stateReason: zod.z.enum([
|
|
252719
253209
|
"disabled",
|
|
253210
|
+
"offline",
|
|
252720
253211
|
"sleeping",
|
|
252721
253212
|
"unreachable",
|
|
252722
253213
|
"waking"
|
|
@@ -273706,6 +274197,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273706
274197
|
function normalizeAudioLabel(value) {
|
|
273707
274198
|
return value.startsWith(AUDIO_KIND_PREFIX) ? value.slice(6) : value;
|
|
273708
274199
|
}
|
|
274200
|
+
function audioKindId(macroId) {
|
|
274201
|
+
return macroId.startsWith(AUDIO_KIND_PREFIX) ? macroId : `${AUDIO_KIND_PREFIX}${macroId}`;
|
|
274202
|
+
}
|
|
273709
274203
|
var NC_AUDIO_DB_MIN = -96;
|
|
273710
274204
|
var NC_AUDIO_DB_MAX = 0;
|
|
273711
274205
|
var NC_AUDIO_DB_STEP = 3;
|
|
@@ -273753,8 +274247,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273753
274247
|
samplingSeconds: clampInt(patch.samplingSeconds ?? base.samplingSeconds, 1, 300)
|
|
273754
274248
|
};
|
|
273755
274249
|
}
|
|
273756
|
-
var UNKNOWN_LABEL_ICON = "\u{1F508}";
|
|
273757
|
-
var ICON_BY_ID = new Map(AUDIO_MACRO_LABELS.flatMap((macro2) => macro2.icon === void 0 ? [] : [[macro2.id, macro2.icon]]));
|
|
273758
274250
|
function audioLabelChoices(taxonomy, selected) {
|
|
273759
274251
|
const fromHub = taxonomy?.audioKinds ?? [];
|
|
273760
274252
|
const choices = fromHub.length > 0 ? fromHub.map((entry) => {
|
|
@@ -273762,13 +274254,13 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273762
274254
|
return {
|
|
273763
274255
|
value: id,
|
|
273764
274256
|
label: entry.label,
|
|
273765
|
-
|
|
274257
|
+
iconId: audioKindId(id),
|
|
273766
274258
|
unknown: false
|
|
273767
274259
|
};
|
|
273768
274260
|
}) : AUDIO_MACRO_LABELS.map((macro2) => ({
|
|
273769
274261
|
value: macro2.id,
|
|
273770
274262
|
label: macro2.name,
|
|
273771
|
-
|
|
274263
|
+
iconId: audioKindId(macro2.id),
|
|
273772
274264
|
unknown: false
|
|
273773
274265
|
}));
|
|
273774
274266
|
const known = new Set(choices.map((choice) => choice.value));
|
|
@@ -273780,7 +274272,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273780
274272
|
choices.push({
|
|
273781
274273
|
value: id,
|
|
273782
274274
|
label: id,
|
|
273783
|
-
|
|
274275
|
+
iconId: audioKindId(id),
|
|
273784
274276
|
unknown: true
|
|
273785
274277
|
});
|
|
273786
274278
|
}
|
|
@@ -274504,6 +274996,74 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
274504
274996
|
if (map !== void 0) return map[String(deviceId)] ?? 0;
|
|
274505
274997
|
return rule.lastGeneratedAt ?? 0;
|
|
274506
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
|
+
}
|
|
274507
275067
|
var DETAIL_CROP_SECTION_ID = "detail-crop";
|
|
274508
275068
|
var DETAIL_CROP_PADDING_KEY = "detailCropPaddingRatio";
|
|
274509
275069
|
var DETAIL_CROP_SQUARE_KEY = "detailCropSquare";
|
|
@@ -274551,6 +275111,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
274551
275111
|
}
|
|
274552
275112
|
return readDetailCropConvention(flat);
|
|
274553
275113
|
}
|
|
275114
|
+
var FULL_IMAGE_BBOX = Object.freeze({
|
|
275115
|
+
x: 0,
|
|
275116
|
+
y: 0,
|
|
275117
|
+
w: 1,
|
|
275118
|
+
h: 1
|
|
275119
|
+
});
|
|
274554
275120
|
var DETAIL_CROP_PADDING_FIELD = {
|
|
274555
275121
|
min: 0,
|
|
274556
275122
|
max: 1,
|
|
@@ -276286,6 +276852,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276286
276852
|
exports.CAP_NAMES_WITH_STATUS = CAP_NAMES_WITH_STATUS;
|
|
276287
276853
|
exports.CAP_NODE_PIN_CONTEXT_KEY = require_sleep.CAP_NODE_PIN_CONTEXT_KEY;
|
|
276288
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;
|
|
276289
276857
|
exports.COCO_80_LABELS = COCO_80_LABELS;
|
|
276290
276858
|
exports.COCO_TO_MACRO = COCO_TO_MACRO;
|
|
276291
276859
|
exports.CONNECTION_TEST_TIMEOUT_MS = CONNECTION_TEST_TIMEOUT_MS;
|
|
@@ -276373,6 +276941,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276373
276941
|
exports.DECLARED_INTEGRATION_FIXED_KEY = DECLARED_INTEGRATION_FIXED_KEY;
|
|
276374
276942
|
exports.DEFAULT_ADDON_PLACEMENT = DEFAULT_ADDON_PLACEMENT;
|
|
276375
276943
|
exports.DEFAULT_AUDIO_ANALYZER_CONFIG = DEFAULT_AUDIO_ANALYZER_CONFIG;
|
|
276944
|
+
exports.DEFAULT_CLUSTER_STEP_MODELS = DEFAULT_CLUSTER_STEP_MODELS;
|
|
276376
276945
|
exports.DEFAULT_DECODER_HWACCEL_CONFIG = DEFAULT_DECODER_HWACCEL_CONFIG;
|
|
276377
276946
|
exports.DEFAULT_DETAIL_CROP_CONVENTION = DEFAULT_DETAIL_CROP_CONVENTION;
|
|
276378
276947
|
exports.DEFAULT_EVENTS_BAND_BUFFER_SEC = DEFAULT_EVENTS_BAND_BUFFER_SEC;
|
|
@@ -276491,6 +277060,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276491
277060
|
exports.ExpressionLiteralBindingSchema = ExpressionLiteralBindingSchema;
|
|
276492
277061
|
exports.ExpressionParseError = ExpressionParseError;
|
|
276493
277062
|
exports.ExpressionSourceSchema = ExpressionSourceSchema;
|
|
277063
|
+
exports.FULL_IMAGE_BBOX = FULL_IMAGE_BBOX;
|
|
276494
277064
|
exports.FanControlStatusSchema = FanControlStatusSchema;
|
|
276495
277065
|
exports.FanDirectionSchema = FanDirectionSchema;
|
|
276496
277066
|
exports.FeatureManifestSchema = FeatureManifestSchema;
|
|
@@ -277110,6 +277680,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277110
277680
|
exports.audioAnalyzerCapability = audioAnalyzerCapability;
|
|
277111
277681
|
exports.audioCodecCapability = audioCodecCapability;
|
|
277112
277682
|
exports.audioIsFailClosed = audioIsFailClosed;
|
|
277683
|
+
exports.audioKindId = audioKindId;
|
|
277113
277684
|
exports.audioLabelChoices = audioLabelChoices;
|
|
277114
277685
|
exports.audioMetricsCapability = audioMetricsCapability;
|
|
277115
277686
|
exports.audioModeOf = audioModeOf;
|
|
@@ -277148,6 +277719,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277148
277719
|
exports.classifyStream = classifyStream;
|
|
277149
277720
|
exports.classifyStreams = classifyStreams;
|
|
277150
277721
|
exports.climateControlCapability = climateControlCapability;
|
|
277722
|
+
exports.clusterModelSettingKey = clusterModelSettingKey;
|
|
277151
277723
|
exports.collectHydratedFieldEntries = require_sleep.collectHydratedFieldEntries;
|
|
277152
277724
|
exports.collectHydratedFieldValues = require_sleep.collectHydratedFieldValues;
|
|
277153
277725
|
exports.colorCapability = colorCapability;
|
|
@@ -277266,6 +277838,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277266
277838
|
exports.isAudioRule = isAudioRule;
|
|
277267
277839
|
exports.isBaseConditionKey = isBaseConditionKey;
|
|
277268
277840
|
exports.isBatteryPresenceFault = isBatteryPresenceFault;
|
|
277841
|
+
exports.isClusterScopedStep = isClusterScopedStep;
|
|
277269
277842
|
exports.isCollectionArrayMethod = isCollectionArrayMethod;
|
|
277270
277843
|
exports.isDeployableToAgent = isDeployableToAgent;
|
|
277271
277844
|
exports.isDetectionMacroClass = isDetectionMacroClass;
|
|
@@ -277349,6 +277922,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277349
277922
|
exports.patchAudio = patchAudio;
|
|
277350
277923
|
exports.petFeederCapability = petFeederCapability;
|
|
277351
277924
|
exports.pickAccessoryControl = pickAccessoryControl;
|
|
277925
|
+
exports.pickClusterStepModels = pickClusterStepModels;
|
|
277352
277926
|
exports.pickDetailCropConvention = pickDetailCropConvention;
|
|
277353
277927
|
exports.pickNativeLeaseOverride = pickNativeLeaseOverride;
|
|
277354
277928
|
exports.pickPreferredRtspEntry = pickPreferredRtspEntry;
|
|
@@ -277372,6 +277946,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277372
277946
|
exports.ptzAutotrackCapability = ptzAutotrackCapability;
|
|
277373
277947
|
exports.ptzCapability = ptzCapability;
|
|
277374
277948
|
exports.pythonScriptForBackend = pythonScriptForBackend;
|
|
277949
|
+
exports.readClusterStepModels = readClusterStepModels;
|
|
277375
277950
|
exports.readDetailCropConvention = readDetailCropConvention;
|
|
277376
277951
|
exports.readDeviceStateFrom = readDeviceStateFrom;
|
|
277377
277952
|
exports.readNativeLeaseOverride = readNativeLeaseOverride;
|
|
@@ -277389,6 +277964,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277389
277964
|
exports.resolveAddonPlacement = resolveAddonPlacement;
|
|
277390
277965
|
exports.resolveAddonRuntime = resolveAddonRuntime;
|
|
277391
277966
|
exports.resolveCapMount = require_sleep.resolveCapMount;
|
|
277967
|
+
exports.resolveClusterStepModelId = resolveClusterStepModelId;
|
|
277392
277968
|
exports.resolveDetectionRuntime = resolveDetectionRuntime;
|
|
277393
277969
|
exports.resolveDeviceControlKind = resolveDeviceControlKind;
|
|
277394
277970
|
exports.resolveDeviceProfile = resolveDeviceProfile;
|
|
@@ -277698,22 +278274,20 @@ var require_update_availability_emitter = __commonJS({
|
|
|
277698
278274
|
this.persistIfDirty();
|
|
277699
278275
|
if (!latestChanged)
|
|
277700
278276
|
return;
|
|
277701
|
-
this.emitList(target);
|
|
278277
|
+
this.emitList(target, scope);
|
|
277702
278278
|
}
|
|
277703
278279
|
/** Publish a partial candidate set without treating omissions as up-to-date. */
|
|
277704
278280
|
publishCandidates(candidates) {
|
|
277705
|
-
|
|
277706
|
-
const targets = /* @__PURE__ */ new Set();
|
|
278281
|
+
const changed = /* @__PURE__ */ new Map();
|
|
277707
278282
|
for (const candidate of candidates) {
|
|
277708
|
-
|
|
277709
|
-
|
|
277710
|
-
|
|
278283
|
+
if (!this.note(candidate))
|
|
278284
|
+
continue;
|
|
278285
|
+
const scope = scopeOf(candidate);
|
|
278286
|
+
changed.set(`${candidate.target}:${scope}`, { target: candidate.target, scope });
|
|
277711
278287
|
}
|
|
277712
278288
|
this.persistIfDirty();
|
|
277713
|
-
|
|
277714
|
-
|
|
277715
|
-
for (const target of targets)
|
|
277716
|
-
this.emitList(target);
|
|
278289
|
+
for (const { target, scope } of changed.values())
|
|
278290
|
+
this.emitList(target, scope);
|
|
277717
278291
|
}
|
|
277718
278292
|
note(candidate) {
|
|
277719
278293
|
const key = this.key(candidate);
|
|
@@ -277739,20 +278313,20 @@ var require_update_availability_emitter = __commonJS({
|
|
|
277739
278313
|
held: Object.fromEntries(this.held)
|
|
277740
278314
|
});
|
|
277741
278315
|
}
|
|
277742
|
-
|
|
277743
|
-
|
|
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);
|
|
277744
278319
|
if (list.length === 0)
|
|
277745
278320
|
return;
|
|
277746
278321
|
const packages = list.map((candidate) => ({
|
|
277747
278322
|
packageName: candidate.packageName,
|
|
277748
278323
|
currentVersion: candidate.currentVersion,
|
|
277749
278324
|
latestVersion: candidate.latestVersion,
|
|
277750
|
-
|
|
278325
|
+
nodeId: scope
|
|
277751
278326
|
}));
|
|
277752
|
-
const nodeIds = unique(packages.map((pkg) => pkg.nodeId).filter((id) => id !== void 0));
|
|
277753
278327
|
const head = list[0];
|
|
277754
278328
|
this.eventBus.emit({
|
|
277755
|
-
id: `update.available:${target}:${packages.map((pkg) => `${pkg.
|
|
278329
|
+
id: `update.available:${target}:${scope}:${packages.map((pkg) => `${pkg.packageName}@${pkg.latestVersion}`).toSorted().join(",")}`,
|
|
277756
278330
|
timestamp: /* @__PURE__ */ new Date(),
|
|
277757
278331
|
source: this.source,
|
|
277758
278332
|
category: types_1.EventCategory.UpdateAvailable,
|
|
@@ -277762,24 +278336,24 @@ var require_update_availability_emitter = __commonJS({
|
|
|
277762
278336
|
currentVersion: head.currentVersion,
|
|
277763
278337
|
latestVersion: head.latestVersion,
|
|
277764
278338
|
packages,
|
|
277765
|
-
|
|
277766
|
-
|
|
278339
|
+
nodeId: scope,
|
|
278340
|
+
nodeIds: [scope]
|
|
277767
278341
|
}
|
|
277768
278342
|
});
|
|
277769
278343
|
}
|
|
277770
278344
|
key(candidate) {
|
|
277771
|
-
return `${candidate.target}:${candidate
|
|
278345
|
+
return `${candidate.target}:${scopeOf(candidate)}:${candidate.packageName}`;
|
|
277772
278346
|
}
|
|
277773
278347
|
};
|
|
277774
278348
|
exports.UpdateAvailabilityEmitter = UpdateAvailabilityEmitter;
|
|
278349
|
+
function scopeOf(candidate) {
|
|
278350
|
+
return candidate.nodeId ?? "hub";
|
|
278351
|
+
}
|
|
277775
278352
|
function stampNode(candidate, nodeId) {
|
|
277776
278353
|
if (nodeId === void 0 || candidate.nodeId !== void 0)
|
|
277777
278354
|
return candidate;
|
|
277778
278355
|
return { ...candidate, nodeId };
|
|
277779
278356
|
}
|
|
277780
|
-
function unique(values) {
|
|
277781
|
-
return [...new Set(values)];
|
|
277782
|
-
}
|
|
277783
278357
|
}
|
|
277784
278358
|
});
|
|
277785
278359
|
|
|
@@ -279134,7 +279708,13 @@ var require_server_root = __commonJS({
|
|
|
279134
279708
|
fromVersion: runningVersion
|
|
279135
279709
|
}
|
|
279136
279710
|
});
|
|
279137
|
-
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
|
+
});
|
|
279138
279718
|
return {
|
|
279139
279719
|
accepted: true,
|
|
279140
279720
|
targetVersion: target,
|
|
@@ -279419,6 +279999,47 @@ var require_server_root = __commonJS({
|
|
|
279419
279999
|
}
|
|
279420
280000
|
});
|
|
279421
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
|
+
|
|
279422
280043
|
// ../../server/backend/dist/agent/agent-update-service.js
|
|
279423
280044
|
var require_agent_update_service = __commonJS({
|
|
279424
280045
|
"../../server/backend/dist/agent/agent-update-service.js"(exports) {
|
|
@@ -279476,6 +280097,7 @@ var require_agent_update_service = __commonJS({
|
|
|
279476
280097
|
var update_availability_emitter_js_1 = require_update_availability_emitter();
|
|
279477
280098
|
var update_availability_store_js_1 = require_update_availability_store();
|
|
279478
280099
|
var index_js_1 = require_server_root();
|
|
280100
|
+
var restart_marker_js_1 = require_restart_marker();
|
|
279479
280101
|
exports.AGENT_RUNTIME_ADDON_ID = "agent-runtime";
|
|
279480
280102
|
exports.AGENT_RESTART_GRACE_MS = 2e3;
|
|
279481
280103
|
function isRunningInContainer(existsSyncFn) {
|
|
@@ -279539,8 +280161,8 @@ var require_agent_update_service = __commonJS({
|
|
|
279539
280161
|
seedDir: "CAMSTACK_SEED_SERVER_DIR"
|
|
279540
280162
|
},
|
|
279541
280163
|
logger: options.logger,
|
|
279542
|
-
restartServer: (requestedBy) => {
|
|
279543
|
-
writeAgentRestartMarker(options.dataDir, requestedBy);
|
|
280164
|
+
restartServer: (requestedBy, update) => {
|
|
280165
|
+
writeAgentRestartMarker(options.dataDir, requestedBy, update);
|
|
279544
280166
|
restartAgent(requestedBy);
|
|
279545
280167
|
},
|
|
279546
280168
|
dataDir: options.dataDir,
|
|
@@ -279618,18 +280240,13 @@ var require_agent_update_service = __commonJS({
|
|
|
279618
280240
|
}
|
|
279619
280241
|
};
|
|
279620
280242
|
exports.AgentUpdateService = AgentUpdateService;
|
|
279621
|
-
function writeAgentRestartMarker(dataDir, requestedBy) {
|
|
279622
|
-
const toVersion = /@([^@\s]+)$/.exec(requestedBy)?.[1];
|
|
280243
|
+
function writeAgentRestartMarker(dataDir, requestedBy, update) {
|
|
279623
280244
|
try {
|
|
279624
|
-
(0, system_1.writePendingRestart)(dataDir, {
|
|
279625
|
-
kind: requestedBy.startsWith("server-update:") ? "framework-update" : "manual",
|
|
280245
|
+
(0, system_1.writePendingRestart)(dataDir, (0, restart_marker_js_1.buildRestartMarker)({
|
|
279626
280246
|
requestedAt: Date.now(),
|
|
279627
280247
|
requestedBy,
|
|
279628
|
-
...
|
|
279629
|
-
|
|
279630
|
-
...toVersion !== void 0 ? { toVersion } : {}
|
|
279631
|
-
} : {}
|
|
279632
|
-
});
|
|
280248
|
+
...update !== void 0 ? { update } : {}
|
|
280249
|
+
}));
|
|
279633
280250
|
} catch {
|
|
279634
280251
|
}
|
|
279635
280252
|
}
|
|
@@ -388500,6 +389117,7 @@ var require_addon_package_service = __commonJS({
|
|
|
388500
389117
|
var types_1 = require_dist9();
|
|
388501
389118
|
var update_availability_emitter_js_1 = require_update_availability_emitter();
|
|
388502
389119
|
var update_check_scheduler_js_1 = require_update_check_scheduler();
|
|
389120
|
+
var restart_marker_js_1 = require_restart_marker();
|
|
388503
389121
|
var package_dir_utils_js_1 = require_package_dir_utils();
|
|
388504
389122
|
var execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
388505
389123
|
exports.SYSTEM_PACKAGE = "@camstack/system";
|
|
@@ -389427,26 +390045,24 @@ var require_addon_package_service = __commonJS({
|
|
|
389427
390045
|
/**
|
|
389428
390046
|
* Gracefully restart the server process.
|
|
389429
390047
|
*
|
|
389430
|
-
* Writes
|
|
389431
|
-
*
|
|
389432
|
-
*
|
|
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`
|
|
389433
390054
|
* with the same payload so subscribers can show a reconnect overlay
|
|
389434
390055
|
* immediately. Then delegates to `scheduleSelfRestart` which picks
|
|
389435
390056
|
* the right exit path (Electron `app.relaunch` vs supervisor-driven
|
|
389436
390057
|
* `process.exit`).
|
|
389437
390058
|
*/
|
|
389438
|
-
restartServer(requestedBy) {
|
|
390059
|
+
restartServer(requestedBy, update) {
|
|
389439
390060
|
this.logger.info("Server restart requested -- initiating graceful shutdown");
|
|
389440
|
-
const
|
|
389441
|
-
const payload = {
|
|
389442
|
-
kind: isServerUpdateRequest(requestedBy) ? "framework-update" : "manual",
|
|
390061
|
+
const payload = (0, restart_marker_js_1.buildRestartMarker)({
|
|
389443
390062
|
requestedAt: Date.now(),
|
|
389444
390063
|
...requestedBy !== void 0 ? { requestedBy } : {},
|
|
389445
|
-
...
|
|
389446
|
-
|
|
389447
|
-
...toVersion !== void 0 ? { toVersion } : {}
|
|
389448
|
-
} : {}
|
|
389449
|
-
};
|
|
390064
|
+
...update !== void 0 ? { update } : {}
|
|
390065
|
+
});
|
|
389450
390066
|
try {
|
|
389451
390067
|
(0, system_1.writePendingRestart)(this.resolveDataDir(), payload);
|
|
389452
390068
|
} catch (err) {
|
|
@@ -389937,22 +390553,23 @@ var require_addon_package_service = __commonJS({
|
|
|
389937
390553
|
* re-announces them on the next good poll.
|
|
389938
390554
|
*/
|
|
389939
390555
|
publishAddonAvailability(updates, failures, nodeId) {
|
|
390556
|
+
const scope = nodeId ?? this.resolveNodeId();
|
|
389940
390557
|
const candidates = updates.map((update) => ({
|
|
389941
390558
|
target: "addon",
|
|
389942
390559
|
packageName: update.name,
|
|
389943
390560
|
currentVersion: update.currentVersion,
|
|
389944
390561
|
latestVersion: update.latestVersion,
|
|
389945
|
-
|
|
390562
|
+
nodeId: scope
|
|
389946
390563
|
}));
|
|
389947
390564
|
if (failures > 0) {
|
|
389948
390565
|
this.logger.warn("Addon update check incomplete \u2014 availability published as partial", {
|
|
389949
|
-
tags: { nodeId:
|
|
390566
|
+
tags: { nodeId: scope },
|
|
389950
390567
|
meta: { failures, published: candidates.length }
|
|
389951
390568
|
});
|
|
389952
390569
|
this.updateAvailability.publishCandidates(candidates);
|
|
389953
390570
|
return;
|
|
389954
390571
|
}
|
|
389955
|
-
this.updateAvailability.publishSnapshot("addon", candidates,
|
|
390572
|
+
this.updateAvailability.publishSnapshot("addon", candidates, scope);
|
|
389956
390573
|
}
|
|
389957
390574
|
/** Fetch npm search results for camstack addon packages (cached 5 min) */
|
|
389958
390575
|
async fetchSearchFromNpm() {
|
|
@@ -390102,15 +390719,6 @@ var require_addon_package_service = __commonJS({
|
|
|
390102
390719
|
}
|
|
390103
390720
|
};
|
|
390104
390721
|
exports.AddonPackageService = AddonPackageService;
|
|
390105
|
-
function isServerUpdateRequest(requestedBy) {
|
|
390106
|
-
return requestedBy !== void 0 && requestedBy.startsWith("server-update:");
|
|
390107
|
-
}
|
|
390108
|
-
function toVersionFromRestartReason(requestedBy) {
|
|
390109
|
-
if (requestedBy === void 0)
|
|
390110
|
-
return void 0;
|
|
390111
|
-
const match = /@([^@\s]+)$/.exec(requestedBy);
|
|
390112
|
-
return match?.[1];
|
|
390113
|
-
}
|
|
390114
390722
|
async function extractTgzStripped(tgz, destDir) {
|
|
390115
390723
|
fs.mkdirSync(destDir, { recursive: true });
|
|
390116
390724
|
const tmpFile = path.join(os.tmpdir(), `camstack-tgz-${(0, node_crypto_1.randomUUID)()}.tgz`);
|
|
@@ -401619,14 +402227,6 @@ var require_integration_visibility = __commonJS({
|
|
|
401619
402227
|
function createFilteredIntegrationRegistry(deps) {
|
|
401620
402228
|
const { raw, logger } = deps;
|
|
401621
402229
|
const isInstalled = (addonId) => deps.isAddonInstalled((0, types_1.bareAddonId)(addonId));
|
|
401622
|
-
let activeIntegrationIds = null;
|
|
401623
|
-
const ensureActiveIds = async () => {
|
|
401624
|
-
if (activeIntegrationIds)
|
|
401625
|
-
return activeIntegrationIds;
|
|
401626
|
-
const all = await raw.listIntegrations();
|
|
401627
|
-
activeIntegrationIds = new Set(all.filter((i) => isInstalled(i.addonId)).map((i) => i.id));
|
|
401628
|
-
return activeIntegrationIds;
|
|
401629
|
-
};
|
|
401630
402230
|
const logHidden = (integration, phase) => {
|
|
401631
402231
|
logger.warn("integration hidden \u2014 its addon is not installed, so this read answers null even though the row exists", {
|
|
401632
402232
|
tags: { integrationId: integration.id, addonId: integration.addonId },
|
|
@@ -401672,39 +402272,7 @@ var require_integration_visibility = __commonJS({
|
|
|
401672
402272
|
// Integration settings: passthrough (already gated by getIntegration)
|
|
401673
402273
|
getIntegrationSettings: (id) => raw.getIntegrationSettings(id),
|
|
401674
402274
|
setIntegrationSetting: (id, key, value) => raw.setIntegrationSetting(id, key, value),
|
|
401675
|
-
setIntegrationSettings: (id, settings) => raw.setIntegrationSettings(id, settings)
|
|
401676
|
-
// Devices: filter out devices belonging to hidden integrations
|
|
401677
|
-
createDevice: (input) => raw.createDevice(input),
|
|
401678
|
-
getDevice: async (id) => {
|
|
401679
|
-
const d = await raw.getDevice(id);
|
|
401680
|
-
if (!d)
|
|
401681
|
-
return null;
|
|
401682
|
-
const ids = await ensureActiveIds();
|
|
401683
|
-
return ids.has(d.integrationId) ? d : null;
|
|
401684
|
-
},
|
|
401685
|
-
getDeviceByStableId: async (stableId) => {
|
|
401686
|
-
const d = await raw.getDeviceByStableId(stableId);
|
|
401687
|
-
if (!d)
|
|
401688
|
-
return null;
|
|
401689
|
-
const ids = await ensureActiveIds();
|
|
401690
|
-
return ids.has(d.integrationId) ? d : null;
|
|
401691
|
-
},
|
|
401692
|
-
listDevices: async (integrationId) => {
|
|
401693
|
-
const devices = await raw.listDevices(integrationId);
|
|
401694
|
-
const ids = await ensureActiveIds();
|
|
401695
|
-
return devices.filter((d) => ids.has(d.integrationId));
|
|
401696
|
-
},
|
|
401697
|
-
listCameras: async () => {
|
|
401698
|
-
const cameras = await raw.listCameras();
|
|
401699
|
-
const ids = await ensureActiveIds();
|
|
401700
|
-
return cameras.filter((d) => ids.has(d.integrationId));
|
|
401701
|
-
},
|
|
401702
|
-
updateDevice: (id, updates) => raw.updateDevice(id, updates),
|
|
401703
|
-
deleteDevice: (id) => raw.deleteDevice(id),
|
|
401704
|
-
// Device settings: passthrough
|
|
401705
|
-
getDeviceSettings: (id) => raw.getDeviceSettings(id),
|
|
401706
|
-
setDeviceSetting: (id, key, value) => raw.setDeviceSetting(id, key, value),
|
|
401707
|
-
setDeviceSettings: (id, settings) => raw.setDeviceSettings(id, settings)
|
|
402275
|
+
setIntegrationSettings: (id, settings) => raw.setIntegrationSettings(id, settings)
|
|
401708
402276
|
};
|
|
401709
402277
|
}
|
|
401710
402278
|
}
|
|
@@ -401908,6 +402476,58 @@ var require_runner_spawn_fanout = __commonJS({
|
|
|
401908
402476
|
}
|
|
401909
402477
|
});
|
|
401910
402478
|
|
|
402479
|
+
// ../../server/backend/dist/core/addon/single-flight-refresh.js
|
|
402480
|
+
var require_single_flight_refresh = __commonJS({
|
|
402481
|
+
"../../server/backend/dist/core/addon/single-flight-refresh.js"(exports) {
|
|
402482
|
+
"use strict";
|
|
402483
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
402484
|
+
exports.createSingleFlightRefresh = createSingleFlightRefresh;
|
|
402485
|
+
function createSingleFlightRefresh(run) {
|
|
402486
|
+
let inFlight = null;
|
|
402487
|
+
let pending = false;
|
|
402488
|
+
let pendingPromise = null;
|
|
402489
|
+
let releasePending = () => {
|
|
402490
|
+
};
|
|
402491
|
+
const start = () => {
|
|
402492
|
+
const active = (async () => {
|
|
402493
|
+
try {
|
|
402494
|
+
await run();
|
|
402495
|
+
} finally {
|
|
402496
|
+
inFlight = null;
|
|
402497
|
+
if (pending) {
|
|
402498
|
+
pending = false;
|
|
402499
|
+
const release = releasePending;
|
|
402500
|
+
releasePending = () => {
|
|
402501
|
+
};
|
|
402502
|
+
pendingPromise = null;
|
|
402503
|
+
void start().finally(release);
|
|
402504
|
+
}
|
|
402505
|
+
}
|
|
402506
|
+
})();
|
|
402507
|
+
inFlight = active;
|
|
402508
|
+
return active;
|
|
402509
|
+
};
|
|
402510
|
+
return {
|
|
402511
|
+
request: async () => {
|
|
402512
|
+
if (inFlight === null) {
|
|
402513
|
+
await start();
|
|
402514
|
+
return;
|
|
402515
|
+
}
|
|
402516
|
+
if (!pending) {
|
|
402517
|
+
pending = true;
|
|
402518
|
+
pendingPromise = new Promise((resolve) => {
|
|
402519
|
+
releasePending = resolve;
|
|
402520
|
+
});
|
|
402521
|
+
}
|
|
402522
|
+
const wait = pendingPromise;
|
|
402523
|
+
if (wait !== null)
|
|
402524
|
+
await wait;
|
|
402525
|
+
}
|
|
402526
|
+
};
|
|
402527
|
+
}
|
|
402528
|
+
}
|
|
402529
|
+
});
|
|
402530
|
+
|
|
401911
402531
|
// ../../server/backend/dist/core/addon/addon-registry.service.js
|
|
401912
402532
|
var require_addon_registry_service = __commonJS({
|
|
401913
402533
|
"../../server/backend/dist/core/addon/addon-registry.service.js"(exports) {
|
|
@@ -401970,6 +402590,7 @@ var require_addon_registry_service = __commonJS({
|
|
|
401970
402590
|
var require_cache_js_1 = require_require_cache();
|
|
401971
402591
|
var runner_convergence_1 = require_runner_convergence();
|
|
401972
402592
|
var runner_spawn_fanout_js_1 = require_runner_spawn_fanout();
|
|
402593
|
+
var single_flight_refresh_js_1 = require_single_flight_refresh();
|
|
401973
402594
|
function shouldEvictMissingOnDisk(entry, id, onDiskIds) {
|
|
401974
402595
|
return entry.source === "installed" && entry.packageName !== "@camstack/system" && !onDiskIds.has(id);
|
|
401975
402596
|
}
|
|
@@ -402765,11 +403386,18 @@ var require_addon_registry_service = __commonJS({
|
|
|
402765
403386
|
}
|
|
402766
403387
|
return out;
|
|
402767
403388
|
};
|
|
403389
|
+
/** One full-fleet rebuild in flight, at most one queued behind it. Every
|
|
403390
|
+
* event below asks for the SAME total rebuild, so running it once per event
|
|
403391
|
+
* was ~1 000 rebuilds of a 974-row mirror on a boot — each one a 625 KB read
|
|
403392
|
+
* and parse on hub-main's event loop. No timer: the first event still
|
|
403393
|
+
* refreshes immediately, because this mirror backs scope enforcement.
|
|
403394
|
+
* See `single-flight-refresh.ts`. */
|
|
403395
|
+
deviceMirrorGate = (0, single_flight_refresh_js_1.createSingleFlightRefresh)(() => this.refreshDeviceParentMirror());
|
|
402768
403396
|
/** Subscribe the mirror to every device-meta lifecycle event, and warm it once
|
|
402769
403397
|
* the addon set (device-manager included) is up. */
|
|
402770
403398
|
wireDeviceParentMirror() {
|
|
402771
403399
|
const refresh = () => {
|
|
402772
|
-
void this.
|
|
403400
|
+
void this.deviceMirrorGate.request();
|
|
402773
403401
|
};
|
|
402774
403402
|
for (const category of [
|
|
402775
403403
|
types_1.EventCategory.DeviceMetaChanged,
|
|
@@ -406225,6 +406853,7 @@ var require_moleculer_service = __commonJS({
|
|
|
406225
406853
|
"use strict";
|
|
406226
406854
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
406227
406855
|
exports.MoleculerService = void 0;
|
|
406856
|
+
exports.childOwnerToken = childOwnerToken;
|
|
406228
406857
|
exports.buildChildUdsManifest = buildChildUdsManifest;
|
|
406229
406858
|
var node_crypto_1 = __require("crypto");
|
|
406230
406859
|
var system_1 = require_dist3();
|
|
@@ -406251,6 +406880,21 @@ var require_moleculer_service = __commonJS({
|
|
|
406251
406880
|
* `$hub.registerNode`. Populated by `onRegisterNode` in `hubDeps`.
|
|
406252
406881
|
*/
|
|
406253
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();
|
|
406254
406898
|
/**
|
|
406255
406899
|
* Fixed-period agent-readiness snapshot sweep (D8 reconcile) — repairs
|
|
406256
406900
|
* agent-origin readiness deltas lost while the agent stayed connected.
|
|
@@ -406572,14 +407216,18 @@ var require_moleculer_service = __commonJS({
|
|
|
406572
407216
|
const hubNodeId = this.brokerSafe.nodeID;
|
|
406573
407217
|
const childNodeId = `${hubNodeId}/${child.childId}`;
|
|
406574
407218
|
const params = buildChildUdsManifest(childNodeId, child.childId, child.caps);
|
|
406575
|
-
this.onRegisterNode(params);
|
|
406576
|
-
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
|
+
});
|
|
406577
407223
|
});
|
|
406578
|
-
registry.onChildGone((childId) => {
|
|
407224
|
+
registry.onChildGone((childId, incarnation) => {
|
|
406579
407225
|
const hubNodeId = this.brokerSafe.nodeID;
|
|
406580
407226
|
const childNodeId = `${hubNodeId}/${childId}`;
|
|
406581
|
-
logger.info("UDS child gone \u2014 removing from registry", {
|
|
406582
|
-
|
|
407227
|
+
logger.info("UDS child gone \u2014 removing from registry", {
|
|
407228
|
+
meta: { childId, incarnation }
|
|
407229
|
+
});
|
|
407230
|
+
this.removeNodeFromRegistry(childNodeId, childOwnerToken(childNodeId, incarnation));
|
|
406583
407231
|
});
|
|
406584
407232
|
registry.onChildLog((childId, entry) => {
|
|
406585
407233
|
this.logging.writeFromWorker((0, system_1.udsChildLogToWorkerEntry)(childId, entry));
|
|
@@ -406763,10 +407411,15 @@ var require_moleculer_service = __commonJS({
|
|
|
406763
407411
|
* CapabilityRegistry update is a diff (atomic replace) rather than an
|
|
406764
407412
|
* unconditional re-register — see `applyNodeManifest` for the rationale.
|
|
406765
407413
|
*/
|
|
406766
|
-
onRegisterNode(params) {
|
|
407414
|
+
onRegisterNode(params, owner) {
|
|
406767
407415
|
const previousManifest = this.nodeRegistry.getNodeManifest(params.nodeId);
|
|
407416
|
+
const previousOwner = this.nodeOwners.get(params.nodeId);
|
|
406768
407417
|
this.nodeRegistry.registerNode(params);
|
|
406769
|
-
|
|
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);
|
|
406770
407423
|
const { nodeId } = params;
|
|
406771
407424
|
if (nodeId !== "hub" && !nodeId.includes("/") && this.onAgentRegisteredCb) {
|
|
406772
407425
|
this.onAgentRegisteredCb(nodeId);
|
|
@@ -406794,7 +407447,7 @@ var require_moleculer_service = __commonJS({
|
|
|
406794
407447
|
* already; this method handles only `params.addons` (system caps).
|
|
406795
407448
|
* Native-cap wiring into device-manager is done in a later task.
|
|
406796
407449
|
*/
|
|
406797
|
-
applyNodeManifest(params, previousManifest) {
|
|
407450
|
+
applyNodeManifest(params, previousManifest, owner, previousOwner) {
|
|
406798
407451
|
const { nodeId, addons } = params;
|
|
406799
407452
|
const hubNodeId = this.brokerSafe.nodeID;
|
|
406800
407453
|
const isLocalChild = nodeId.startsWith(hubNodeId + "/");
|
|
@@ -406825,9 +407478,16 @@ var require_moleculer_service = __commonJS({
|
|
|
406825
407478
|
for (const [key, { addonId, capName }] of previous) {
|
|
406826
407479
|
if (desired.has(key))
|
|
406827
407480
|
continue;
|
|
406828
|
-
registry.unregisterProvider(capName, registryKeyFor(addonId));
|
|
407481
|
+
registry.unregisterProvider(capName, registryKeyFor(addonId), previousOwner);
|
|
406829
407482
|
this.nodeCallFns.delete(`${nodeId}::${capName}`);
|
|
406830
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
|
+
}
|
|
406831
407491
|
for (const [key, { addonId, capName, capDef }] of desired) {
|
|
406832
407492
|
if (previous.has(key))
|
|
406833
407493
|
continue;
|
|
@@ -406852,7 +407512,7 @@ var require_moleculer_service = __commonJS({
|
|
|
406852
407512
|
for (const methodName of Object.keys((0, system_1.expandCapMethods)(capDef))) {
|
|
406853
407513
|
proxy[methodName] = (methodParams) => callFn(methodName, methodParams);
|
|
406854
407514
|
}
|
|
406855
|
-
registry.registerProvider(capName, registryKey, proxy);
|
|
407515
|
+
registry.registerProvider(capName, registryKey, proxy, owner);
|
|
406856
407516
|
if (capDef.mode === "singleton" && isLocalChild) {
|
|
406857
407517
|
const activeKey = registry.getSingletonAddonId(capName);
|
|
406858
407518
|
if (activeKey === null || activeKey.includes("@")) {
|
|
@@ -406885,7 +407545,14 @@ var require_moleculer_service = __commonJS({
|
|
|
406885
407545
|
* Unregisters every cap the node's last manifest declared and emits
|
|
406886
407546
|
* synthetic readiness-down events for each.
|
|
406887
407547
|
*/
|
|
406888
|
-
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
|
+
}
|
|
406889
407556
|
const manifest = this.nodeRegistry.getNodeManifest(nodeId);
|
|
406890
407557
|
if (!manifest)
|
|
406891
407558
|
return;
|
|
@@ -406899,7 +407566,7 @@ var require_moleculer_service = __commonJS({
|
|
|
406899
407566
|
const registryKey = isLocalChild ? addonId : `${addonId}@${nodeId}`;
|
|
406900
407567
|
if (registry) {
|
|
406901
407568
|
for (const capName of capabilities) {
|
|
406902
|
-
registry.unregisterProvider(capName, registryKey);
|
|
407569
|
+
registry.unregisterProvider(capName, registryKey, owner);
|
|
406903
407570
|
}
|
|
406904
407571
|
}
|
|
406905
407572
|
for (const capName of capabilities) {
|
|
@@ -406920,6 +407587,7 @@ var require_moleculer_service = __commonJS({
|
|
|
406920
407587
|
}
|
|
406921
407588
|
}
|
|
406922
407589
|
}
|
|
407590
|
+
this.nodeOwners.delete(nodeId);
|
|
406923
407591
|
this.nodeRegistry.removeNode(nodeId);
|
|
406924
407592
|
}
|
|
406925
407593
|
findCallFn(nodeId, capabilityName) {
|
|
@@ -407083,6 +407751,9 @@ var require_moleculer_service = __commonJS({
|
|
|
407083
407751
|
}
|
|
407084
407752
|
};
|
|
407085
407753
|
exports.MoleculerService = MoleculerService;
|
|
407754
|
+
function childOwnerToken(childNodeId, incarnation) {
|
|
407755
|
+
return `${childNodeId}#${incarnation}`;
|
|
407756
|
+
}
|
|
407086
407757
|
function buildChildUdsManifest(nodeId, childId, caps) {
|
|
407087
407758
|
const capsByAddon = /* @__PURE__ */ new Map();
|
|
407088
407759
|
for (const cap of caps) {
|
|
@@ -408443,7 +409114,7 @@ var require_manual_boot = __commonJS({
|
|
|
408443
409114
|
const replEngineService = new repl_engine_service_1.ReplEngineService(addonRegistryService, eventBusService, loggingService);
|
|
408444
409115
|
const serverUpdateService = new server_update_service_1.ServerUpdateService({
|
|
408445
409116
|
logger: loggingService.createLogger("ServerUpdate"),
|
|
408446
|
-
restartServer: (requestedBy) => addonPackageService.restartServer(requestedBy),
|
|
409117
|
+
restartServer: (requestedBy, update) => addonPackageService.restartServer(requestedBy, update),
|
|
408447
409118
|
eventBus: eventBusService,
|
|
408448
409119
|
updateAvailabilityStore: new update_availability_store_js_1.FileUpdateAvailabilityStore(availabilityDataDir, "server-update", loggingService.createLogger("UpdateAvailability"))
|
|
408449
409120
|
});
|