camstack 1.2.35 → 1.2.37
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-C1Aea3c6.js
|
|
23635
|
+
var require_dist_C1Aea3c6 = __commonJS({
|
|
23636
|
+
"../system/dist/dist-C1Aea3c6.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
|
|
@@ -37270,6 +37300,12 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
37270
37300
|
/** Unix timestamp (ms) when this snapshot was composed server-side. */
|
|
37271
37301
|
fetchedAt: zod.z.number()
|
|
37272
37302
|
});
|
|
37303
|
+
var InferenceDeviceExclusionReasonSchema = zod.z.enum([
|
|
37304
|
+
"disabled",
|
|
37305
|
+
"unavailable",
|
|
37306
|
+
"cannot-host-camera-root",
|
|
37307
|
+
"accelerator-preferred"
|
|
37308
|
+
]);
|
|
37273
37309
|
var NodeInferenceDeviceSchema = zod.z.object({
|
|
37274
37310
|
/** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
|
|
37275
37311
|
key: zod.z.string(),
|
|
@@ -37300,7 +37336,17 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
37300
37336
|
* available per format; this is the stored selection that becomes the
|
|
37301
37337
|
* default for EVERY camera landing on this accelerator.
|
|
37302
37338
|
*/
|
|
37303
|
-
steps: zod.z.record(zod.z.string(), DeviceStepConfigSchema).optional()
|
|
37339
|
+
steps: zod.z.record(zod.z.string(), DeviceStepConfigSchema).optional(),
|
|
37340
|
+
/**
|
|
37341
|
+
* `null` when the device IS a camera-root candidate on this node; otherwise
|
|
37342
|
+
* the reason the dispatcher drops it. Computed by the SAME
|
|
37343
|
+
* `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
|
|
37344
|
+
* never disagree with the election — deriving it in the UI from
|
|
37345
|
+
* `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
|
|
37346
|
+
* the node's model catalog) and `accelerator-preferred` (needs the node-wide
|
|
37347
|
+
* "an accelerator is serving" predicate).
|
|
37348
|
+
*/
|
|
37349
|
+
exclusion: InferenceDeviceExclusionReasonSchema.nullable()
|
|
37304
37350
|
});
|
|
37305
37351
|
var NodeInferenceDevicesSchema = zod.z.object({
|
|
37306
37352
|
nodeId: zod.z.string(),
|
|
@@ -38098,6 +38144,7 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
38098
38144
|
isBattery: zod.z.boolean(),
|
|
38099
38145
|
reason: zod.z.enum([
|
|
38100
38146
|
"disabled",
|
|
38147
|
+
"offline",
|
|
38101
38148
|
"sleeping",
|
|
38102
38149
|
"unreachable",
|
|
38103
38150
|
"waking"
|
|
@@ -38188,9 +38235,17 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
38188
38235
|
sleeping: zod.z.boolean(),
|
|
38189
38236
|
/** Current device state rendered over the cached frame. State images
|
|
38190
38237
|
* remain authoritative even when their photographic background is
|
|
38191
|
-
* old; null means the link must carry a current camera frame.
|
|
38238
|
+
* old; null means the link must carry a current camera frame.
|
|
38239
|
+
*
|
|
38240
|
+
* **This is the ONLY source a surface may derive an inactive tile
|
|
38241
|
+
* from.** A client that reads a device's `online`/`disabled` flags and
|
|
38242
|
+
* draws its own verdict will disagree with the pixels it is painting
|
|
38243
|
+
* — the frame comes from here, and so must the sentence over it
|
|
38244
|
+
* (D190). Pair with `ageMs` for the age of the photograph behind the
|
|
38245
|
+
* state. */
|
|
38192
38246
|
stateReason: zod.z.enum([
|
|
38193
38247
|
"disabled",
|
|
38248
|
+
"offline",
|
|
38194
38249
|
"sleeping",
|
|
38195
38250
|
"unreachable",
|
|
38196
38251
|
"waking"
|
|
@@ -42300,7 +42355,35 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
42300
42355
|
*/
|
|
42301
42356
|
priority: zod.z.number()
|
|
42302
42357
|
});
|
|
42303
|
-
var
|
|
42358
|
+
var LocalPortSourceEnum = zod.z.enum([
|
|
42359
|
+
"server-config",
|
|
42360
|
+
"server-env",
|
|
42361
|
+
"caller-hint",
|
|
42362
|
+
"default"
|
|
42363
|
+
]);
|
|
42364
|
+
var AdvertisedLocalPortSchema = zod.z.object({
|
|
42365
|
+
port: zod.z.number().int().min(1).max(65535),
|
|
42366
|
+
source: LocalPortSourceEnum
|
|
42367
|
+
});
|
|
42368
|
+
var GetConnectionEndpointsResultSchema = zod.z.object({
|
|
42369
|
+
endpoints: zod.z.array(ConnectionEndpointSchema).readonly(),
|
|
42370
|
+
/**
|
|
42371
|
+
* The port the hub built the LAN/loopback URLs with, and where that number
|
|
42372
|
+
* came from.
|
|
42373
|
+
*
|
|
42374
|
+
* Returned rather than merely applied, because "the URL is right" and "the
|
|
42375
|
+
* client can KNOW the URL is right" are different properties. A client that
|
|
42376
|
+
* only sees a corrected URL cannot distinguish a hub that fixed the port from
|
|
42377
|
+
* a hub that echoed the port the client sent, so it cannot decide whether to
|
|
42378
|
+
* race the candidate or discard it. With `source` it can: anything but
|
|
42379
|
+
* `caller-hint` is the hub's own socket.
|
|
42380
|
+
*
|
|
42381
|
+
* Absent on hubs predating this field — a client that finds it missing is
|
|
42382
|
+
* talking to an echoing hub and must degrade exactly as it does for
|
|
42383
|
+
* `caller-hint`.
|
|
42384
|
+
*/
|
|
42385
|
+
localPort: AdvertisedLocalPortSchema
|
|
42386
|
+
});
|
|
42304
42387
|
var NotificationEndpointSchema = zod.z.object({
|
|
42305
42388
|
/** The operator's explicit choice, or null for AUTO. */
|
|
42306
42389
|
baseUrl: zod.z.string().nullable(),
|
|
@@ -42342,10 +42425,35 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
42342
42425
|
* Honours `getAllowedAddresses()` when set — addresses outside
|
|
42343
42426
|
* the allowlist are dropped (the public tunnel + loopback are
|
|
42344
42427
|
* always included as escape hatches).
|
|
42428
|
+
*
|
|
42429
|
+
* **The port is the hub's, not the caller's** (D62 — a function's fact
|
|
42430
|
+
* belongs to whoever already owns it). This method used to take a `port`
|
|
42431
|
+
* and echo it onto every LAN and loopback URL it advertised, which made
|
|
42432
|
+
* every client a second authority on a socket only the hub binds. A viewer
|
|
42433
|
+
* configured with a port-less public URL (`https://camstack.example.top`)
|
|
42434
|
+
* infers 443 from the scheme, asks with 443, and was handed
|
|
42435
|
+
* `https://192.168.1.9:443` for a hub listening on 4443. That candidate
|
|
42436
|
+
* cannot ever answer, and losing its race reads in the log exactly like
|
|
42437
|
+
* "the LAN was tried and it was slower" — so an operator sitting on his own
|
|
42438
|
+
* WiFi stayed on the public tunnel for weeks, with the LAN candidate born
|
|
42439
|
+
* dead on every single connect.
|
|
42440
|
+
*
|
|
42441
|
+
* The hub now resolves its own listen port and reports it as `localPort`,
|
|
42442
|
+
* with the SOURCE of the number, so a client can tell a fact from an echo.
|
|
42443
|
+
*
|
|
42444
|
+
* Out of scope on purpose: a LAN URL that is not the hub's own socket (a
|
|
42445
|
+
* reverse proxy in front of it on another port) is not this method's to
|
|
42446
|
+
* invent either — pin it with `setNotificationEndpoint` / a configured
|
|
42447
|
+
* origin, which is an operator statement rather than a guess.
|
|
42345
42448
|
*/
|
|
42346
42449
|
getConnectionEndpoints: method(zod.z.object({
|
|
42347
|
-
/**
|
|
42348
|
-
|
|
42450
|
+
/**
|
|
42451
|
+
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
42452
|
+
* written against the echoing contract keep working; the hub uses it
|
|
42453
|
+
* only when it cannot read its own port, and says so via
|
|
42454
|
+
* `localPort.source === 'caller-hint'`.
|
|
42455
|
+
*/
|
|
42456
|
+
port: zod.z.number().int().min(1).max(65535).optional(),
|
|
42349
42457
|
/** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
|
|
42350
42458
|
* candidate. Default `true`. */
|
|
42351
42459
|
includeLoopback: zod.z.boolean().optional(),
|
|
@@ -55173,7 +55281,6 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
55173
55281
|
hitPercent: 60,
|
|
55174
55282
|
samplingSeconds: 10
|
|
55175
55283
|
};
|
|
55176
|
-
new Map(AUDIO_MACRO_LABELS.flatMap((macro2) => macro2.icon === void 0 ? [] : [[macro2.id, macro2.icon]]));
|
|
55177
55284
|
NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
|
|
55178
55285
|
var TimelapseTemplateSchema = zod.z.object({
|
|
55179
55286
|
title: zod.z.string().max(500).optional(),
|
|
@@ -55284,6 +55391,39 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
55284
55391
|
createdAt: zod.z.number(),
|
|
55285
55392
|
updatedAt: zod.z.number()
|
|
55286
55393
|
});
|
|
55394
|
+
Object.freeze(Object.fromEntries([{
|
|
55395
|
+
stepId: "face-embedding",
|
|
55396
|
+
label: "Face recognition model",
|
|
55397
|
+
defaultModelId: "arcface-r100",
|
|
55398
|
+
indexName: "the enrolled face gallery",
|
|
55399
|
+
options: [
|
|
55400
|
+
{
|
|
55401
|
+
id: "arcface-r100",
|
|
55402
|
+
label: "ArcFace (ResNet34)"
|
|
55403
|
+
},
|
|
55404
|
+
{
|
|
55405
|
+
id: "auraface-r100",
|
|
55406
|
+
label: "AuraFace R100"
|
|
55407
|
+
},
|
|
55408
|
+
{
|
|
55409
|
+
id: "inception-resnet-v1",
|
|
55410
|
+
label: "Inception ResNet V1"
|
|
55411
|
+
}
|
|
55412
|
+
]
|
|
55413
|
+
}, {
|
|
55414
|
+
stepId: "clip-embedding",
|
|
55415
|
+
label: "Semantic search model",
|
|
55416
|
+
defaultModelId: "mobileclip-s1",
|
|
55417
|
+
indexName: "the object semantic-search index",
|
|
55418
|
+
options: [{
|
|
55419
|
+
id: "mobileclip-s1",
|
|
55420
|
+
label: "MobileCLIP S1"
|
|
55421
|
+
}, {
|
|
55422
|
+
id: "mobileclip-s2",
|
|
55423
|
+
label: "MobileCLIP S2"
|
|
55424
|
+
}]
|
|
55425
|
+
}].map((s) => [s.stepId, s.defaultModelId])));
|
|
55426
|
+
zod.z.string().min(1);
|
|
55287
55427
|
zod.z.object({
|
|
55288
55428
|
/**
|
|
55289
55429
|
* Fraction of the box's own size added on EACH side before cutting.
|
|
@@ -55304,10 +55444,17 @@ var require_dist_Ctfhlrtl = __commonJS({
|
|
|
55304
55444
|
*/
|
|
55305
55445
|
square: zod.z.boolean()
|
|
55306
55446
|
});
|
|
55307
|
-
|
|
55447
|
+
var DEFAULT_DETAIL_CROP_CONVENTION = {
|
|
55308
55448
|
paddingRatio: 0.15,
|
|
55309
55449
|
square: false
|
|
55310
|
-
}
|
|
55450
|
+
};
|
|
55451
|
+
Object.freeze({
|
|
55452
|
+
x: 0,
|
|
55453
|
+
y: 0,
|
|
55454
|
+
w: 1,
|
|
55455
|
+
h: 1
|
|
55456
|
+
});
|
|
55457
|
+
DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
|
|
55311
55458
|
var NativeLeaseAdmissionSchema = zod.z.enum(["all", "inferred"]);
|
|
55312
55459
|
zod.z.object({
|
|
55313
55460
|
/**
|
|
@@ -56108,7 +56255,7 @@ var require_alerts_addon = __commonJS({
|
|
|
56108
56255
|
[Symbol.toStringTag]: { value: "Module" }
|
|
56109
56256
|
});
|
|
56110
56257
|
require_chunk_Cek0wNdY();
|
|
56111
|
-
var require_dist10 =
|
|
56258
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
56112
56259
|
function selectExpired(alerts, cutoffMs) {
|
|
56113
56260
|
return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
|
|
56114
56261
|
}
|
|
@@ -56921,7 +57068,7 @@ var require_console_logging = __commonJS({
|
|
|
56921
57068
|
[Symbol.toStringTag]: { value: "Module" }
|
|
56922
57069
|
});
|
|
56923
57070
|
require_chunk_Cek0wNdY();
|
|
56924
|
-
var require_dist10 =
|
|
57071
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
56925
57072
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
56926
57073
|
var LEVEL_RANK = {
|
|
56927
57074
|
debug: 0,
|
|
@@ -57015,7 +57162,7 @@ var require_core_blocks_addon = __commonJS({
|
|
|
57015
57162
|
"use strict";
|
|
57016
57163
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
57017
57164
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
57018
|
-
var require_dist10 =
|
|
57165
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
57019
57166
|
var node_crypto = __require("crypto");
|
|
57020
57167
|
var node_fs_promises = __require("fs/promises");
|
|
57021
57168
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -57912,6 +58059,230 @@ var require_core_blocks = __commonJS({
|
|
|
57912
58059
|
}
|
|
57913
58060
|
});
|
|
57914
58061
|
|
|
58062
|
+
// ../system/dist/retired-settings-keys-B52KsNz8.js
|
|
58063
|
+
var require_retired_settings_keys_B52KsNz8 = __commonJS({
|
|
58064
|
+
"../system/dist/retired-settings-keys-B52KsNz8.js"(exports) {
|
|
58065
|
+
"use strict";
|
|
58066
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
58067
|
+
function settingsStoreIsAuthoritativeHere(env) {
|
|
58068
|
+
const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
|
|
58069
|
+
return raw === "" || raw === "hub";
|
|
58070
|
+
}
|
|
58071
|
+
var RETIRED_SETTINGS_KEYS = [{
|
|
58072
|
+
namespace: "detection-pipeline",
|
|
58073
|
+
collection: "addon-settings",
|
|
58074
|
+
row: "root",
|
|
58075
|
+
keys: ["pipelineSteps", "pipelineEngine"],
|
|
58076
|
+
perNodeKeys: [
|
|
58077
|
+
"engineRuntime",
|
|
58078
|
+
"engineBackend",
|
|
58079
|
+
"engineDevice",
|
|
58080
|
+
"probedBestEngine"
|
|
58081
|
+
],
|
|
58082
|
+
reason: "global step/engine seed and the per-node engine cascade \u2014 both removed; the live per-camera dispatch path never read either"
|
|
58083
|
+
}];
|
|
58084
|
+
var RETIRED_SETTINGS_ROWS = [
|
|
58085
|
+
{
|
|
58086
|
+
collection: "addon-settings",
|
|
58087
|
+
row: "device-manager.deviceMeta",
|
|
58088
|
+
owner: "device-manager",
|
|
58089
|
+
evidence: { collection: "device-manager:devices" },
|
|
58090
|
+
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"
|
|
58091
|
+
},
|
|
58092
|
+
{
|
|
58093
|
+
collection: "addon-settings",
|
|
58094
|
+
row: "device-manager.deviceMetadata",
|
|
58095
|
+
owner: "device-manager",
|
|
58096
|
+
evidence: { collection: "device-manager:devices" },
|
|
58097
|
+
reason: "hardware-identity metadata is the `metadata` column of the device row; the separate blob existed only because a blob write is whole-value"
|
|
58098
|
+
},
|
|
58099
|
+
{
|
|
58100
|
+
collection: "addon-settings",
|
|
58101
|
+
row: "device-manager.deviceIndex",
|
|
58102
|
+
owner: "device-manager",
|
|
58103
|
+
evidence: { collection: "device-manager:devices" },
|
|
58104
|
+
reason: "derivable: `addonId`/`stableId` are indexed columns, and index membership is the `registered` column"
|
|
58105
|
+
}
|
|
58106
|
+
];
|
|
58107
|
+
function retiredKeyStoreOf(backend) {
|
|
58108
|
+
return {
|
|
58109
|
+
async read(spec) {
|
|
58110
|
+
return require_dist10.asJsonObject(await backend.get({
|
|
58111
|
+
namespace: spec.namespace,
|
|
58112
|
+
collection: spec.collection,
|
|
58113
|
+
key: spec.row
|
|
58114
|
+
}));
|
|
58115
|
+
},
|
|
58116
|
+
async write(spec, value) {
|
|
58117
|
+
await backend.set({
|
|
58118
|
+
namespace: spec.namespace,
|
|
58119
|
+
collection: spec.collection,
|
|
58120
|
+
key: spec.row,
|
|
58121
|
+
value
|
|
58122
|
+
});
|
|
58123
|
+
}
|
|
58124
|
+
};
|
|
58125
|
+
}
|
|
58126
|
+
function planNestedRetiredKeyPurge(blob, spec) {
|
|
58127
|
+
const nestedKeys = spec.mapValueKeys ?? [];
|
|
58128
|
+
if (nestedKeys.length === 0) return [];
|
|
58129
|
+
const found = [];
|
|
58130
|
+
for (const [mapKey, value] of Object.entries(blob)) {
|
|
58131
|
+
const inner = require_dist10.asJsonObject(value);
|
|
58132
|
+
if (inner === null) continue;
|
|
58133
|
+
for (const retired of nestedKeys) if (retired in inner) found.push(`${mapKey}.${retired}`);
|
|
58134
|
+
}
|
|
58135
|
+
return found;
|
|
58136
|
+
}
|
|
58137
|
+
function planRetiredKeyPurge(blob, spec) {
|
|
58138
|
+
const exact = new Set(spec.keys);
|
|
58139
|
+
const found = [];
|
|
58140
|
+
for (const key of Object.keys(blob)) {
|
|
58141
|
+
if (exact.has(key)) {
|
|
58142
|
+
found.push(key);
|
|
58143
|
+
continue;
|
|
58144
|
+
}
|
|
58145
|
+
for (const perNode of spec.perNodeKeys) if (key === perNode || key.startsWith(`${perNode}@`)) {
|
|
58146
|
+
found.push(key);
|
|
58147
|
+
break;
|
|
58148
|
+
}
|
|
58149
|
+
}
|
|
58150
|
+
return found;
|
|
58151
|
+
}
|
|
58152
|
+
async function purgeRetiredSettingsKeys(store, logger, specs = RETIRED_SETTINGS_KEYS, env = process.env) {
|
|
58153
|
+
if (!settingsStoreIsAuthoritativeHere(env)) return [];
|
|
58154
|
+
const results = [];
|
|
58155
|
+
for (const spec of specs) {
|
|
58156
|
+
let blob;
|
|
58157
|
+
try {
|
|
58158
|
+
blob = await store.read(spec);
|
|
58159
|
+
} catch (err) {
|
|
58160
|
+
logger.warn("retired settings keys \u2014 read failed, row left untouched", { meta: {
|
|
58161
|
+
addon: spec.namespace,
|
|
58162
|
+
collection: spec.collection,
|
|
58163
|
+
row: spec.row,
|
|
58164
|
+
error: err instanceof Error ? err.message : String(err)
|
|
58165
|
+
} });
|
|
58166
|
+
continue;
|
|
58167
|
+
}
|
|
58168
|
+
if (blob === null) continue;
|
|
58169
|
+
const doomed = planRetiredKeyPurge(blob, spec);
|
|
58170
|
+
const doomedNested = planNestedRetiredKeyPurge(blob, spec);
|
|
58171
|
+
if (doomed.length === 0 && doomedNested.length === 0) continue;
|
|
58172
|
+
const doomedSet = new Set(doomed);
|
|
58173
|
+
const nestedByMapKey = /* @__PURE__ */ new Map();
|
|
58174
|
+
for (const path of doomedNested) {
|
|
58175
|
+
const cut = path.indexOf(".");
|
|
58176
|
+
const mapKey = path.slice(0, cut);
|
|
58177
|
+
const inner = path.slice(cut + 1);
|
|
58178
|
+
const bucket = nestedByMapKey.get(mapKey) ?? /* @__PURE__ */ new Set();
|
|
58179
|
+
bucket.add(inner);
|
|
58180
|
+
nestedByMapKey.set(mapKey, bucket);
|
|
58181
|
+
}
|
|
58182
|
+
const next = Object.fromEntries(Object.entries(blob).filter(([key]) => !doomedSet.has(key)).map(([key, value]) => {
|
|
58183
|
+
const retired = nestedByMapKey.get(key);
|
|
58184
|
+
if (retired === void 0) return [key, value];
|
|
58185
|
+
const inner = require_dist10.asJsonObject(value);
|
|
58186
|
+
if (inner === null) return [key, value];
|
|
58187
|
+
return [key, Object.fromEntries(Object.entries(inner).filter(([k]) => !retired.has(k)))];
|
|
58188
|
+
}));
|
|
58189
|
+
await store.write(spec, next);
|
|
58190
|
+
const result = {
|
|
58191
|
+
addon: spec.namespace,
|
|
58192
|
+
collection: spec.collection,
|
|
58193
|
+
row: spec.row,
|
|
58194
|
+
keys: doomed,
|
|
58195
|
+
nested: doomedNested
|
|
58196
|
+
};
|
|
58197
|
+
results.push(result);
|
|
58198
|
+
logger.info(`purged ${doomed.length + doomedNested.length} retired settings keys`, { meta: {
|
|
58199
|
+
...result,
|
|
58200
|
+
reason: spec.reason
|
|
58201
|
+
} });
|
|
58202
|
+
}
|
|
58203
|
+
return results;
|
|
58204
|
+
}
|
|
58205
|
+
async function purgeRetiredSettingsRows(store, logger, specs = RETIRED_SETTINGS_ROWS, env = process.env) {
|
|
58206
|
+
if (!settingsStoreIsAuthoritativeHere(env)) return [];
|
|
58207
|
+
const results = [];
|
|
58208
|
+
const evidence = /* @__PURE__ */ new Map();
|
|
58209
|
+
for (const spec of specs) {
|
|
58210
|
+
let present;
|
|
58211
|
+
try {
|
|
58212
|
+
present = await store.hasRow(spec);
|
|
58213
|
+
} catch (err) {
|
|
58214
|
+
logger.warn("retired settings row \u2014 read failed, row left untouched", { meta: {
|
|
58215
|
+
addon: spec.owner,
|
|
58216
|
+
collection: spec.collection,
|
|
58217
|
+
row: spec.row,
|
|
58218
|
+
error: errText(err)
|
|
58219
|
+
} });
|
|
58220
|
+
continue;
|
|
58221
|
+
}
|
|
58222
|
+
if (!present) continue;
|
|
58223
|
+
const successor = spec.evidence.collection;
|
|
58224
|
+
let count = evidence.get(successor);
|
|
58225
|
+
if (count === void 0) {
|
|
58226
|
+
try {
|
|
58227
|
+
count = await store.countRows(successor);
|
|
58228
|
+
} catch (err) {
|
|
58229
|
+
logger.warn("retired settings row \u2014 successor unreadable, row left untouched", { meta: {
|
|
58230
|
+
addon: spec.owner,
|
|
58231
|
+
row: spec.row,
|
|
58232
|
+
successor,
|
|
58233
|
+
error: errText(err)
|
|
58234
|
+
} });
|
|
58235
|
+
count = 0;
|
|
58236
|
+
}
|
|
58237
|
+
evidence.set(successor, count);
|
|
58238
|
+
}
|
|
58239
|
+
if (count === 0) {
|
|
58240
|
+
logger.warn("retired settings row kept \u2014 its successor is empty, migration has not run", { meta: {
|
|
58241
|
+
addon: spec.owner,
|
|
58242
|
+
collection: spec.collection,
|
|
58243
|
+
row: spec.row,
|
|
58244
|
+
successor
|
|
58245
|
+
} });
|
|
58246
|
+
continue;
|
|
58247
|
+
}
|
|
58248
|
+
await store.deleteRow(spec);
|
|
58249
|
+
const result = {
|
|
58250
|
+
owner: spec.owner,
|
|
58251
|
+
collection: spec.collection,
|
|
58252
|
+
row: spec.row
|
|
58253
|
+
};
|
|
58254
|
+
results.push(result);
|
|
58255
|
+
logger.info("purged a retired settings row", { meta: {
|
|
58256
|
+
...result,
|
|
58257
|
+
reason: spec.reason
|
|
58258
|
+
} });
|
|
58259
|
+
}
|
|
58260
|
+
return results;
|
|
58261
|
+
}
|
|
58262
|
+
function errText(err) {
|
|
58263
|
+
return err instanceof Error ? err.message : String(err);
|
|
58264
|
+
}
|
|
58265
|
+
Object.defineProperty(exports, "purgeRetiredSettingsKeys", {
|
|
58266
|
+
enumerable: true,
|
|
58267
|
+
get: function() {
|
|
58268
|
+
return purgeRetiredSettingsKeys;
|
|
58269
|
+
}
|
|
58270
|
+
});
|
|
58271
|
+
Object.defineProperty(exports, "purgeRetiredSettingsRows", {
|
|
58272
|
+
enumerable: true,
|
|
58273
|
+
get: function() {
|
|
58274
|
+
return purgeRetiredSettingsRows;
|
|
58275
|
+
}
|
|
58276
|
+
});
|
|
58277
|
+
Object.defineProperty(exports, "retiredKeyStoreOf", {
|
|
58278
|
+
enumerable: true,
|
|
58279
|
+
get: function() {
|
|
58280
|
+
return retiredKeyStoreOf;
|
|
58281
|
+
}
|
|
58282
|
+
});
|
|
58283
|
+
}
|
|
58284
|
+
});
|
|
58285
|
+
|
|
57915
58286
|
// ../types/dist/canonical-hash-DNV8S5ET.js
|
|
57916
58287
|
var require_canonical_hash_DNV8S5ET = __commonJS({
|
|
57917
58288
|
"../types/dist/canonical-hash-DNV8S5ET.js"(exports) {
|
|
@@ -59735,7 +60106,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
59735
60106
|
[Symbol.toStringTag]: { value: "Module" }
|
|
59736
60107
|
});
|
|
59737
60108
|
require_chunk_Cek0wNdY();
|
|
59738
|
-
var require_dist10 =
|
|
60109
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
60110
|
+
var require_retired_settings_keys = require_retired_settings_keys_B52KsNz8();
|
|
59739
60111
|
var node_crypto = __require("crypto");
|
|
59740
60112
|
var _camstack_types_node = require_node();
|
|
59741
60113
|
var JOB_HISTORY = 20;
|
|
@@ -60023,6 +60395,311 @@ var require_device_manager_addon = __commonJS({
|
|
|
60023
60395
|
}
|
|
60024
60396
|
}
|
|
60025
60397
|
};
|
|
60398
|
+
function tagContribution(contribution, capName, addonId, kind) {
|
|
60399
|
+
const source = kind === "settings" ? "settings" : "live";
|
|
60400
|
+
return {
|
|
60401
|
+
...contribution.tabs ? { tabs: [...contribution.tabs] } : {},
|
|
60402
|
+
sections: contribution.sections.map((section) => ({
|
|
60403
|
+
...section,
|
|
60404
|
+
fields: section.fields.map((field) => tagField(field, capName, addonId, source, kind))
|
|
60405
|
+
}))
|
|
60406
|
+
};
|
|
60407
|
+
}
|
|
60408
|
+
function isFieldRecord(value) {
|
|
60409
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
60410
|
+
}
|
|
60411
|
+
function toWireShape(input) {
|
|
60412
|
+
const out = { sections: input.sections.map((s) => ({
|
|
60413
|
+
id: s.id,
|
|
60414
|
+
title: s.title,
|
|
60415
|
+
...s.description !== void 0 ? { description: s.description } : {},
|
|
60416
|
+
...s.style !== void 0 ? { style: s.style } : {},
|
|
60417
|
+
...s.defaultCollapsed !== void 0 ? { defaultCollapsed: s.defaultCollapsed } : {},
|
|
60418
|
+
...s.columns !== void 0 ? { columns: s.columns } : {},
|
|
60419
|
+
...s.tab !== void 0 ? { tab: s.tab } : {},
|
|
60420
|
+
...s.location !== void 0 ? { location: s.location } : {},
|
|
60421
|
+
...s.order !== void 0 ? { order: s.order } : {},
|
|
60422
|
+
fields: [...s.fields]
|
|
60423
|
+
})) };
|
|
60424
|
+
if (input.tabs) out.tabs = [...input.tabs];
|
|
60425
|
+
return out;
|
|
60426
|
+
}
|
|
60427
|
+
function tagField(field, capName, addonId, source, kind) {
|
|
60428
|
+
if (!isFieldRecord(field)) return field;
|
|
60429
|
+
const f = field;
|
|
60430
|
+
const structuralTypes = /* @__PURE__ */ new Set([
|
|
60431
|
+
"separator",
|
|
60432
|
+
"info",
|
|
60433
|
+
"button"
|
|
60434
|
+
]);
|
|
60435
|
+
if (typeof f.type === "string" && structuralTypes.has(f.type)) return field;
|
|
60436
|
+
const tagged = {
|
|
60437
|
+
...f,
|
|
60438
|
+
source
|
|
60439
|
+
};
|
|
60440
|
+
if (kind === "live" || f.readonlyField === true) tagged.readonlyField = true;
|
|
60441
|
+
else {
|
|
60442
|
+
tagged.writerCapName = capName;
|
|
60443
|
+
tagged.writerAddonId = addonId;
|
|
60444
|
+
}
|
|
60445
|
+
if (f.type === "group") {
|
|
60446
|
+
const children = Array.isArray(f.fields) ? f.fields : [];
|
|
60447
|
+
if (children.length > 0) tagged.fields = children.map((child) => tagField(child, capName, addonId, source, kind));
|
|
60448
|
+
} else if (f.type === "sub-tabs") {
|
|
60449
|
+
const rawTabs = Array.isArray(f.tabs) ? f.tabs : [];
|
|
60450
|
+
if (rawTabs.length > 0) tagged.tabs = rawTabs.map((tab) => {
|
|
60451
|
+
if (!isFieldRecord(tab)) return tab;
|
|
60452
|
+
const tabChildren = Array.isArray(tab.fields) ? tab.fields : [];
|
|
60453
|
+
return {
|
|
60454
|
+
...tab,
|
|
60455
|
+
fields: tabChildren.map((child) => tagField(child, capName, addonId, source, kind))
|
|
60456
|
+
};
|
|
60457
|
+
});
|
|
60458
|
+
}
|
|
60459
|
+
return tagged;
|
|
60460
|
+
}
|
|
60461
|
+
function mergeAggregates(parts) {
|
|
60462
|
+
const tabDecls = /* @__PURE__ */ new Map();
|
|
60463
|
+
const sections = [];
|
|
60464
|
+
const seenSectionIds = /* @__PURE__ */ new Set();
|
|
60465
|
+
for (const part of parts) {
|
|
60466
|
+
if (part.tabs) {
|
|
60467
|
+
for (const t of part.tabs) if (!tabDecls.has(t.id)) tabDecls.set(t.id, t);
|
|
60468
|
+
}
|
|
60469
|
+
for (const s of part.sections) {
|
|
60470
|
+
if (s.id !== void 0) {
|
|
60471
|
+
if (seenSectionIds.has(s.id)) continue;
|
|
60472
|
+
seenSectionIds.add(s.id);
|
|
60473
|
+
}
|
|
60474
|
+
sections.push(s);
|
|
60475
|
+
}
|
|
60476
|
+
}
|
|
60477
|
+
for (const s of sections) {
|
|
60478
|
+
const tabId = s.tab ?? "general";
|
|
60479
|
+
if (tabDecls.has(tabId)) continue;
|
|
60480
|
+
const known = require_dist10.WELL_KNOWN_TAB_MAP[tabId];
|
|
60481
|
+
if (known) tabDecls.set(tabId, {
|
|
60482
|
+
id: known.id,
|
|
60483
|
+
label: known.label,
|
|
60484
|
+
icon: known.icon,
|
|
60485
|
+
order: known.order
|
|
60486
|
+
});
|
|
60487
|
+
else tabDecls.set(tabId, {
|
|
60488
|
+
id: tabId,
|
|
60489
|
+
label: tabId,
|
|
60490
|
+
icon: "wrench",
|
|
60491
|
+
order: 100
|
|
60492
|
+
});
|
|
60493
|
+
}
|
|
60494
|
+
sections.sort((a, b) => {
|
|
60495
|
+
const tabA = a.tab ?? "general";
|
|
60496
|
+
const tabB = b.tab ?? "general";
|
|
60497
|
+
if (tabA !== tabB) {
|
|
60498
|
+
const orderA = tabDecls.get(tabA)?.order ?? 100;
|
|
60499
|
+
const orderB = tabDecls.get(tabB)?.order ?? 100;
|
|
60500
|
+
if (orderA !== orderB) return orderA - orderB;
|
|
60501
|
+
return tabA.localeCompare(tabB);
|
|
60502
|
+
}
|
|
60503
|
+
return (a.order ?? 0) - (b.order ?? 0);
|
|
60504
|
+
});
|
|
60505
|
+
const sortedTabs = [...tabDecls.values()].toSorted((a, b) => (a.order ?? 100) - (b.order ?? 100));
|
|
60506
|
+
const out = { sections };
|
|
60507
|
+
if (sortedTabs.length > 0) out.tabs = sortedTabs;
|
|
60508
|
+
return out;
|
|
60509
|
+
}
|
|
60510
|
+
function wireRemoteNativeCapSync(ctx, remoteNativeCaps) {
|
|
60511
|
+
const localNodeId = ctx.kernel.localNodeId ?? "hub";
|
|
60512
|
+
ctx.eventBus.subscribe({ category: require_dist10.EventCategory.DeviceBindingsChanged }, (event) => {
|
|
60513
|
+
const { deviceId, capName, reason, addonId, nodeId } = event.data;
|
|
60514
|
+
if (nodeId === localNodeId) return;
|
|
60515
|
+
if (reason === "native-registered") {
|
|
60516
|
+
let perDevice = remoteNativeCaps.get(deviceId);
|
|
60517
|
+
if (!perDevice) {
|
|
60518
|
+
perDevice = /* @__PURE__ */ new Map();
|
|
60519
|
+
remoteNativeCaps.set(deviceId, perDevice);
|
|
60520
|
+
}
|
|
60521
|
+
perDevice.set(capName, {
|
|
60522
|
+
addonId,
|
|
60523
|
+
nodeId
|
|
60524
|
+
});
|
|
60525
|
+
} else if (reason === "native-unregistered") {
|
|
60526
|
+
const perDevice = remoteNativeCaps.get(deviceId);
|
|
60527
|
+
if (!perDevice) return;
|
|
60528
|
+
perDevice.delete(capName);
|
|
60529
|
+
if (perDevice.size === 0) remoteNativeCaps.delete(deviceId);
|
|
60530
|
+
}
|
|
60531
|
+
});
|
|
60532
|
+
const cluster = ctx.kernel.cluster;
|
|
60533
|
+
if (cluster) cluster.broker.localBus.on("$node.disconnected", (payload) => {
|
|
60534
|
+
const gone = payload.node.id;
|
|
60535
|
+
const emptyDevices = [];
|
|
60536
|
+
for (const [deviceId, perDevice] of remoteNativeCaps) {
|
|
60537
|
+
const toDelete = [];
|
|
60538
|
+
for (const [capName, entry] of perDevice) if (entry.nodeId === gone) toDelete.push(capName);
|
|
60539
|
+
for (const capName of toDelete) perDevice.delete(capName);
|
|
60540
|
+
if (perDevice.size === 0) emptyDevices.push(deviceId);
|
|
60541
|
+
}
|
|
60542
|
+
for (const deviceId of emptyDevices) remoteNativeCaps.delete(deviceId);
|
|
60543
|
+
});
|
|
60544
|
+
}
|
|
60545
|
+
async function readBindingsStore(deps) {
|
|
60546
|
+
return { deviceBindings: (await deps.ctx.settings.readAddonStore()).deviceBindings ?? {} };
|
|
60547
|
+
}
|
|
60548
|
+
async function writeBindingsStore(deps, next) {
|
|
60549
|
+
await deps.ctx.settings.writeAddonStore({ deviceBindings: next.deviceBindings });
|
|
60550
|
+
}
|
|
60551
|
+
function resolveWrapperNodeId(_wrapperAddonId) {
|
|
60552
|
+
return "hub";
|
|
60553
|
+
}
|
|
60554
|
+
function toRoutableProviderNodeId(nodeId) {
|
|
60555
|
+
const slash = nodeId.indexOf("/");
|
|
60556
|
+
return slash === -1 ? nodeId : nodeId.slice(0, slash);
|
|
60557
|
+
}
|
|
60558
|
+
function resolveRemoteNativeCapFromRegistry(deps, capName, deviceId) {
|
|
60559
|
+
const clusterCaps = deps.ctx.kernel.listClusterNativeCapsForDevice?.(deviceId) ?? deps.ctx.kernel.listClusterNativeCaps?.();
|
|
60560
|
+
if (!clusterCaps) return null;
|
|
60561
|
+
for (const entry of clusterCaps) if (entry.capName === capName && entry.deviceId === deviceId && entry.addonId) return {
|
|
60562
|
+
addonId: entry.addonId,
|
|
60563
|
+
nodeId: entry.nodeId
|
|
60564
|
+
};
|
|
60565
|
+
return null;
|
|
60566
|
+
}
|
|
60567
|
+
function resolveDeviceType(deps, row, deviceId) {
|
|
60568
|
+
const persisted = row?.meta.type;
|
|
60569
|
+
if (typeof persisted === "string" && persisted.length > 0) return persisted;
|
|
60570
|
+
const live = deps.ctx.kernel?.deviceRegistry?.getById(deviceId)?.type;
|
|
60571
|
+
return typeof live === "string" && live.length > 0 ? live : void 0;
|
|
60572
|
+
}
|
|
60573
|
+
async function resolveDevicePresence(deps, row, deviceId) {
|
|
60574
|
+
if (deps.ctx.kernel?.deviceRegistry?.getById(deviceId)) return "present";
|
|
60575
|
+
if (row !== null) return "present";
|
|
60576
|
+
return await deps.rows.count() > 0 ? "absent" : "unknown";
|
|
60577
|
+
}
|
|
60578
|
+
function capAppliesToDeviceType(def, deviceType) {
|
|
60579
|
+
if (deviceType === void 0) return true;
|
|
60580
|
+
const declared = def?.deviceTypes;
|
|
60581
|
+
if (!declared || declared.length === 0) return true;
|
|
60582
|
+
return declared.some((t) => t === deviceType);
|
|
60583
|
+
}
|
|
60584
|
+
async function getBindings(deps, input) {
|
|
60585
|
+
const storeKey = String(input.deviceId);
|
|
60586
|
+
const perDevice = (await readBindingsStore(deps)).deviceBindings[storeKey] ?? {};
|
|
60587
|
+
const row = await deps.rows.get(input.deviceId);
|
|
60588
|
+
const deviceType = resolveDeviceType(deps, row, input.deviceId);
|
|
60589
|
+
const presence = await resolveDevicePresence(deps, row, input.deviceId);
|
|
60590
|
+
const entries = [];
|
|
60591
|
+
const seenCaps = /* @__PURE__ */ new Set();
|
|
60592
|
+
const resolveRemote = (capName) => deps.remoteNativeCaps.get(input.deviceId)?.get(capName) ?? resolveRemoteNativeCapFromRegistry(deps, capName, input.deviceId);
|
|
60593
|
+
for (const [capName, { wrapperAddonId }] of Object.entries(perDevice)) {
|
|
60594
|
+
const hubLocalNative = deps.capabilityRegistry?.getNativeAddonId(capName, input.deviceId) ?? null;
|
|
60595
|
+
const remoteNative = resolveRemote(capName);
|
|
60596
|
+
const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? "";
|
|
60597
|
+
const nativeNodeId = hubLocalNative ? deps.ctx.kernel.localNodeId ?? "hub" : remoteNative?.nodeId ?? deps.ctx.kernel.localNodeId ?? "hub";
|
|
60598
|
+
if (wrapperAddonId === null && !nativeAddonId) {
|
|
60599
|
+
seenCaps.add(capName);
|
|
60600
|
+
continue;
|
|
60601
|
+
}
|
|
60602
|
+
entries.push({
|
|
60603
|
+
capName,
|
|
60604
|
+
kind: wrapperAddonId ? "wrapped" : "native",
|
|
60605
|
+
providerAddonId: wrapperAddonId ?? nativeAddonId,
|
|
60606
|
+
providerNodeId: wrapperAddonId ? resolveWrapperNodeId(wrapperAddonId) : toRoutableProviderNodeId(nativeNodeId),
|
|
60607
|
+
nativeAddonId
|
|
60608
|
+
});
|
|
60609
|
+
seenCaps.add(capName);
|
|
60610
|
+
}
|
|
60611
|
+
if (presence === "absent") deps.ctx.logger.debug("bindings requested for absent device \u2014 returning none", { tags: { deviceId: input.deviceId } });
|
|
60612
|
+
else if (deps.capabilityRegistry) {
|
|
60613
|
+
const skippedForType = [];
|
|
60614
|
+
for (const capName of deps.capabilityRegistry.getCapsWithDefaultWrapper()) {
|
|
60615
|
+
if (seenCaps.has(capName)) continue;
|
|
60616
|
+
if (!capAppliesToDeviceType(deps.capabilityRegistry.getDefinition(capName), deviceType)) {
|
|
60617
|
+
skippedForType.push(capName);
|
|
60618
|
+
continue;
|
|
60619
|
+
}
|
|
60620
|
+
const defaultWrapperAddonId = deps.capabilityRegistry.getDefaultWrapperForCap(capName);
|
|
60621
|
+
if (!defaultWrapperAddonId) continue;
|
|
60622
|
+
const hubLocalNative = deps.capabilityRegistry.getNativeAddonId(capName, input.deviceId) ?? null;
|
|
60623
|
+
const remoteNative = resolveRemote(capName);
|
|
60624
|
+
const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? "";
|
|
60625
|
+
entries.push({
|
|
60626
|
+
capName,
|
|
60627
|
+
kind: "wrapped",
|
|
60628
|
+
providerAddonId: defaultWrapperAddonId,
|
|
60629
|
+
providerNodeId: resolveWrapperNodeId(defaultWrapperAddonId),
|
|
60630
|
+
nativeAddonId
|
|
60631
|
+
});
|
|
60632
|
+
seenCaps.add(capName);
|
|
60633
|
+
}
|
|
60634
|
+
if (skippedForType.length > 0) deps.ctx.logger.debug("getBindings: default wrappers skipped \u2014 deviceTypes mismatch", {
|
|
60635
|
+
tags: { deviceId: input.deviceId },
|
|
60636
|
+
meta: {
|
|
60637
|
+
deviceType,
|
|
60638
|
+
skipped: skippedForType
|
|
60639
|
+
}
|
|
60640
|
+
});
|
|
60641
|
+
}
|
|
60642
|
+
if (deps.capabilityRegistry) for (const capName of deps.capabilityRegistry.getNativeCapsForDevice(input.deviceId)) {
|
|
60643
|
+
if (seenCaps.has(capName)) continue;
|
|
60644
|
+
const nativeAddonId = deps.capabilityRegistry.getNativeAddonId(capName, input.deviceId) ?? "";
|
|
60645
|
+
entries.push({
|
|
60646
|
+
capName,
|
|
60647
|
+
kind: "native",
|
|
60648
|
+
providerAddonId: nativeAddonId,
|
|
60649
|
+
providerNodeId: deps.ctx.kernel.localNodeId ?? "hub",
|
|
60650
|
+
nativeAddonId
|
|
60651
|
+
});
|
|
60652
|
+
seenCaps.add(capName);
|
|
60653
|
+
}
|
|
60654
|
+
const pushFed = deps.remoteNativeCaps.get(input.deviceId);
|
|
60655
|
+
if (pushFed) for (const [capName, info] of pushFed) {
|
|
60656
|
+
if (seenCaps.has(capName)) continue;
|
|
60657
|
+
entries.push({
|
|
60658
|
+
capName,
|
|
60659
|
+
kind: "native",
|
|
60660
|
+
providerAddonId: info.addonId,
|
|
60661
|
+
providerNodeId: toRoutableProviderNodeId(info.nodeId),
|
|
60662
|
+
nativeAddonId: info.addonId
|
|
60663
|
+
});
|
|
60664
|
+
seenCaps.add(capName);
|
|
60665
|
+
}
|
|
60666
|
+
const clusterCaps = deps.ctx.kernel.listClusterNativeCapsForDevice?.(input.deviceId) ?? deps.ctx.kernel.listClusterNativeCaps?.();
|
|
60667
|
+
if (clusterCaps) for (const entry of clusterCaps) {
|
|
60668
|
+
if (entry.deviceId !== input.deviceId) continue;
|
|
60669
|
+
if (seenCaps.has(entry.capName)) continue;
|
|
60670
|
+
if (!entry.addonId) continue;
|
|
60671
|
+
const localNodeId = deps.ctx.kernel.localNodeId ?? "hub";
|
|
60672
|
+
if (entry.nodeId === localNodeId) continue;
|
|
60673
|
+
entries.push({
|
|
60674
|
+
capName: entry.capName,
|
|
60675
|
+
kind: "native",
|
|
60676
|
+
providerAddonId: entry.addonId,
|
|
60677
|
+
providerNodeId: toRoutableProviderNodeId(entry.nodeId),
|
|
60678
|
+
nativeAddonId: entry.addonId
|
|
60679
|
+
});
|
|
60680
|
+
seenCaps.add(entry.capName);
|
|
60681
|
+
}
|
|
60682
|
+
return {
|
|
60683
|
+
deviceId: input.deviceId,
|
|
60684
|
+
entries
|
|
60685
|
+
};
|
|
60686
|
+
}
|
|
60687
|
+
async function getAllBindings(deps) {
|
|
60688
|
+
const ids = /* @__PURE__ */ new Set();
|
|
60689
|
+
for (const row of await deps.rows.listAll()) ids.add(row.meta.id);
|
|
60690
|
+
const registered = deps.ctx.kernel?.deviceRegistry?.getAll() ?? [];
|
|
60691
|
+
for (const device of registered) ids.add(device.id);
|
|
60692
|
+
if (ids.size === 0) {
|
|
60693
|
+
deps.ctx.logger.warn("getAllBindings found no devices \u2014 warm boot will see nothing", { meta: { hasRegistry: deps.ctx.kernel?.deviceRegistry !== void 0 } });
|
|
60694
|
+
return [];
|
|
60695
|
+
}
|
|
60696
|
+
const out = [];
|
|
60697
|
+
for (const deviceId of [...ids].sort((a, b) => a - b)) out.push(await getBindings(deps, { deviceId }));
|
|
60698
|
+
return out;
|
|
60699
|
+
}
|
|
60700
|
+
async function lookupPersistedStableId(deps, deviceId) {
|
|
60701
|
+
return (await deps.rows.get(deviceId))?.meta.stableId;
|
|
60702
|
+
}
|
|
60026
60703
|
var MODE_LABELS = {
|
|
60027
60704
|
auto: "Auto",
|
|
60028
60705
|
day: "Day",
|
|
@@ -60345,118 +61022,6 @@ var require_device_manager_addon = __commonJS({
|
|
|
60345
61022
|
if (reducer.kind !== "settings") throw new Error(`device-config: builderId "${builderId}" is not a settings-based derived-form`);
|
|
60346
61023
|
return reducer.parsePatch(patch);
|
|
60347
61024
|
}
|
|
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
61025
|
function isTransientMoleculerError(err) {
|
|
60461
61026
|
if (err !== null && typeof err === "object") {
|
|
60462
61027
|
const e = err;
|
|
@@ -60548,22 +61113,15 @@ var require_device_manager_addon = __commonJS({
|
|
|
60548
61113
|
}
|
|
60549
61114
|
async function listPersistedByAddon(pctx, input) {
|
|
60550
61115
|
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
|
-
});
|
|
61116
|
+
return (await pctx.metaStore.rows.listByAddon(addonId)).map(({ meta: m }) => ({
|
|
61117
|
+
id: m.id,
|
|
61118
|
+
stableId: m.stableId,
|
|
61119
|
+
type: m.type,
|
|
61120
|
+
name: m.name,
|
|
61121
|
+
location: m.location ?? null,
|
|
61122
|
+
disabled: m.disabled ?? false,
|
|
61123
|
+
parentDeviceId: m.parentDeviceId
|
|
61124
|
+
}));
|
|
60567
61125
|
}
|
|
60568
61126
|
async function listAll(pctx, input) {
|
|
60569
61127
|
const ownerFilter = Reflect.get(input, "addonId");
|
|
@@ -60573,7 +61131,9 @@ var require_device_manager_addon = __commonJS({
|
|
|
60573
61131
|
const camerasOnly = input.isCamera === true;
|
|
60574
61132
|
const results = [];
|
|
60575
61133
|
const seen = /* @__PURE__ */ new Set();
|
|
60576
|
-
const
|
|
61134
|
+
const fleet = addonId ? await pctx.metaStore.rows.listByAddon(addonId) : await pctx.metaStore.rows.listAll();
|
|
61135
|
+
const rowById = /* @__PURE__ */ new Map();
|
|
61136
|
+
for (const row of fleet) rowById.set(row.meta.id, row);
|
|
60577
61137
|
if (pctx.registry) {
|
|
60578
61138
|
const liveEntries = addonId ? pctx.registry.getAllForAddon(addonId).map((device) => ({
|
|
60579
61139
|
addonId,
|
|
@@ -60581,7 +61141,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
60581
61141
|
})) : pctx.registry.getAllWithAddonId();
|
|
60582
61142
|
for (const { addonId: aid, device } of liveEntries) {
|
|
60583
61143
|
const key = String(device.id);
|
|
60584
|
-
const
|
|
61144
|
+
const row = rowById.get(device.id);
|
|
61145
|
+
const info = toDeviceInfo(aid, device, row?.metadata ?? null, row?.meta ?? null);
|
|
60585
61146
|
seen.add(key);
|
|
60586
61147
|
if (camerasOnly && !info.isCamera) continue;
|
|
60587
61148
|
results.push(slim ? {
|
|
@@ -60591,38 +61152,36 @@ var require_device_manager_addon = __commonJS({
|
|
|
60591
61152
|
} : info);
|
|
60592
61153
|
}
|
|
60593
61154
|
}
|
|
60594
|
-
const
|
|
60595
|
-
|
|
60596
|
-
|
|
60597
|
-
|
|
60598
|
-
|
|
60599
|
-
const key = String(m.id);
|
|
60600
|
-
if (seen.has(key)) continue;
|
|
61155
|
+
for (const row of fleet) {
|
|
61156
|
+
const m = row.meta;
|
|
61157
|
+
const aid = m.addonId;
|
|
61158
|
+
const stableId = m.stableId;
|
|
61159
|
+
if (seen.has(String(m.id))) continue;
|
|
60601
61160
|
const persistedType = m.type;
|
|
60602
61161
|
if (camerasOnly && persistedType !== require_dist10.DeviceType.Camera) continue;
|
|
60603
61162
|
const persistedConfig = slim ? {} : await pctx.settings.readDeviceStore(m.id);
|
|
60604
|
-
const metadata = slim ? null :
|
|
61163
|
+
const metadata = slim ? null : row.metadata;
|
|
60605
61164
|
results.push({
|
|
60606
61165
|
id: m.id,
|
|
60607
61166
|
stableId,
|
|
60608
61167
|
addonId: aid,
|
|
60609
61168
|
type: persistedType,
|
|
60610
|
-
name: m
|
|
60611
|
-
location: m
|
|
60612
|
-
disabled: m
|
|
60613
|
-
parentDeviceId: m
|
|
60614
|
-
role: toDeviceRole(m
|
|
61169
|
+
name: m.name,
|
|
61170
|
+
location: m.location ?? null,
|
|
61171
|
+
disabled: m.disabled ?? false,
|
|
61172
|
+
parentDeviceId: m.parentDeviceId ?? null,
|
|
61173
|
+
role: toDeviceRole(m.role),
|
|
60615
61174
|
online: pctx.host.resolveDeviceOnline(m.id, pctx.registry !== null),
|
|
60616
61175
|
probed: pctx.host.resolveDeviceProbed(m.id),
|
|
60617
|
-
features: persistedFeatures(m
|
|
61176
|
+
features: persistedFeatures(m.features),
|
|
60618
61177
|
isCamera: persistedType === require_dist10.DeviceType.Camera,
|
|
60619
61178
|
config: persistedConfig ?? {},
|
|
60620
61179
|
metadata,
|
|
60621
|
-
...m
|
|
60622
|
-
...m
|
|
60623
|
-
...m
|
|
60624
|
-
...m
|
|
60625
|
-
...m
|
|
61180
|
+
...m.integrationId !== void 0 ? { integrationId: m.integrationId } : {},
|
|
61181
|
+
...m.linkDeviceId !== void 0 ? { linkDeviceId: m.linkDeviceId } : {},
|
|
61182
|
+
...m.primaryChildEntityId !== void 0 ? { primaryChildEntityId: m.primaryChildEntityId } : {},
|
|
61183
|
+
...m.childLayout !== void 0 ? { childLayout: m.childLayout } : {},
|
|
61184
|
+
...m.display !== void 0 ? { display: m.display } : {},
|
|
60626
61185
|
...(() => {
|
|
60627
61186
|
if (slim) return {};
|
|
60628
61187
|
const si = buildSourceInfoFromConfig(persistedConfig ?? {}, stableId, aid);
|
|
@@ -60637,19 +61196,15 @@ var require_device_manager_addon = __commonJS({
|
|
|
60637
61196
|
if (pctx.registry) {
|
|
60638
61197
|
const found = resolveDeviceById(pctx.registry, deviceId);
|
|
60639
61198
|
if (found) {
|
|
60640
|
-
const
|
|
60641
|
-
|
|
60642
|
-
const metadata2 = map[key2] ?? null;
|
|
60643
|
-
const metaRow = metaMap[key2] ?? null;
|
|
60644
|
-
return toDeviceInfo(found.addonId, found.device, metadata2, metaRow);
|
|
61199
|
+
const row2 = await pctx.metaStore.getRow(found.device.id);
|
|
61200
|
+
return toDeviceInfo(found.addonId, found.device, row2?.metadata ?? null, row2?.meta ?? null);
|
|
60645
61201
|
}
|
|
60646
61202
|
}
|
|
60647
|
-
const
|
|
60648
|
-
if (
|
|
60649
|
-
const {
|
|
61203
|
+
const row = await pctx.metaStore.getRow(deviceId);
|
|
61204
|
+
if (row === null) return null;
|
|
61205
|
+
const { meta: m, metadata } = row;
|
|
61206
|
+
const { addonId: aid, stableId } = m;
|
|
60650
61207
|
const persistedConfig = await pctx.settings.readDeviceStore(m.id);
|
|
60651
|
-
const key = String(deviceId);
|
|
60652
|
-
const metadata = (await pctx.metaStore.readMetadataMap())[key] ?? null;
|
|
60653
61208
|
const sourceInfoGetDevice = buildSourceInfoFromConfig(persistedConfig ?? {}, stableId, aid);
|
|
60654
61209
|
return {
|
|
60655
61210
|
id: deviceId,
|
|
@@ -60688,26 +61243,25 @@ var require_device_manager_addon = __commonJS({
|
|
|
60688
61243
|
}
|
|
60689
61244
|
const results = [];
|
|
60690
61245
|
const seen = /* @__PURE__ */ new Set();
|
|
60691
|
-
const
|
|
61246
|
+
const childRows = await pctx.metaStore.rows.listByParent(parentDeviceId);
|
|
61247
|
+
const rowById = /* @__PURE__ */ new Map();
|
|
61248
|
+
for (const row of childRows) rowById.set(row.meta.id, row);
|
|
60692
61249
|
if (pctx.registry) {
|
|
60693
61250
|
const liveChildren = pctx.registry.getChildren(parentDeviceId);
|
|
60694
61251
|
for (const device of liveChildren) {
|
|
60695
61252
|
const key = String(device.id);
|
|
60696
|
-
const
|
|
60697
|
-
|
|
60698
|
-
results.push(toDeviceInfo(ownerAddonId, device, metadata, metaRow));
|
|
61253
|
+
const row = rowById.get(device.id);
|
|
61254
|
+
results.push(toDeviceInfo(ownerAddonId, device, row?.metadata ?? null, row?.meta ?? null));
|
|
60699
61255
|
seen.add(key);
|
|
60700
61256
|
}
|
|
60701
61257
|
}
|
|
60702
|
-
const
|
|
60703
|
-
|
|
60704
|
-
|
|
60705
|
-
for (const childStableId of persistedChildren) {
|
|
60706
|
-
const m = ownerMetaByStableId.get(childStableId);
|
|
61258
|
+
for (const row of childRows) {
|
|
61259
|
+
const m = row.meta;
|
|
61260
|
+
const childStableId = m.stableId;
|
|
60707
61261
|
const key = String(m.id);
|
|
60708
61262
|
if (seen.has(key)) continue;
|
|
60709
61263
|
const persistedConfig = await pctx.settings.readDeviceStore(m.id);
|
|
60710
|
-
const metadata =
|
|
61264
|
+
const metadata = row.metadata;
|
|
60711
61265
|
const sourceInfoChild = buildSourceInfoFromConfig(persistedConfig ?? {}, childStableId, ownerAddonId);
|
|
60712
61266
|
results.push({
|
|
60713
61267
|
id: m.id,
|
|
@@ -60851,16 +61405,10 @@ var require_device_manager_addon = __commonJS({
|
|
|
60851
61405
|
}
|
|
60852
61406
|
async function removeByIntegration(pctx, input) {
|
|
60853
61407
|
const { integrationId } = input;
|
|
60854
|
-
const
|
|
60855
|
-
const parentKeys = Object.keys(meta).filter((key) => {
|
|
60856
|
-
const m = meta[key];
|
|
60857
|
-
return m !== void 0 && m.integrationId === integrationId && m.parentDeviceId === null;
|
|
60858
|
-
});
|
|
61408
|
+
const parents = (await pctx.metaStore.rows.listByIntegration(integrationId)).filter((row) => row.meta.parentDeviceId === null);
|
|
60859
61409
|
let removed = 0;
|
|
60860
|
-
for (const
|
|
60861
|
-
|
|
60862
|
-
if (!m) continue;
|
|
60863
|
-
await pctx.provider.remove({ deviceId: m.id });
|
|
61410
|
+
for (const parent of parents) {
|
|
61411
|
+
await pctx.provider.remove({ deviceId: parent.meta.id });
|
|
60864
61412
|
removed++;
|
|
60865
61413
|
}
|
|
60866
61414
|
return { removed };
|
|
@@ -61249,204 +61797,6 @@ var require_device_manager_addon = __commonJS({
|
|
|
61249
61797
|
driverPatch
|
|
61250
61798
|
};
|
|
61251
61799
|
}
|
|
61252
|
-
function wireRemoteNativeCapSync(ctx, remoteNativeCaps) {
|
|
61253
|
-
const localNodeId = ctx.kernel.localNodeId ?? "hub";
|
|
61254
|
-
ctx.eventBus.subscribe({ category: require_dist10.EventCategory.DeviceBindingsChanged }, (event) => {
|
|
61255
|
-
const { deviceId, capName, reason, addonId, nodeId } = event.data;
|
|
61256
|
-
if (nodeId === localNodeId) return;
|
|
61257
|
-
if (reason === "native-registered") {
|
|
61258
|
-
let perDevice = remoteNativeCaps.get(deviceId);
|
|
61259
|
-
if (!perDevice) {
|
|
61260
|
-
perDevice = /* @__PURE__ */ new Map();
|
|
61261
|
-
remoteNativeCaps.set(deviceId, perDevice);
|
|
61262
|
-
}
|
|
61263
|
-
perDevice.set(capName, {
|
|
61264
|
-
addonId,
|
|
61265
|
-
nodeId
|
|
61266
|
-
});
|
|
61267
|
-
} else if (reason === "native-unregistered") {
|
|
61268
|
-
const perDevice = remoteNativeCaps.get(deviceId);
|
|
61269
|
-
if (!perDevice) return;
|
|
61270
|
-
perDevice.delete(capName);
|
|
61271
|
-
if (perDevice.size === 0) remoteNativeCaps.delete(deviceId);
|
|
61272
|
-
}
|
|
61273
|
-
});
|
|
61274
|
-
const cluster = ctx.kernel.cluster;
|
|
61275
|
-
if (cluster) cluster.broker.localBus.on("$node.disconnected", (payload) => {
|
|
61276
|
-
const gone = payload.node.id;
|
|
61277
|
-
const emptyDevices = [];
|
|
61278
|
-
for (const [deviceId, perDevice] of remoteNativeCaps) {
|
|
61279
|
-
const toDelete = [];
|
|
61280
|
-
for (const [capName, entry] of perDevice) if (entry.nodeId === gone) toDelete.push(capName);
|
|
61281
|
-
for (const capName of toDelete) perDevice.delete(capName);
|
|
61282
|
-
if (perDevice.size === 0) emptyDevices.push(deviceId);
|
|
61283
|
-
}
|
|
61284
|
-
for (const deviceId of emptyDevices) remoteNativeCaps.delete(deviceId);
|
|
61285
|
-
});
|
|
61286
|
-
}
|
|
61287
|
-
async function readBindingsStore(deps) {
|
|
61288
|
-
return { deviceBindings: (await deps.ctx.settings.readAddonStore()).deviceBindings ?? {} };
|
|
61289
|
-
}
|
|
61290
|
-
async function writeBindingsStore(deps, next) {
|
|
61291
|
-
await deps.ctx.settings.writeAddonStore({ deviceBindings: next.deviceBindings });
|
|
61292
|
-
}
|
|
61293
|
-
function resolveWrapperNodeId(_wrapperAddonId) {
|
|
61294
|
-
return "hub";
|
|
61295
|
-
}
|
|
61296
|
-
function toRoutableProviderNodeId(nodeId) {
|
|
61297
|
-
const slash = nodeId.indexOf("/");
|
|
61298
|
-
return slash === -1 ? nodeId : nodeId.slice(0, slash);
|
|
61299
|
-
}
|
|
61300
|
-
function resolveRemoteNativeCapFromRegistry(deps, capName, deviceId) {
|
|
61301
|
-
const clusterCaps = deps.ctx.kernel.listClusterNativeCapsForDevice?.(deviceId) ?? deps.ctx.kernel.listClusterNativeCaps?.();
|
|
61302
|
-
if (!clusterCaps) return null;
|
|
61303
|
-
for (const entry of clusterCaps) if (entry.capName === capName && entry.deviceId === deviceId && entry.addonId) return {
|
|
61304
|
-
addonId: entry.addonId,
|
|
61305
|
-
nodeId: entry.nodeId
|
|
61306
|
-
};
|
|
61307
|
-
return null;
|
|
61308
|
-
}
|
|
61309
|
-
function resolveDeviceType(deps, rawStore, deviceId) {
|
|
61310
|
-
const persisted = rawStore.deviceMeta?.[String(deviceId)]?.type;
|
|
61311
|
-
if (typeof persisted === "string" && persisted.length > 0) return persisted;
|
|
61312
|
-
const live = deps.ctx.kernel?.deviceRegistry?.getById(deviceId)?.type;
|
|
61313
|
-
return typeof live === "string" && live.length > 0 ? live : void 0;
|
|
61314
|
-
}
|
|
61315
|
-
function resolveDevicePresence(deps, rawStore, deviceId) {
|
|
61316
|
-
if (deps.ctx.kernel?.deviceRegistry?.getById(deviceId)) return "present";
|
|
61317
|
-
const meta = rawStore.deviceMeta;
|
|
61318
|
-
if (meta && Object.keys(meta).length > 0) return meta[String(deviceId)] === void 0 ? "absent" : "present";
|
|
61319
|
-
return "unknown";
|
|
61320
|
-
}
|
|
61321
|
-
function capAppliesToDeviceType(def, deviceType) {
|
|
61322
|
-
if (deviceType === void 0) return true;
|
|
61323
|
-
const declared = def?.deviceTypes;
|
|
61324
|
-
if (!declared || declared.length === 0) return true;
|
|
61325
|
-
return declared.some((t) => t === deviceType);
|
|
61326
|
-
}
|
|
61327
|
-
async function getBindings(deps, input) {
|
|
61328
|
-
const storeKey = String(input.deviceId);
|
|
61329
|
-
const rawStore = await deps.ctx.settings.readAddonStore();
|
|
61330
|
-
const perDevice = (rawStore.deviceBindings ?? {})[storeKey] ?? {};
|
|
61331
|
-
const deviceType = resolveDeviceType(deps, rawStore, input.deviceId);
|
|
61332
|
-
const presence = resolveDevicePresence(deps, rawStore, input.deviceId);
|
|
61333
|
-
const entries = [];
|
|
61334
|
-
const seenCaps = /* @__PURE__ */ new Set();
|
|
61335
|
-
const resolveRemote = (capName) => deps.remoteNativeCaps.get(input.deviceId)?.get(capName) ?? resolveRemoteNativeCapFromRegistry(deps, capName, input.deviceId);
|
|
61336
|
-
for (const [capName, { wrapperAddonId }] of Object.entries(perDevice)) {
|
|
61337
|
-
const hubLocalNative = deps.capabilityRegistry?.getNativeAddonId(capName, input.deviceId) ?? null;
|
|
61338
|
-
const remoteNative = resolveRemote(capName);
|
|
61339
|
-
const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? "";
|
|
61340
|
-
const nativeNodeId = hubLocalNative ? deps.ctx.kernel.localNodeId ?? "hub" : remoteNative?.nodeId ?? deps.ctx.kernel.localNodeId ?? "hub";
|
|
61341
|
-
if (wrapperAddonId === null && !nativeAddonId) {
|
|
61342
|
-
seenCaps.add(capName);
|
|
61343
|
-
continue;
|
|
61344
|
-
}
|
|
61345
|
-
entries.push({
|
|
61346
|
-
capName,
|
|
61347
|
-
kind: wrapperAddonId ? "wrapped" : "native",
|
|
61348
|
-
providerAddonId: wrapperAddonId ?? nativeAddonId,
|
|
61349
|
-
providerNodeId: wrapperAddonId ? resolveWrapperNodeId(wrapperAddonId) : toRoutableProviderNodeId(nativeNodeId),
|
|
61350
|
-
nativeAddonId
|
|
61351
|
-
});
|
|
61352
|
-
seenCaps.add(capName);
|
|
61353
|
-
}
|
|
61354
|
-
if (presence === "absent") deps.ctx.logger.debug("bindings requested for absent device \u2014 returning none", { tags: { deviceId: input.deviceId } });
|
|
61355
|
-
else if (deps.capabilityRegistry) {
|
|
61356
|
-
const skippedForType = [];
|
|
61357
|
-
for (const capName of deps.capabilityRegistry.getCapsWithDefaultWrapper()) {
|
|
61358
|
-
if (seenCaps.has(capName)) continue;
|
|
61359
|
-
if (!capAppliesToDeviceType(deps.capabilityRegistry.getDefinition(capName), deviceType)) {
|
|
61360
|
-
skippedForType.push(capName);
|
|
61361
|
-
continue;
|
|
61362
|
-
}
|
|
61363
|
-
const defaultWrapperAddonId = deps.capabilityRegistry.getDefaultWrapperForCap(capName);
|
|
61364
|
-
if (!defaultWrapperAddonId) continue;
|
|
61365
|
-
const hubLocalNative = deps.capabilityRegistry.getNativeAddonId(capName, input.deviceId) ?? null;
|
|
61366
|
-
const remoteNative = resolveRemote(capName);
|
|
61367
|
-
const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? "";
|
|
61368
|
-
entries.push({
|
|
61369
|
-
capName,
|
|
61370
|
-
kind: "wrapped",
|
|
61371
|
-
providerAddonId: defaultWrapperAddonId,
|
|
61372
|
-
providerNodeId: resolveWrapperNodeId(defaultWrapperAddonId),
|
|
61373
|
-
nativeAddonId
|
|
61374
|
-
});
|
|
61375
|
-
seenCaps.add(capName);
|
|
61376
|
-
}
|
|
61377
|
-
if (skippedForType.length > 0) deps.ctx.logger.debug("getBindings: default wrappers skipped \u2014 deviceTypes mismatch", {
|
|
61378
|
-
tags: { deviceId: input.deviceId },
|
|
61379
|
-
meta: {
|
|
61380
|
-
deviceType,
|
|
61381
|
-
skipped: skippedForType
|
|
61382
|
-
}
|
|
61383
|
-
});
|
|
61384
|
-
}
|
|
61385
|
-
if (deps.capabilityRegistry) for (const capName of deps.capabilityRegistry.getNativeCapsForDevice(input.deviceId)) {
|
|
61386
|
-
if (seenCaps.has(capName)) continue;
|
|
61387
|
-
const nativeAddonId = deps.capabilityRegistry.getNativeAddonId(capName, input.deviceId) ?? "";
|
|
61388
|
-
entries.push({
|
|
61389
|
-
capName,
|
|
61390
|
-
kind: "native",
|
|
61391
|
-
providerAddonId: nativeAddonId,
|
|
61392
|
-
providerNodeId: deps.ctx.kernel.localNodeId ?? "hub",
|
|
61393
|
-
nativeAddonId
|
|
61394
|
-
});
|
|
61395
|
-
seenCaps.add(capName);
|
|
61396
|
-
}
|
|
61397
|
-
const pushFed = deps.remoteNativeCaps.get(input.deviceId);
|
|
61398
|
-
if (pushFed) for (const [capName, info] of pushFed) {
|
|
61399
|
-
if (seenCaps.has(capName)) continue;
|
|
61400
|
-
entries.push({
|
|
61401
|
-
capName,
|
|
61402
|
-
kind: "native",
|
|
61403
|
-
providerAddonId: info.addonId,
|
|
61404
|
-
providerNodeId: toRoutableProviderNodeId(info.nodeId),
|
|
61405
|
-
nativeAddonId: info.addonId
|
|
61406
|
-
});
|
|
61407
|
-
seenCaps.add(capName);
|
|
61408
|
-
}
|
|
61409
|
-
const clusterCaps = deps.ctx.kernel.listClusterNativeCapsForDevice?.(input.deviceId) ?? deps.ctx.kernel.listClusterNativeCaps?.();
|
|
61410
|
-
if (clusterCaps) for (const entry of clusterCaps) {
|
|
61411
|
-
if (entry.deviceId !== input.deviceId) continue;
|
|
61412
|
-
if (seenCaps.has(entry.capName)) continue;
|
|
61413
|
-
if (!entry.addonId) continue;
|
|
61414
|
-
const localNodeId = deps.ctx.kernel.localNodeId ?? "hub";
|
|
61415
|
-
if (entry.nodeId === localNodeId) continue;
|
|
61416
|
-
entries.push({
|
|
61417
|
-
capName: entry.capName,
|
|
61418
|
-
kind: "native",
|
|
61419
|
-
providerAddonId: entry.addonId,
|
|
61420
|
-
providerNodeId: toRoutableProviderNodeId(entry.nodeId),
|
|
61421
|
-
nativeAddonId: entry.addonId
|
|
61422
|
-
});
|
|
61423
|
-
seenCaps.add(entry.capName);
|
|
61424
|
-
}
|
|
61425
|
-
return {
|
|
61426
|
-
deviceId: input.deviceId,
|
|
61427
|
-
entries
|
|
61428
|
-
};
|
|
61429
|
-
}
|
|
61430
|
-
async function getAllBindings(deps) {
|
|
61431
|
-
const store = await deps.ctx.settings.readAddonStore();
|
|
61432
|
-
const ids = /* @__PURE__ */ new Set();
|
|
61433
|
-
for (const key of Object.keys(store.deviceMeta ?? {})) {
|
|
61434
|
-
const id = Number(key);
|
|
61435
|
-
if (Number.isInteger(id)) ids.add(id);
|
|
61436
|
-
}
|
|
61437
|
-
const registered = deps.ctx.kernel?.deviceRegistry?.getAll() ?? [];
|
|
61438
|
-
for (const device of registered) ids.add(device.id);
|
|
61439
|
-
if (ids.size === 0) {
|
|
61440
|
-
deps.ctx.logger.warn("getAllBindings found no devices \u2014 warm boot will see nothing", { meta: { hasRegistry: deps.ctx.kernel?.deviceRegistry !== void 0 } });
|
|
61441
|
-
return [];
|
|
61442
|
-
}
|
|
61443
|
-
const out = [];
|
|
61444
|
-
for (const deviceId of [...ids].sort((a, b) => a - b)) out.push(await getBindings(deps, { deviceId }));
|
|
61445
|
-
return out;
|
|
61446
|
-
}
|
|
61447
|
-
async function lookupPersistedStableId(deps, deviceId) {
|
|
61448
|
-
return ((await deps.ctx.settings.readAddonStore()).deviceMeta ?? {})[String(deviceId)]?.stableId;
|
|
61449
|
-
}
|
|
61450
61800
|
async function getDeviceAggregate(deps, deviceId, kind) {
|
|
61451
61801
|
const registry = deps.capabilityRegistry;
|
|
61452
61802
|
if (!registry) {
|
|
@@ -62003,7 +62353,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62003
62353
|
if (wireable) caps.push(wireable);
|
|
62004
62354
|
}
|
|
62005
62355
|
if (input.includeSynthesizable === true) {
|
|
62006
|
-
const deviceType = (
|
|
62356
|
+
const deviceType = (await deps.bindingsDeps.rows.get(deviceId))?.meta.type;
|
|
62007
62357
|
if (deviceType !== void 0) for (const def of require_dist10.ALL_CAPABILITY_DEFINITIONS) {
|
|
62008
62358
|
if (seen.has(def.name)) continue;
|
|
62009
62359
|
if (def.scope !== "device" || def.kind === "wrapper") continue;
|
|
@@ -62091,20 +62441,10 @@ var require_device_manager_addon = __commonJS({
|
|
|
62091
62441
|
return chain;
|
|
62092
62442
|
}
|
|
62093
62443
|
};
|
|
62094
|
-
async function stampIntegrationId(metaStore,
|
|
62444
|
+
async function stampIntegrationId(metaStore, ctx, deviceId, integrationId) {
|
|
62095
62445
|
await metaStore.withMetaWriteLock(async () => {
|
|
62096
|
-
|
|
62097
|
-
|
|
62098
|
-
const { meta: m } = persisted;
|
|
62099
|
-
const key = String(deviceId);
|
|
62100
|
-
const allMeta = await metaStore.readMeta();
|
|
62101
|
-
await settings.writeAddonStore({ deviceMeta: {
|
|
62102
|
-
...allMeta,
|
|
62103
|
-
[key]: {
|
|
62104
|
-
...m,
|
|
62105
|
-
integrationId
|
|
62106
|
-
}
|
|
62107
|
-
} });
|
|
62446
|
+
if (!await metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] stampIntegrationId: unknown device id=${deviceId}`);
|
|
62447
|
+
await metaStore.rows.patch(deviceId, { integrationId });
|
|
62108
62448
|
});
|
|
62109
62449
|
ctx.eventBus.emit({
|
|
62110
62450
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62124,70 +62464,54 @@ var require_device_manager_addon = __commonJS({
|
|
|
62124
62464
|
async function allocateDeviceId(pctx, input) {
|
|
62125
62465
|
const { addonId, stableId } = input;
|
|
62126
62466
|
return await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62127
|
-
const
|
|
62128
|
-
|
|
62129
|
-
if (existing) return { id: existing.id };
|
|
62467
|
+
const existing = await pctx.metaStore.rows.findByStableId(addonId, stableId);
|
|
62468
|
+
if (existing) return { id: existing.meta.id };
|
|
62130
62469
|
const id = await pctx.metaStore.allocateNextDeviceId();
|
|
62131
|
-
await pctx.
|
|
62132
|
-
|
|
62133
|
-
|
|
62134
|
-
|
|
62135
|
-
|
|
62136
|
-
|
|
62137
|
-
|
|
62138
|
-
|
|
62139
|
-
|
|
62140
|
-
|
|
62141
|
-
|
|
62142
|
-
|
|
62143
|
-
}
|
|
62470
|
+
await pctx.metaStore.rows.upsert({
|
|
62471
|
+
addonId,
|
|
62472
|
+
stableId,
|
|
62473
|
+
type: "generic",
|
|
62474
|
+
name: stableId,
|
|
62475
|
+
location: null,
|
|
62476
|
+
disabled: false,
|
|
62477
|
+
parentDeviceId: null,
|
|
62478
|
+
id
|
|
62479
|
+
}, {
|
|
62480
|
+
registered: false,
|
|
62481
|
+
metadata: null
|
|
62482
|
+
});
|
|
62144
62483
|
return { id };
|
|
62145
62484
|
});
|
|
62146
62485
|
}
|
|
62147
62486
|
async function registerDevice(pctx, input) {
|
|
62148
62487
|
const { addonId, stableId, id, type, name, parentDeviceId, features, config } = input;
|
|
62149
|
-
const key = String(id);
|
|
62150
62488
|
const featuresArr = Array.isArray(features) ? [...features] : [];
|
|
62151
62489
|
const { isFirstRegistration, exportFingerprint, fingerprintChanged } = await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62152
|
-
const
|
|
62153
|
-
const
|
|
62154
|
-
const
|
|
62155
|
-
if (!wasInIndex) await pctx.settings.writeAddonStore({ deviceIndex: {
|
|
62156
|
-
...index,
|
|
62157
|
-
[addonId]: [...existing, stableId]
|
|
62158
|
-
} });
|
|
62159
|
-
const meta = await pctx.metaStore.readMeta();
|
|
62160
|
-
const existingMeta = meta[key];
|
|
62490
|
+
const existingRow = await pctx.metaStore.rows.get(id);
|
|
62491
|
+
const existingMeta = existingRow?.meta;
|
|
62492
|
+
const wasRegistered = existingRow?.registered ?? false;
|
|
62161
62493
|
const wasUserNamed = existingMeta?.userNamed ?? (existingMeta !== void 0 && existingMeta.name !== stableId);
|
|
62162
62494
|
const resolvedName = wasUserNamed && existingMeta !== void 0 ? existingMeta.name : name;
|
|
62163
|
-
const isFirst = !existingMeta || !
|
|
62495
|
+
const isFirst = !existingMeta || !wasRegistered;
|
|
62164
62496
|
const fingerprint = (0, _camstack_types_node.canonicalDeviceFingerprint)({
|
|
62165
62497
|
deviceId: id,
|
|
62166
62498
|
deviceType: type,
|
|
62167
62499
|
features: featuresArr
|
|
62168
62500
|
});
|
|
62169
|
-
await pctx.
|
|
62170
|
-
|
|
62171
|
-
|
|
62172
|
-
|
|
62173
|
-
|
|
62174
|
-
|
|
62175
|
-
|
|
62176
|
-
|
|
62177
|
-
|
|
62178
|
-
|
|
62179
|
-
|
|
62180
|
-
|
|
62181
|
-
|
|
62182
|
-
|
|
62183
|
-
...existingMeta?.role !== void 0 ? { role: existingMeta.role } : {},
|
|
62184
|
-
...existingMeta?.display !== void 0 ? { display: existingMeta.display } : {},
|
|
62185
|
-
parentDeviceId,
|
|
62186
|
-
id,
|
|
62187
|
-
features: featuresArr,
|
|
62188
|
-
exportFingerprint: fingerprint
|
|
62189
|
-
}
|
|
62190
|
-
} });
|
|
62501
|
+
await pctx.metaStore.rows.upsertRegistration({
|
|
62502
|
+
deviceId: id,
|
|
62503
|
+
addonId,
|
|
62504
|
+
stableId,
|
|
62505
|
+
type,
|
|
62506
|
+
name: resolvedName,
|
|
62507
|
+
userNamed: wasUserNamed,
|
|
62508
|
+
location: existingMeta?.location ?? null,
|
|
62509
|
+
disabled: existingMeta?.disabled ?? false,
|
|
62510
|
+
parentDeviceId: parentDeviceId ?? null,
|
|
62511
|
+
registered: true,
|
|
62512
|
+
features: featuresArr,
|
|
62513
|
+
exportFingerprint: fingerprint
|
|
62514
|
+
});
|
|
62191
62515
|
return {
|
|
62192
62516
|
isFirstRegistration: isFirst,
|
|
62193
62517
|
exportFingerprint: fingerprint,
|
|
@@ -62247,26 +62571,9 @@ var require_device_manager_addon = __commonJS({
|
|
|
62247
62571
|
const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
|
|
62248
62572
|
if (!persisted) return;
|
|
62249
62573
|
const { addonId, stableId, meta: persistedMeta } = persisted;
|
|
62250
|
-
const key = String(deviceId);
|
|
62251
62574
|
const deviceName = persistedMeta.name;
|
|
62252
62575
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62253
|
-
|
|
62254
|
-
const remaining = (index[addonId] ?? []).filter((sid) => sid !== stableId);
|
|
62255
|
-
const updatedIndex = remaining.length > 0 ? {
|
|
62256
|
-
...index,
|
|
62257
|
-
[addonId]: remaining
|
|
62258
|
-
} : (() => {
|
|
62259
|
-
const { [addonId]: _removed, ...rest } = index;
|
|
62260
|
-
return rest;
|
|
62261
|
-
})();
|
|
62262
|
-
await pctx.settings.writeAddonStore({ deviceIndex: updatedIndex });
|
|
62263
|
-
const { [key]: _removedMeta, ...restMeta } = await pctx.metaStore.readMeta();
|
|
62264
|
-
await pctx.settings.writeAddonStore({ deviceMeta: restMeta });
|
|
62265
|
-
const map = await pctx.metaStore.readMetadataMap();
|
|
62266
|
-
if (key in map) {
|
|
62267
|
-
const { [key]: _removedMetadata, ...restMap } = map;
|
|
62268
|
-
await pctx.settings.writeAddonStore({ deviceMetadata: restMap });
|
|
62269
|
-
}
|
|
62576
|
+
await pctx.metaStore.rows.remove(deviceId);
|
|
62270
62577
|
});
|
|
62271
62578
|
await pctx.settings.clearDeviceStore(deviceId);
|
|
62272
62579
|
await pctx.settings.clearDeviceRuntimeState(deviceId);
|
|
@@ -62310,11 +62617,10 @@ var require_device_manager_addon = __commonJS({
|
|
|
62310
62617
|
}
|
|
62311
62618
|
async function loadMeta(pctx, input) {
|
|
62312
62619
|
const { deviceId } = input;
|
|
62313
|
-
const
|
|
62314
|
-
if (
|
|
62315
|
-
const {
|
|
62316
|
-
const
|
|
62317
|
-
const metadata = (await pctx.metaStore.readMetadataMap())[key] ?? null;
|
|
62620
|
+
const row = await pctx.metaStore.getRow(deviceId);
|
|
62621
|
+
if (row === null) return null;
|
|
62622
|
+
const { meta: m, metadata } = row;
|
|
62623
|
+
const { addonId, stableId } = m;
|
|
62318
62624
|
return {
|
|
62319
62625
|
id: m.id,
|
|
62320
62626
|
stableId,
|
|
@@ -62337,32 +62643,26 @@ var require_device_manager_addon = __commonJS({
|
|
|
62337
62643
|
const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
|
|
62338
62644
|
if (!persisted) throw new Error(`[device-manager] setName: unknown device id=${deviceId}`);
|
|
62339
62645
|
const { meta: m } = persisted;
|
|
62340
|
-
const key = String(deviceId);
|
|
62341
62646
|
const oldName = m.name;
|
|
62342
|
-
|
|
62343
|
-
|
|
62344
|
-
|
|
62345
|
-
|
|
62346
|
-
|
|
62347
|
-
|
|
62348
|
-
|
|
62647
|
+
await pctx.metaStore.rows.patch(deviceId, {
|
|
62648
|
+
name,
|
|
62649
|
+
userNamed: true
|
|
62650
|
+
});
|
|
62651
|
+
if (oldName.length > 0 && oldName !== name) {
|
|
62652
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
62653
|
+
for (const row of await pctx.metaStore.rows.listByParent(deviceId)) candidates.set(row.meta.id, row.meta);
|
|
62654
|
+
for (const row of await pctx.metaStore.rows.listAll()) if (row.meta.linkDeviceId === deviceId) candidates.set(row.meta.id, row.meta);
|
|
62655
|
+
candidates.delete(deviceId);
|
|
62656
|
+
for (const childMeta of candidates.values()) {
|
|
62657
|
+
if (childMeta.name !== oldName && !childMeta.name.startsWith(`${oldName} `)) continue;
|
|
62658
|
+
const childName = childMeta.name === oldName ? name : `${name}${childMeta.name.slice(oldName.length)}`;
|
|
62659
|
+
await pctx.metaStore.rows.patch(childMeta.id, { name: childName });
|
|
62660
|
+
cascaded.push({
|
|
62661
|
+
id: childMeta.id,
|
|
62662
|
+
name: childName
|
|
62663
|
+
});
|
|
62349
62664
|
}
|
|
62350
|
-
};
|
|
62351
|
-
if (oldName.length > 0 && oldName !== name) for (const [childKey, childMeta] of Object.entries(allMeta)) {
|
|
62352
|
-
if (childKey === key) continue;
|
|
62353
|
-
if (!(childMeta.parentDeviceId === deviceId || childMeta.linkDeviceId === deviceId)) continue;
|
|
62354
|
-
if (childMeta.name !== oldName && !childMeta.name.startsWith(`${oldName} `)) continue;
|
|
62355
|
-
const childName = childMeta.name === oldName ? name : `${name}${childMeta.name.slice(oldName.length)}`;
|
|
62356
|
-
nextMeta[childKey] = {
|
|
62357
|
-
...childMeta,
|
|
62358
|
-
name: childName
|
|
62359
|
-
};
|
|
62360
|
-
cascaded.push({
|
|
62361
|
-
id: Number(childKey),
|
|
62362
|
-
name: childName
|
|
62363
|
-
});
|
|
62364
62665
|
}
|
|
62365
|
-
await pctx.settings.writeAddonStore({ deviceMeta: nextMeta });
|
|
62366
62666
|
});
|
|
62367
62667
|
pctx.host.ctx.eventBus.emit({
|
|
62368
62668
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62396,18 +62696,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62396
62696
|
async function setLocation(pctx, input) {
|
|
62397
62697
|
const { deviceId, location } = input;
|
|
62398
62698
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62399
|
-
|
|
62400
|
-
|
|
62401
|
-
const { meta: m } = persisted;
|
|
62402
|
-
const key = String(deviceId);
|
|
62403
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62404
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62405
|
-
...allMeta,
|
|
62406
|
-
[key]: {
|
|
62407
|
-
...m,
|
|
62408
|
-
location
|
|
62409
|
-
}
|
|
62410
|
-
} });
|
|
62699
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setLocation: unknown device id=${deviceId}`);
|
|
62700
|
+
await pctx.metaStore.rows.patch(deviceId, { location });
|
|
62411
62701
|
});
|
|
62412
62702
|
pctx.host.ctx.eventBus.emit({
|
|
62413
62703
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62427,18 +62717,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62427
62717
|
async function setType(pctx, input) {
|
|
62428
62718
|
const { deviceId, type } = input;
|
|
62429
62719
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62430
|
-
|
|
62431
|
-
|
|
62432
|
-
const { meta: m } = persisted;
|
|
62433
|
-
const key = String(deviceId);
|
|
62434
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62435
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62436
|
-
...allMeta,
|
|
62437
|
-
[key]: {
|
|
62438
|
-
...m,
|
|
62439
|
-
type
|
|
62440
|
-
}
|
|
62441
|
-
} });
|
|
62720
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setType: unknown device id=${deviceId}`);
|
|
62721
|
+
await pctx.metaStore.rows.patch(deviceId, { type });
|
|
62442
62722
|
});
|
|
62443
62723
|
pctx.host.ctx.eventBus.emit({
|
|
62444
62724
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62461,18 +62741,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62461
62741
|
async function setLinkDeviceId(pctx, input) {
|
|
62462
62742
|
const { deviceId, linkDeviceId } = input;
|
|
62463
62743
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62464
|
-
|
|
62465
|
-
|
|
62466
|
-
const { meta: m } = persisted;
|
|
62467
|
-
const key = String(deviceId);
|
|
62468
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62469
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62470
|
-
...allMeta,
|
|
62471
|
-
[key]: {
|
|
62472
|
-
...m,
|
|
62473
|
-
linkDeviceId
|
|
62474
|
-
}
|
|
62475
|
-
} });
|
|
62744
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setLinkDeviceId: unknown device id=${deviceId}`);
|
|
62745
|
+
await pctx.metaStore.rows.patch(deviceId, { linkDeviceId });
|
|
62476
62746
|
});
|
|
62477
62747
|
pctx.host.ctx.eventBus.emit({
|
|
62478
62748
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62492,18 +62762,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62492
62762
|
async function setPrimaryChildEntityId(pctx, input) {
|
|
62493
62763
|
const { deviceId, primaryChildEntityId } = input;
|
|
62494
62764
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62495
|
-
|
|
62496
|
-
|
|
62497
|
-
const { meta: m } = persisted;
|
|
62498
|
-
const key = String(deviceId);
|
|
62499
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62500
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62501
|
-
...allMeta,
|
|
62502
|
-
[key]: {
|
|
62503
|
-
...m,
|
|
62504
|
-
primaryChildEntityId
|
|
62505
|
-
}
|
|
62506
|
-
} });
|
|
62765
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setPrimaryChildEntityId: unknown device id=${deviceId}`);
|
|
62766
|
+
await pctx.metaStore.rows.patch(deviceId, { primaryChildEntityId });
|
|
62507
62767
|
});
|
|
62508
62768
|
pctx.host.ctx.eventBus.emit({
|
|
62509
62769
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62523,18 +62783,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62523
62783
|
async function setChildLayout(pctx, input) {
|
|
62524
62784
|
const { deviceId, childLayout } = input;
|
|
62525
62785
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62526
|
-
|
|
62527
|
-
|
|
62528
|
-
const { meta: m } = persisted;
|
|
62529
|
-
const key = String(deviceId);
|
|
62530
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62531
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62532
|
-
...allMeta,
|
|
62533
|
-
[key]: {
|
|
62534
|
-
...m,
|
|
62535
|
-
childLayout
|
|
62536
|
-
}
|
|
62537
|
-
} });
|
|
62786
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setChildLayout: unknown device id=${deviceId}`);
|
|
62787
|
+
await pctx.metaStore.rows.patch(deviceId, { childLayout });
|
|
62538
62788
|
});
|
|
62539
62789
|
pctx.host.ctx.eventBus.emit({
|
|
62540
62790
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62554,18 +62804,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62554
62804
|
async function setRole(pctx, input) {
|
|
62555
62805
|
const { deviceId, role } = input;
|
|
62556
62806
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62557
|
-
|
|
62558
|
-
|
|
62559
|
-
const { meta: m } = persisted;
|
|
62560
|
-
const key = String(deviceId);
|
|
62561
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62562
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62563
|
-
...allMeta,
|
|
62564
|
-
[key]: {
|
|
62565
|
-
...m,
|
|
62566
|
-
role
|
|
62567
|
-
}
|
|
62568
|
-
} });
|
|
62807
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setRole: unknown device id=${deviceId}`);
|
|
62808
|
+
await pctx.metaStore.rows.patch(deviceId, { role });
|
|
62569
62809
|
});
|
|
62570
62810
|
pctx.host.ctx.eventBus.emit({
|
|
62571
62811
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62596,19 +62836,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62596
62836
|
const { deviceId, display } = input;
|
|
62597
62837
|
const normalized = display === null ? null : normalizeDisplayOverride(display);
|
|
62598
62838
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62599
|
-
|
|
62600
|
-
|
|
62601
|
-
const { meta: m } = persisted;
|
|
62602
|
-
const key = String(deviceId);
|
|
62603
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62604
|
-
const nextRow = normalized === null ? (({ display: _drop, ...rest }) => rest)(m) : {
|
|
62605
|
-
...m,
|
|
62606
|
-
display: normalized
|
|
62607
|
-
};
|
|
62608
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62609
|
-
...allMeta,
|
|
62610
|
-
[key]: nextRow
|
|
62611
|
-
} });
|
|
62839
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setDisplay: unknown device id=${deviceId}`);
|
|
62840
|
+
await pctx.metaStore.rows.patch(deviceId, { display: normalized });
|
|
62612
62841
|
});
|
|
62613
62842
|
pctx.host.ctx.eventBus.emit({
|
|
62614
62843
|
id: (0, node_crypto.randomUUID)(),
|
|
@@ -62638,24 +62867,15 @@ var require_device_manager_addon = __commonJS({
|
|
|
62638
62867
|
async function applyInitialMeta(pctx, input) {
|
|
62639
62868
|
const { deviceId, name, location, type, integrationId, linkDeviceId, role } = input;
|
|
62640
62869
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62641
|
-
|
|
62642
|
-
|
|
62643
|
-
const { meta: m } = persisted;
|
|
62644
|
-
const key = String(deviceId);
|
|
62645
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62646
|
-
const merged = {
|
|
62647
|
-
...m,
|
|
62870
|
+
if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] applyInitialMeta: unknown device id=${deviceId}`);
|
|
62871
|
+
await pctx.metaStore.rows.patch(deviceId, {
|
|
62648
62872
|
...name !== void 0 ? { name } : {},
|
|
62649
62873
|
...location !== void 0 ? { location } : {},
|
|
62650
62874
|
...type !== void 0 ? { type } : {},
|
|
62651
62875
|
...integrationId !== void 0 ? { integrationId } : {},
|
|
62652
62876
|
...linkDeviceId !== void 0 ? { linkDeviceId } : {},
|
|
62653
62877
|
...role !== void 0 ? { role } : {}
|
|
62654
|
-
};
|
|
62655
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62656
|
-
...allMeta,
|
|
62657
|
-
[key]: merged
|
|
62658
|
-
} });
|
|
62878
|
+
});
|
|
62659
62879
|
});
|
|
62660
62880
|
const emitMetaChanged = (field, value) => {
|
|
62661
62881
|
pctx.host.ctx.eventBus.emit({
|
|
@@ -62683,10 +62903,9 @@ var require_device_manager_addon = __commonJS({
|
|
|
62683
62903
|
async function setMetadata(pctx, input) {
|
|
62684
62904
|
const { deviceId, patch } = input;
|
|
62685
62905
|
const result = await pctx.metaStore.withMetaWriteLock(async () => {
|
|
62686
|
-
|
|
62687
|
-
|
|
62688
|
-
const
|
|
62689
|
-
const next = { ...map[key] ?? {} };
|
|
62906
|
+
const row = await pctx.metaStore.rows.get(deviceId);
|
|
62907
|
+
if (row === null) throw new Error(`[device-manager] setMetadata: unknown device id=${deviceId}`);
|
|
62908
|
+
const next = { ...row.metadata ?? {} };
|
|
62690
62909
|
let changed = false;
|
|
62691
62910
|
for (const [k, v] of Object.entries(patch)) if (v === null) {
|
|
62692
62911
|
if (k in next) {
|
|
@@ -62699,10 +62918,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62699
62918
|
}
|
|
62700
62919
|
if (!changed) return { changed: false };
|
|
62701
62920
|
const hasFields = Object.keys(next).length > 0;
|
|
62702
|
-
|
|
62703
|
-
if (hasFields) updatedMap[key] = next;
|
|
62704
|
-
else delete updatedMap[key];
|
|
62705
|
-
await pctx.settings.writeAddonStore({ deviceMetadata: updatedMap });
|
|
62921
|
+
await pctx.metaStore.rows.patch(deviceId, { metadata: hasFields ? next : null });
|
|
62706
62922
|
return {
|
|
62707
62923
|
changed: true,
|
|
62708
62924
|
finalMeta: hasFields ? next : null
|
|
@@ -62730,15 +62946,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62730
62946
|
const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
|
|
62731
62947
|
if (!persisted) throw new Error(`[device-manager] setDisabled: unknown device id=${deviceId}`);
|
|
62732
62948
|
const { meta: m } = persisted;
|
|
62733
|
-
|
|
62734
|
-
const allMeta = await pctx.metaStore.readMeta();
|
|
62735
|
-
await pctx.settings.writeAddonStore({ deviceMeta: {
|
|
62736
|
-
...allMeta,
|
|
62737
|
-
[key]: {
|
|
62738
|
-
...m,
|
|
62739
|
-
disabled
|
|
62740
|
-
}
|
|
62741
|
-
} });
|
|
62949
|
+
await pctx.metaStore.rows.patch(deviceId, { disabled });
|
|
62742
62950
|
return {
|
|
62743
62951
|
changed: (m.disabled ?? false) !== disabled,
|
|
62744
62952
|
integrationId: m.integrationId ?? ""
|
|
@@ -62781,9 +62989,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62781
62989
|
return data;
|
|
62782
62990
|
}
|
|
62783
62991
|
async function listLocations(pctx) {
|
|
62784
|
-
const
|
|
62785
|
-
const meta = store.deviceMeta ?? {};
|
|
62786
|
-
const locations = store.locations ?? [];
|
|
62992
|
+
const locations = (await pctx.metaStore.readStore()).locations ?? [];
|
|
62787
62993
|
const seen = /* @__PURE__ */ new Map();
|
|
62788
62994
|
const consider = (raw) => {
|
|
62789
62995
|
if (typeof raw !== "string") return;
|
|
@@ -62793,38 +62999,34 @@ var require_device_manager_addon = __commonJS({
|
|
|
62793
62999
|
if (!seen.has(key)) seen.set(key, trimmed);
|
|
62794
63000
|
};
|
|
62795
63001
|
for (const label of locations) consider(label);
|
|
62796
|
-
for (const
|
|
63002
|
+
for (const row of await pctx.metaStore.rows.listAll()) consider(row.meta.location);
|
|
62797
63003
|
return [...seen.values()].toSorted((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
|
|
62798
63004
|
}
|
|
62799
63005
|
async function addLocation(pctx, input) {
|
|
62800
63006
|
const trimmed = input.name.trim();
|
|
62801
63007
|
if (trimmed.length === 0) throw new Error("[device-manager] addLocation: name must be non-empty");
|
|
62802
|
-
const current = (await pctx.
|
|
63008
|
+
const current = (await pctx.metaStore.readStore()).locations ?? [];
|
|
62803
63009
|
if (current.some((l) => l.toLowerCase() === trimmed.toLowerCase())) return;
|
|
62804
63010
|
await pctx.settings.writeAddonStore({ locations: [...current, trimmed] });
|
|
62805
63011
|
}
|
|
62806
63012
|
async function removeLocation(pctx, input) {
|
|
62807
63013
|
const trimmed = input.name.trim();
|
|
62808
63014
|
if (trimmed.length === 0) return;
|
|
62809
|
-
const
|
|
62810
|
-
const current = store.locations ?? [];
|
|
63015
|
+
const current = (await pctx.metaStore.readStore()).locations ?? [];
|
|
62811
63016
|
const remaining = current.filter((l) => l.toLowerCase() !== trimmed.toLowerCase());
|
|
62812
63017
|
if (remaining.length !== current.length) await pctx.settings.writeAddonStore({ locations: remaining });
|
|
62813
63018
|
if (input.cascade !== true) return;
|
|
62814
|
-
const
|
|
62815
|
-
|
|
62816
|
-
|
|
62817
|
-
|
|
62818
|
-
|
|
62819
|
-
|
|
62820
|
-
|
|
62821
|
-
|
|
62822
|
-
|
|
62823
|
-
|
|
62824
|
-
|
|
62825
|
-
}
|
|
62826
|
-
if (cleared.length === 0) return;
|
|
62827
|
-
await pctx.settings.writeAddonStore({ deviceMeta: updates });
|
|
63019
|
+
const cleared = await pctx.metaStore.withMetaWriteLock(async () => {
|
|
63020
|
+
const out = [];
|
|
63021
|
+
for (const row of await pctx.metaStore.rows.listAll()) {
|
|
63022
|
+
const location = row.meta.location;
|
|
63023
|
+
if (typeof location !== "string") continue;
|
|
63024
|
+
if (location.trim().toLowerCase() !== trimmed.toLowerCase()) continue;
|
|
63025
|
+
await pctx.metaStore.rows.patch(row.meta.id, { location: null });
|
|
63026
|
+
out.push(row.meta.id);
|
|
63027
|
+
}
|
|
63028
|
+
return out;
|
|
63029
|
+
});
|
|
62828
63030
|
for (const deviceId of cleared) pctx.host.ctx.eventBus.emit({
|
|
62829
63031
|
id: (0, node_crypto.randomUUID)(),
|
|
62830
63032
|
timestamp: /* @__PURE__ */ new Date(),
|
|
@@ -62840,41 +63042,48 @@ var require_device_manager_addon = __commonJS({
|
|
|
62840
63042
|
}
|
|
62841
63043
|
});
|
|
62842
63044
|
}
|
|
63045
|
+
function decodeAddonStore(raw) {
|
|
63046
|
+
const nextDeviceId = raw["nextDeviceId"];
|
|
63047
|
+
const locations = raw["locations"];
|
|
63048
|
+
const roleDisplayDefaults = raw["roleDisplayDefaults"];
|
|
63049
|
+
return {
|
|
63050
|
+
...typeof nextDeviceId === "number" && Number.isFinite(nextDeviceId) ? { nextDeviceId } : {},
|
|
63051
|
+
...Array.isArray(locations) ? { locations: locations.filter((l) => typeof l === "string") } : {},
|
|
63052
|
+
...isRoleDisplayDefaults(roleDisplayDefaults) ? { roleDisplayDefaults } : {}
|
|
63053
|
+
};
|
|
63054
|
+
}
|
|
63055
|
+
function isRoleDisplayDefaults(value) {
|
|
63056
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
63057
|
+
}
|
|
62843
63058
|
var DeviceMetaStore = class {
|
|
62844
63059
|
settings;
|
|
62845
63060
|
registry;
|
|
63061
|
+
rows;
|
|
62846
63062
|
/** Synchronous ownership cache, keyed by NUMERIC deviceId → owning addonId.
|
|
62847
|
-
* The persisted
|
|
63063
|
+
* The persisted row store is authoritative but reads are async; hub-side
|
|
62848
63064
|
* callers (e.g. `CapabilityRegistry.getNativeProvider` fallback) need
|
|
62849
63065
|
* ownership without awaiting. Kept in sync with every register/remove and
|
|
62850
63066
|
* warmed from persistence on boot. */
|
|
62851
63067
|
idToAddonId = /* @__PURE__ */ new Map();
|
|
62852
|
-
/** Serialises every read-modify-write of
|
|
62853
|
-
*
|
|
62854
|
-
* identical to the former `onInitialize` closure variable. */
|
|
63068
|
+
/** Serialises every read-modify-write of a device row through one promise
|
|
63069
|
+
* chain (see `withMetaWriteLock`). Per-instance state. */
|
|
62855
63070
|
metaWriteChain = Promise.resolve();
|
|
62856
|
-
constructor(settings, registry) {
|
|
63071
|
+
constructor(settings, registry, rows) {
|
|
62857
63072
|
this.settings = settings;
|
|
62858
63073
|
this.registry = registry;
|
|
63074
|
+
this.rows = rows;
|
|
62859
63075
|
}
|
|
62860
63076
|
/** The read currently in flight, or null. Never a settled value — see
|
|
62861
63077
|
* {@link readStore}. */
|
|
62862
63078
|
inFlightRead = null;
|
|
62863
63079
|
/**
|
|
62864
|
-
* The
|
|
63080
|
+
* The addon's own settings row set — `nextDeviceId`, `roleDisplayDefaults`,
|
|
63081
|
+
* `locations`. NOT the fleet: no device has lived here since the flatten.
|
|
62865
63082
|
*
|
|
62866
63083
|
* **Concurrent callers join the read already in flight.** This is not a
|
|
62867
63084
|
* cache and nothing survives settlement: a caller that awaited the running
|
|
62868
63085
|
* promise could not have observed anything older than its result, so the
|
|
62869
|
-
* only thing that changes is cost.
|
|
62870
|
-
* 2026-08-19 boot: `readAddonStore` lands in `SqliteSettingsBackend
|
|
62871
|
-
* .getAllAddon`, which reads and `JSON.parse`s this addon's rows —
|
|
62872
|
-
* 625 KB on the live hub (`deviceMeta` 467 KB + `deviceMetadata` 87 KB +
|
|
62873
|
-
* `deviceIndex` 70 KB) — synchronously, on the hub's event loop. A V8
|
|
62874
|
-
* profile of that boot had hub-main's JS thread 99.9% busy with 64% of it
|
|
62875
|
-
* inside `getAllAddon`, ~89% of that entered here, and every runner's first
|
|
62876
|
-
* store read queued behind it (the notification centre's six parallel reads
|
|
62877
|
-
* all resolved together at t+44.7 s).
|
|
63086
|
+
* only thing that changes is cost.
|
|
62878
63087
|
*
|
|
62879
63088
|
* A rejection is NOT latched: the slot is cleared before the promise
|
|
62880
63089
|
* settles either way, so a failed read costs the joiners that one failure
|
|
@@ -62885,7 +63094,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
62885
63094
|
if (existing !== null) return existing;
|
|
62886
63095
|
const read = (async () => {
|
|
62887
63096
|
try {
|
|
62888
|
-
return await this.settings.readAddonStore();
|
|
63097
|
+
return decodeAddonStore(await this.settings.readAddonStore());
|
|
62889
63098
|
} finally {
|
|
62890
63099
|
this.inFlightRead = null;
|
|
62891
63100
|
}
|
|
@@ -62893,41 +63102,6 @@ var require_device_manager_addon = __commonJS({
|
|
|
62893
63102
|
this.inFlightRead = read;
|
|
62894
63103
|
return read;
|
|
62895
63104
|
};
|
|
62896
|
-
/**
|
|
62897
|
-
* The three fleet projections from ONE read.
|
|
62898
|
-
*
|
|
62899
|
-
* `listAll` asked for `deviceMeta`, then `deviceMetadata`, then
|
|
62900
|
-
* `deviceIndex` — three SEQUENTIAL awaits, which {@link readStore}'s
|
|
62901
|
-
* in-flight join cannot collapse because each starts after the previous one
|
|
62902
|
-
* settled. Three full 625 KB parses per call, on a call made once per device
|
|
62903
|
-
* lifecycle event during a 974-device boot: 23% of hub-main's CPU.
|
|
62904
|
-
*
|
|
62905
|
-
* It is also ONE snapshot. Three separate reads could straddle a write and
|
|
62906
|
-
* hand back an index that names a device the meta map no longer has.
|
|
62907
|
-
*/
|
|
62908
|
-
readAll = async () => {
|
|
62909
|
-
const store = await this.readStore();
|
|
62910
|
-
return {
|
|
62911
|
-
index: store.deviceIndex ?? {},
|
|
62912
|
-
meta: store.deviceMeta ?? {},
|
|
62913
|
-
metadata: store.deviceMetadata ?? {}
|
|
62914
|
-
};
|
|
62915
|
-
};
|
|
62916
|
-
readIndex = async () => {
|
|
62917
|
-
return (await this.readStore()).deviceIndex ?? {};
|
|
62918
|
-
};
|
|
62919
|
-
readMeta = async () => {
|
|
62920
|
-
return (await this.readStore()).deviceMeta ?? {};
|
|
62921
|
-
};
|
|
62922
|
-
/** Hardware-identity metadata map. Lives in a sibling key on the
|
|
62923
|
-
* device-manager addon store so its writers (`setMetadata`) never
|
|
62924
|
-
* collide with the lifecycle writers on `deviceMeta`
|
|
62925
|
-
* (`registerDevice` / `setName` / `setLocation` / `setDisabled`).
|
|
62926
|
-
* Single-writer per row eliminates the "writer X clobbers writer
|
|
62927
|
-
* Y's field" bug class — `setMetadata` is the only producer. */
|
|
62928
|
-
readMetadataMap = async () => {
|
|
62929
|
-
return (await this.readStore()).deviceMetadata ?? {};
|
|
62930
|
-
};
|
|
62931
63105
|
withMetaWriteLock = async (fn) => {
|
|
62932
63106
|
const previous = this.metaWriteChain;
|
|
62933
63107
|
let release = () => {
|
|
@@ -62944,31 +63118,36 @@ var require_device_manager_addon = __commonJS({
|
|
|
62944
63118
|
release();
|
|
62945
63119
|
}
|
|
62946
63120
|
};
|
|
63121
|
+
/** The whole persisted row for one device, or `null`. */
|
|
63122
|
+
getRow = async (deviceId) => this.rows.get(deviceId);
|
|
62947
63123
|
/**
|
|
62948
63124
|
* Resolve a numeric deviceId to the owning `(addonId, stableId)` pair.
|
|
62949
|
-
*
|
|
63125
|
+
* Reads the device's own row — live IDevice lookup (hub registry) is handled
|
|
62950
63126
|
* separately per call site so callers can decide whether to route to
|
|
62951
63127
|
* an in-process driver or to the cross-process `device-ops` bridge.
|
|
62952
63128
|
* Returns null when no device with that id is known to the hub.
|
|
62953
63129
|
*/
|
|
62954
63130
|
resolvePersistedById = async (deviceId) => {
|
|
62955
|
-
const
|
|
62956
|
-
if (
|
|
63131
|
+
const row = await this.rows.get(deviceId);
|
|
63132
|
+
if (row === null) return null;
|
|
62957
63133
|
return {
|
|
62958
|
-
addonId:
|
|
62959
|
-
stableId:
|
|
62960
|
-
meta:
|
|
63134
|
+
addonId: row.meta.addonId,
|
|
63135
|
+
stableId: row.meta.stableId,
|
|
63136
|
+
meta: row.meta
|
|
62961
63137
|
};
|
|
62962
63138
|
};
|
|
63139
|
+
/** The device's hardware-identity metadata blob, or `null`. */
|
|
63140
|
+
readMetadata = async (deviceId) => {
|
|
63141
|
+
return (await this.rows.get(deviceId))?.metadata ?? null;
|
|
63142
|
+
};
|
|
62963
63143
|
/** Direct children of a device: the union of the live registry's children
|
|
62964
|
-
* and the persisted
|
|
63144
|
+
* and the persisted rows whose `parentDeviceId` is `parentId`, deduplicated
|
|
62965
63145
|
* and excluding self. Shared by the `remove` cascade and the `resetToSource`
|
|
62966
63146
|
* resync purge (#19). */
|
|
62967
63147
|
directChildIds = async (parentId) => {
|
|
62968
63148
|
const ids = /* @__PURE__ */ new Set();
|
|
62969
63149
|
if (this.registry) for (const c of this.registry.getChildren(parentId)) ids.add(c.id);
|
|
62970
|
-
const
|
|
62971
|
-
for (const m of Object.values(meta)) if (m.parentDeviceId === parentId) ids.add(m.id);
|
|
63150
|
+
for (const row of await this.rows.listByParent(parentId)) ids.add(row.meta.id);
|
|
62972
63151
|
ids.delete(parentId);
|
|
62973
63152
|
return [...ids];
|
|
62974
63153
|
};
|
|
@@ -62978,6 +63157,476 @@ var require_device_manager_addon = __commonJS({
|
|
|
62978
63157
|
return current;
|
|
62979
63158
|
};
|
|
62980
63159
|
};
|
|
63160
|
+
var DEVICE_ROWS_COLLECTION = "device-manager:devices";
|
|
63161
|
+
var DEVICE_ROWS_FLEET_LIMIT = 2e4;
|
|
63162
|
+
var DEVICE_ROWS_COLUMNS = [
|
|
63163
|
+
{
|
|
63164
|
+
name: "id",
|
|
63165
|
+
type: "TEXT",
|
|
63166
|
+
primaryKey: true,
|
|
63167
|
+
notNull: true
|
|
63168
|
+
},
|
|
63169
|
+
{
|
|
63170
|
+
name: "deviceId",
|
|
63171
|
+
type: "INTEGER",
|
|
63172
|
+
notNull: true
|
|
63173
|
+
},
|
|
63174
|
+
{
|
|
63175
|
+
name: "addonId",
|
|
63176
|
+
type: "TEXT",
|
|
63177
|
+
notNull: true
|
|
63178
|
+
},
|
|
63179
|
+
{
|
|
63180
|
+
name: "stableId",
|
|
63181
|
+
type: "TEXT",
|
|
63182
|
+
notNull: true
|
|
63183
|
+
},
|
|
63184
|
+
{
|
|
63185
|
+
name: "type",
|
|
63186
|
+
type: "TEXT",
|
|
63187
|
+
notNull: true
|
|
63188
|
+
},
|
|
63189
|
+
{
|
|
63190
|
+
name: "name",
|
|
63191
|
+
type: "TEXT",
|
|
63192
|
+
notNull: true
|
|
63193
|
+
},
|
|
63194
|
+
{
|
|
63195
|
+
name: "userNamed",
|
|
63196
|
+
type: "BOOLEAN"
|
|
63197
|
+
},
|
|
63198
|
+
{
|
|
63199
|
+
name: "location",
|
|
63200
|
+
type: "TEXT"
|
|
63201
|
+
},
|
|
63202
|
+
{
|
|
63203
|
+
name: "disabled",
|
|
63204
|
+
type: "BOOLEAN",
|
|
63205
|
+
notNull: true,
|
|
63206
|
+
defaultValue: false
|
|
63207
|
+
},
|
|
63208
|
+
{
|
|
63209
|
+
name: "parentDeviceId",
|
|
63210
|
+
type: "INTEGER"
|
|
63211
|
+
},
|
|
63212
|
+
{
|
|
63213
|
+
name: "registered",
|
|
63214
|
+
type: "BOOLEAN",
|
|
63215
|
+
notNull: true,
|
|
63216
|
+
defaultValue: false
|
|
63217
|
+
},
|
|
63218
|
+
{
|
|
63219
|
+
name: "features",
|
|
63220
|
+
type: "JSON"
|
|
63221
|
+
},
|
|
63222
|
+
{
|
|
63223
|
+
name: "exportFingerprint",
|
|
63224
|
+
type: "TEXT"
|
|
63225
|
+
},
|
|
63226
|
+
{
|
|
63227
|
+
name: "integrationId",
|
|
63228
|
+
type: "TEXT"
|
|
63229
|
+
},
|
|
63230
|
+
{
|
|
63231
|
+
name: "linkDeviceId",
|
|
63232
|
+
type: "INTEGER"
|
|
63233
|
+
},
|
|
63234
|
+
{
|
|
63235
|
+
name: "primaryChildEntityId",
|
|
63236
|
+
type: "TEXT"
|
|
63237
|
+
},
|
|
63238
|
+
{
|
|
63239
|
+
name: "childLayout",
|
|
63240
|
+
type: "JSON"
|
|
63241
|
+
},
|
|
63242
|
+
{
|
|
63243
|
+
name: "role",
|
|
63244
|
+
type: "TEXT"
|
|
63245
|
+
},
|
|
63246
|
+
{
|
|
63247
|
+
name: "display",
|
|
63248
|
+
type: "JSON"
|
|
63249
|
+
},
|
|
63250
|
+
{
|
|
63251
|
+
name: "metadata",
|
|
63252
|
+
type: "JSON"
|
|
63253
|
+
}
|
|
63254
|
+
];
|
|
63255
|
+
var DEVICE_ROWS_INDEXES = [
|
|
63256
|
+
{
|
|
63257
|
+
name: "idx_dm_devices_addon_stable",
|
|
63258
|
+
columns: ["addonId", "stableId"]
|
|
63259
|
+
},
|
|
63260
|
+
{
|
|
63261
|
+
name: "idx_dm_devices_parent",
|
|
63262
|
+
columns: ["parentDeviceId"]
|
|
63263
|
+
},
|
|
63264
|
+
{
|
|
63265
|
+
name: "idx_dm_devices_integration",
|
|
63266
|
+
columns: ["integrationId"]
|
|
63267
|
+
}
|
|
63268
|
+
];
|
|
63269
|
+
function deviceRowBackendOf(client) {
|
|
63270
|
+
return {
|
|
63271
|
+
declareCollection: async (input) => {
|
|
63272
|
+
await client.declareCollection.mutate(input);
|
|
63273
|
+
},
|
|
63274
|
+
get: (input) => client.get.query(input),
|
|
63275
|
+
set: async (input) => {
|
|
63276
|
+
await client.set.mutate(input);
|
|
63277
|
+
},
|
|
63278
|
+
query: (input) => client.query.query(input),
|
|
63279
|
+
updateWhere: (input) => client.updateWhere.mutate(input),
|
|
63280
|
+
delete: async (input) => {
|
|
63281
|
+
await client.delete.mutate(input);
|
|
63282
|
+
},
|
|
63283
|
+
count: (input) => client.count.query(input)
|
|
63284
|
+
};
|
|
63285
|
+
}
|
|
63286
|
+
function isPlainObject3(value) {
|
|
63287
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
63288
|
+
}
|
|
63289
|
+
function readString(value) {
|
|
63290
|
+
return typeof value === "string" ? value : void 0;
|
|
63291
|
+
}
|
|
63292
|
+
function readNumber(value) {
|
|
63293
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
63294
|
+
}
|
|
63295
|
+
function readBoolean(value) {
|
|
63296
|
+
if (typeof value === "boolean") return value;
|
|
63297
|
+
if (value === 1) return true;
|
|
63298
|
+
if (value === 0) return false;
|
|
63299
|
+
}
|
|
63300
|
+
function readStringArray(value) {
|
|
63301
|
+
if (!Array.isArray(value)) return void 0;
|
|
63302
|
+
const out = [];
|
|
63303
|
+
for (const entry of value) if (typeof entry === "string") out.push(entry);
|
|
63304
|
+
return out;
|
|
63305
|
+
}
|
|
63306
|
+
function decodeDeviceRow(data) {
|
|
63307
|
+
const deviceId = readNumber(data["deviceId"]);
|
|
63308
|
+
const addonId = readString(data["addonId"]);
|
|
63309
|
+
const stableId = readString(data["stableId"]);
|
|
63310
|
+
const type = readString(data["type"]);
|
|
63311
|
+
const name = readString(data["name"]);
|
|
63312
|
+
if (deviceId === void 0 || addonId === void 0 || stableId === void 0 || type === void 0 || name === void 0) return null;
|
|
63313
|
+
const location = readString(data["location"]);
|
|
63314
|
+
const parentDeviceId = readNumber(data["parentDeviceId"]);
|
|
63315
|
+
const userNamed = readBoolean(data["userNamed"]);
|
|
63316
|
+
const features = readStringArray(data["features"]);
|
|
63317
|
+
const exportFingerprint = readString(data["exportFingerprint"]);
|
|
63318
|
+
const integrationId = readString(data["integrationId"]);
|
|
63319
|
+
const linkDeviceId = readNumber(data["linkDeviceId"]);
|
|
63320
|
+
const primaryChildEntityId = readString(data["primaryChildEntityId"]);
|
|
63321
|
+
const role = readString(data["role"]);
|
|
63322
|
+
const rawChildLayout = data["childLayout"];
|
|
63323
|
+
const rawDisplay = data["display"];
|
|
63324
|
+
const rawMetadata = data["metadata"];
|
|
63325
|
+
return {
|
|
63326
|
+
meta: {
|
|
63327
|
+
id: deviceId,
|
|
63328
|
+
addonId,
|
|
63329
|
+
stableId,
|
|
63330
|
+
type,
|
|
63331
|
+
name,
|
|
63332
|
+
location: location ?? null,
|
|
63333
|
+
disabled: readBoolean(data["disabled"]) ?? false,
|
|
63334
|
+
parentDeviceId: parentDeviceId ?? null,
|
|
63335
|
+
...userNamed !== void 0 ? { userNamed } : {},
|
|
63336
|
+
...features !== void 0 ? { features } : {},
|
|
63337
|
+
...exportFingerprint !== void 0 ? { exportFingerprint } : {},
|
|
63338
|
+
...integrationId !== void 0 ? { integrationId } : {},
|
|
63339
|
+
...linkDeviceId !== void 0 ? { linkDeviceId } : {},
|
|
63340
|
+
...primaryChildEntityId !== void 0 ? { primaryChildEntityId } : {},
|
|
63341
|
+
...isChildLayout(rawChildLayout) ? { childLayout: rawChildLayout } : {},
|
|
63342
|
+
...role !== void 0 ? { role } : {},
|
|
63343
|
+
...isPlainObject3(rawDisplay) ? { display: toDisplayOverride(rawDisplay) } : {}
|
|
63344
|
+
},
|
|
63345
|
+
metadata: isPlainObject3(rawMetadata) ? rawMetadata : null,
|
|
63346
|
+
registered: readBoolean(data["registered"]) ?? false
|
|
63347
|
+
};
|
|
63348
|
+
}
|
|
63349
|
+
function isChildLayout(value) {
|
|
63350
|
+
return Array.isArray(value);
|
|
63351
|
+
}
|
|
63352
|
+
function toDisplayOverride(value) {
|
|
63353
|
+
return value;
|
|
63354
|
+
}
|
|
63355
|
+
function encodeDeviceRow(meta, extra = {}) {
|
|
63356
|
+
const row = {
|
|
63357
|
+
id: String(meta.id),
|
|
63358
|
+
deviceId: meta.id,
|
|
63359
|
+
addonId: meta.addonId,
|
|
63360
|
+
stableId: meta.stableId,
|
|
63361
|
+
type: meta.type,
|
|
63362
|
+
name: meta.name,
|
|
63363
|
+
userNamed: meta.userNamed ?? null,
|
|
63364
|
+
location: meta.location,
|
|
63365
|
+
disabled: meta.disabled,
|
|
63366
|
+
parentDeviceId: meta.parentDeviceId,
|
|
63367
|
+
registered: extra.registered ?? false,
|
|
63368
|
+
features: meta.features === void 0 ? null : [...meta.features],
|
|
63369
|
+
exportFingerprint: meta.exportFingerprint ?? null,
|
|
63370
|
+
integrationId: meta.integrationId ?? null,
|
|
63371
|
+
linkDeviceId: meta.linkDeviceId ?? null,
|
|
63372
|
+
primaryChildEntityId: meta.primaryChildEntityId ?? null,
|
|
63373
|
+
childLayout: meta.childLayout ?? null,
|
|
63374
|
+
role: meta.role ?? null,
|
|
63375
|
+
display: meta.display ?? null
|
|
63376
|
+
};
|
|
63377
|
+
if (extra.metadata !== void 0) row["metadata"] = extra.metadata;
|
|
63378
|
+
return row;
|
|
63379
|
+
}
|
|
63380
|
+
function encodeDeviceRowPatch(patch) {
|
|
63381
|
+
const row = {};
|
|
63382
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
63383
|
+
if (value === void 0) continue;
|
|
63384
|
+
row[key] = value;
|
|
63385
|
+
}
|
|
63386
|
+
return row;
|
|
63387
|
+
}
|
|
63388
|
+
var DeviceRowStore = class {
|
|
63389
|
+
backend;
|
|
63390
|
+
logger;
|
|
63391
|
+
declared = null;
|
|
63392
|
+
constructor(backend, logger) {
|
|
63393
|
+
this.backend = backend;
|
|
63394
|
+
this.logger = logger;
|
|
63395
|
+
}
|
|
63396
|
+
/**
|
|
63397
|
+
* Lazy, idempotent `declareCollection`, memoised on the PROMISE so N
|
|
63398
|
+
* concurrent first-callers issue one declaration rather than N. A rejection
|
|
63399
|
+
* is not latched — the slot is cleared so the next caller retries instead of
|
|
63400
|
+
* inheriting a dead collection forever.
|
|
63401
|
+
*/
|
|
63402
|
+
async declare() {
|
|
63403
|
+
const existing = this.declared;
|
|
63404
|
+
if (existing !== null) return existing;
|
|
63405
|
+
const run = (async () => {
|
|
63406
|
+
try {
|
|
63407
|
+
await this.backend.declareCollection({
|
|
63408
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63409
|
+
columns: DEVICE_ROWS_COLUMNS,
|
|
63410
|
+
indexes: DEVICE_ROWS_INDEXES
|
|
63411
|
+
});
|
|
63412
|
+
} catch (err) {
|
|
63413
|
+
this.declared = null;
|
|
63414
|
+
throw err;
|
|
63415
|
+
}
|
|
63416
|
+
})();
|
|
63417
|
+
this.declared = run;
|
|
63418
|
+
return run;
|
|
63419
|
+
}
|
|
63420
|
+
/** One device, by numeric id. `null` when the fleet does not know it. */
|
|
63421
|
+
async get(deviceId) {
|
|
63422
|
+
await this.declare();
|
|
63423
|
+
const raw = await this.backend.get({
|
|
63424
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63425
|
+
key: String(deviceId)
|
|
63426
|
+
});
|
|
63427
|
+
if (!isPlainObject3(raw)) return null;
|
|
63428
|
+
const decoded = decodeDeviceRow(raw);
|
|
63429
|
+
if (decoded === null) {
|
|
63430
|
+
this.logger.warn("device row skipped \u2014 identity fields missing", { tags: { deviceId } });
|
|
63431
|
+
return null;
|
|
63432
|
+
}
|
|
63433
|
+
return decoded;
|
|
63434
|
+
}
|
|
63435
|
+
/** Every device, ordered by numeric id. */
|
|
63436
|
+
async listAll() {
|
|
63437
|
+
return this.list({
|
|
63438
|
+
orderBy: {
|
|
63439
|
+
field: "deviceId",
|
|
63440
|
+
direction: "asc"
|
|
63441
|
+
},
|
|
63442
|
+
limit: DEVICE_ROWS_FLEET_LIMIT
|
|
63443
|
+
});
|
|
63444
|
+
}
|
|
63445
|
+
/**
|
|
63446
|
+
* The device an addon knows as `stableId`, or `null`.
|
|
63447
|
+
*
|
|
63448
|
+
* `(addonId, stableId)` is the addon-facing identity — unique by
|
|
63449
|
+
* construction, since `allocateDeviceId` is the only thing that mints a row
|
|
63450
|
+
* and it returns the existing id for a pair it already knows. A second row
|
|
63451
|
+
* for the pair would be a corruption, so this takes the LOWEST id and says
|
|
63452
|
+
* so rather than picking arbitrarily.
|
|
63453
|
+
*/
|
|
63454
|
+
async findByStableId(addonId, stableId) {
|
|
63455
|
+
const rows = await this.list({
|
|
63456
|
+
where: {
|
|
63457
|
+
addonId,
|
|
63458
|
+
stableId
|
|
63459
|
+
},
|
|
63460
|
+
orderBy: {
|
|
63461
|
+
field: "deviceId",
|
|
63462
|
+
direction: "asc"
|
|
63463
|
+
},
|
|
63464
|
+
limit: 2
|
|
63465
|
+
});
|
|
63466
|
+
const first = rows[0];
|
|
63467
|
+
if (first === void 0) return null;
|
|
63468
|
+
if (rows.length > 1) this.logger.warn("duplicate device rows for one (addonId, stableId) \u2014 using the lowest id", {
|
|
63469
|
+
tags: { deviceId: first.meta.id },
|
|
63470
|
+
meta: {
|
|
63471
|
+
addonId,
|
|
63472
|
+
stableId,
|
|
63473
|
+
ids: rows.map((r) => r.meta.id)
|
|
63474
|
+
}
|
|
63475
|
+
});
|
|
63476
|
+
return first;
|
|
63477
|
+
}
|
|
63478
|
+
/** Every device owned by one addon, ordered by numeric id. */
|
|
63479
|
+
async listByAddon(addonId) {
|
|
63480
|
+
return this.list({
|
|
63481
|
+
where: { addonId },
|
|
63482
|
+
orderBy: {
|
|
63483
|
+
field: "deviceId",
|
|
63484
|
+
direction: "asc"
|
|
63485
|
+
},
|
|
63486
|
+
limit: DEVICE_ROWS_FLEET_LIMIT
|
|
63487
|
+
});
|
|
63488
|
+
}
|
|
63489
|
+
/** Every device an integration owns, ordered by numeric id. */
|
|
63490
|
+
async listByIntegration(integrationId) {
|
|
63491
|
+
return this.list({
|
|
63492
|
+
where: { integrationId },
|
|
63493
|
+
orderBy: {
|
|
63494
|
+
field: "deviceId",
|
|
63495
|
+
direction: "asc"
|
|
63496
|
+
},
|
|
63497
|
+
limit: DEVICE_ROWS_FLEET_LIMIT
|
|
63498
|
+
});
|
|
63499
|
+
}
|
|
63500
|
+
/** Direct children of one device, ordered by numeric id. */
|
|
63501
|
+
async listByParent(parentDeviceId) {
|
|
63502
|
+
return this.list({
|
|
63503
|
+
where: { parentDeviceId },
|
|
63504
|
+
orderBy: {
|
|
63505
|
+
field: "deviceId",
|
|
63506
|
+
direction: "asc"
|
|
63507
|
+
},
|
|
63508
|
+
limit: DEVICE_ROWS_FLEET_LIMIT
|
|
63509
|
+
});
|
|
63510
|
+
}
|
|
63511
|
+
/** How many devices the fleet holds. Used to tell "empty store" from "gone device". */
|
|
63512
|
+
async count() {
|
|
63513
|
+
await this.declare();
|
|
63514
|
+
return this.backend.count({ collection: DEVICE_ROWS_COLLECTION });
|
|
63515
|
+
}
|
|
63516
|
+
async list(filter) {
|
|
63517
|
+
await this.declare();
|
|
63518
|
+
const records = await this.backend.query({
|
|
63519
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63520
|
+
filter
|
|
63521
|
+
});
|
|
63522
|
+
const out = [];
|
|
63523
|
+
for (const record of records) {
|
|
63524
|
+
const decoded = decodeDeviceRow(record.data);
|
|
63525
|
+
if (decoded === null) {
|
|
63526
|
+
this.logger.warn("device row skipped \u2014 identity fields missing", { meta: { rowId: record.id } });
|
|
63527
|
+
continue;
|
|
63528
|
+
}
|
|
63529
|
+
out.push(decoded);
|
|
63530
|
+
}
|
|
63531
|
+
return out;
|
|
63532
|
+
}
|
|
63533
|
+
/** Insert or replace the whole identity row. */
|
|
63534
|
+
async upsert(meta, extra = {}) {
|
|
63535
|
+
await this.declare();
|
|
63536
|
+
await this.backend.set({
|
|
63537
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63538
|
+
key: String(meta.id),
|
|
63539
|
+
value: encodeDeviceRow(meta, extra)
|
|
63540
|
+
});
|
|
63541
|
+
}
|
|
63542
|
+
/**
|
|
63543
|
+
* Write the named columns of an EXISTING row and nothing else.
|
|
63544
|
+
*
|
|
63545
|
+
* An UPDATE, deliberately not an upsert: a partial upsert would try to INSERT
|
|
63546
|
+
* a row carrying only the patched columns, and SQLite rejects that on the
|
|
63547
|
+
* `NOT NULL` identity columns before the primary-key conflict can turn it
|
|
63548
|
+
* into an update. Every caller resolves the row under the write lock and
|
|
63549
|
+
* throws when it is gone, so matching zero rows means a device was removed
|
|
63550
|
+
* between the resolve and the write — the patch is lost, and a lost write
|
|
63551
|
+
* that says nothing reads as a write that happened.
|
|
63552
|
+
*/
|
|
63553
|
+
async patch(deviceId, patch) {
|
|
63554
|
+
const columns = encodeDeviceRowPatch(patch);
|
|
63555
|
+
if (Object.keys(columns).length === 0) return;
|
|
63556
|
+
await this.declare();
|
|
63557
|
+
const { updated } = await this.backend.updateWhere({
|
|
63558
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63559
|
+
filter: { where: { id: String(deviceId) } },
|
|
63560
|
+
data: columns
|
|
63561
|
+
});
|
|
63562
|
+
if (updated === 0) this.logger.warn("device row patch matched no row \u2014 the device was removed under it", {
|
|
63563
|
+
tags: { deviceId },
|
|
63564
|
+
meta: { columns: Object.keys(columns) }
|
|
63565
|
+
});
|
|
63566
|
+
}
|
|
63567
|
+
/**
|
|
63568
|
+
* Insert-or-update the registration columns. See {@link DeviceRegistrationRow}
|
|
63569
|
+
* for why this is a separate, fully-specified statement rather than a patch.
|
|
63570
|
+
*/
|
|
63571
|
+
async upsertRegistration(row) {
|
|
63572
|
+
await this.declare();
|
|
63573
|
+
await this.backend.set({
|
|
63574
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63575
|
+
key: String(row.deviceId),
|
|
63576
|
+
value: {
|
|
63577
|
+
deviceId: row.deviceId,
|
|
63578
|
+
addonId: row.addonId,
|
|
63579
|
+
stableId: row.stableId,
|
|
63580
|
+
type: row.type,
|
|
63581
|
+
name: row.name,
|
|
63582
|
+
userNamed: row.userNamed,
|
|
63583
|
+
location: row.location,
|
|
63584
|
+
disabled: row.disabled,
|
|
63585
|
+
parentDeviceId: row.parentDeviceId,
|
|
63586
|
+
registered: row.registered,
|
|
63587
|
+
features: [...row.features],
|
|
63588
|
+
exportFingerprint: row.exportFingerprint
|
|
63589
|
+
}
|
|
63590
|
+
});
|
|
63591
|
+
}
|
|
63592
|
+
/** Drop the device's row. Idempotent. */
|
|
63593
|
+
async remove(deviceId) {
|
|
63594
|
+
await this.declare();
|
|
63595
|
+
await this.backend.delete({
|
|
63596
|
+
collection: DEVICE_ROWS_COLLECTION,
|
|
63597
|
+
key: String(deviceId)
|
|
63598
|
+
});
|
|
63599
|
+
}
|
|
63600
|
+
/**
|
|
63601
|
+
* The retirement door for the three blobs this collection replaced.
|
|
63602
|
+
*
|
|
63603
|
+
* The purge is gated on THIS collection being non-empty, and the count has to
|
|
63604
|
+
* be taken after `declare()` — which is why the owning addon runs it and the
|
|
63605
|
+
* settings engine cannot: at the engine's own boot no addon has declared
|
|
63606
|
+
* anything, so every successor would read as unreadable, forever.
|
|
63607
|
+
*/
|
|
63608
|
+
retiredRowStore() {
|
|
63609
|
+
return {
|
|
63610
|
+
hasRow: async (spec) => {
|
|
63611
|
+
const raw = await this.backend.get({
|
|
63612
|
+
collection: spec.collection,
|
|
63613
|
+
key: spec.row
|
|
63614
|
+
});
|
|
63615
|
+
return raw !== void 0 && raw !== null;
|
|
63616
|
+
},
|
|
63617
|
+
deleteRow: async (spec) => {
|
|
63618
|
+
await this.backend.delete({
|
|
63619
|
+
collection: spec.collection,
|
|
63620
|
+
key: spec.row
|
|
63621
|
+
});
|
|
63622
|
+
},
|
|
63623
|
+
countRows: async (collection) => {
|
|
63624
|
+
await this.declare();
|
|
63625
|
+
return this.backend.count({ collection });
|
|
63626
|
+
}
|
|
63627
|
+
};
|
|
63628
|
+
}
|
|
63629
|
+
};
|
|
62981
63630
|
function persistableBlob(blob, policyFor) {
|
|
62982
63631
|
const out = {};
|
|
62983
63632
|
for (const [capName, slice] of Object.entries(blob)) {
|
|
@@ -63456,12 +64105,21 @@ var require_device_manager_addon = __commonJS({
|
|
|
63456
64105
|
}
|
|
63457
64106
|
}))).filter((id) => id !== null);
|
|
63458
64107
|
}
|
|
64108
|
+
/**
|
|
64109
|
+
* Row access for `device-manager:devices`. Built once in `onInitialize`
|
|
64110
|
+
* (which is also where the collection is declared) — every consumer reaches
|
|
64111
|
+
* it through {@link bindingsDeps} or the `ProviderContext`.
|
|
64112
|
+
*/
|
|
64113
|
+
deviceRows = null;
|
|
63459
64114
|
/** Build the dependency context the extracted binding resolvers consume. */
|
|
63460
64115
|
get bindingsDeps() {
|
|
64116
|
+
const rows = this.deviceRows;
|
|
64117
|
+
if (rows === null) throw new Error("[device-manager] device row store not initialized");
|
|
63461
64118
|
return {
|
|
63462
64119
|
ctx: this.ctx,
|
|
63463
64120
|
capabilityRegistry: this.capabilityRegistry,
|
|
63464
|
-
remoteNativeCaps: this.remoteNativeCaps
|
|
64121
|
+
remoteNativeCaps: this.remoteNativeCaps,
|
|
64122
|
+
rows
|
|
63465
64123
|
};
|
|
63466
64124
|
}
|
|
63467
64125
|
async getBindings(input) {
|
|
@@ -63517,17 +64175,23 @@ var require_device_manager_addon = __commonJS({
|
|
|
63517
64175
|
if (!ops) throw new Error(`[device-manager] device-ops native provider not found for '${deviceId}'`);
|
|
63518
64176
|
return ops;
|
|
63519
64177
|
};
|
|
63520
|
-
const
|
|
64178
|
+
const settingsStoreApi = this.ctx.api?.settingsStore;
|
|
64179
|
+
if (!settingsStoreApi) throw new Error("[device-manager] settings-store API not available \u2014 refusing to serve a fleet it cannot persist");
|
|
64180
|
+
const deviceRows = new DeviceRowStore(deviceRowBackendOf(settingsStoreApi), this.ctx.logger.child("rows"));
|
|
64181
|
+
await deviceRows.declare();
|
|
64182
|
+
this.deviceRows = deviceRows;
|
|
64183
|
+
try {
|
|
64184
|
+
await require_retired_settings_keys.purgeRetiredSettingsRows(deviceRows.retiredRowStore(), this.ctx.logger.child("RetiredRows"));
|
|
64185
|
+
} catch (err) {
|
|
64186
|
+
this.ctx.logger.warn("retired-row purge failed", { meta: { error: require_dist10.errMsg(err) } });
|
|
64187
|
+
}
|
|
64188
|
+
const metaStore = new DeviceMetaStore(settings, registry, deviceRows);
|
|
63521
64189
|
this.stateMirrorImpl = new DeviceStateMirror(this.ctx);
|
|
63522
64190
|
const stateMirror = this.stateMirrorImpl;
|
|
63523
|
-
const readMeta = metaStore.readMeta;
|
|
63524
64191
|
const resolvePersistedById = metaStore.resolvePersistedById;
|
|
63525
64192
|
const idToAddonId = metaStore.idToAddonId;
|
|
63526
|
-
|
|
63527
|
-
|
|
63528
|
-
for (const m of Object.values(meta)) idToAddonId.set(m.id, m.addonId);
|
|
63529
|
-
}
|
|
63530
|
-
const stampIntegrationId$1 = (deviceId, integrationId) => stampIntegrationId(metaStore, settings, this.ctx, deviceId, integrationId);
|
|
64193
|
+
for (const row of await deviceRows.listAll()) idToAddonId.set(row.meta.id, row.meta.addonId);
|
|
64194
|
+
const stampIntegrationId$1 = (deviceId, integrationId) => stampIntegrationId(metaStore, this.ctx, deviceId, integrationId);
|
|
63531
64195
|
const pctx = {
|
|
63532
64196
|
host: this.providerHost,
|
|
63533
64197
|
metaStore,
|
|
@@ -63770,7 +64434,7 @@ var require_hub_forwarder = __commonJS({
|
|
|
63770
64434
|
[Symbol.toStringTag]: { value: "Module" }
|
|
63771
64435
|
});
|
|
63772
64436
|
require_chunk_Cek0wNdY();
|
|
63773
|
-
var require_dist10 =
|
|
64437
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
63774
64438
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
63775
64439
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
63776
64440
|
var HubForwarderDestination = class {
|
|
@@ -63907,7 +64571,7 @@ var require_liveness_monitor_addon = __commonJS({
|
|
|
63907
64571
|
"use strict";
|
|
63908
64572
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
63909
64573
|
require_chunk_Cek0wNdY();
|
|
63910
|
-
var require_dist10 =
|
|
64574
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
63911
64575
|
var NO_DEVICES = "liveness:no-devices";
|
|
63912
64576
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
63913
64577
|
var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
|
|
@@ -64097,7 +64761,7 @@ var require_local_auth_addon = __commonJS({
|
|
|
64097
64761
|
[Symbol.toStringTag]: { value: "Module" }
|
|
64098
64762
|
});
|
|
64099
64763
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
64100
|
-
var require_dist10 =
|
|
64764
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
64101
64765
|
var node_crypto = __require("crypto");
|
|
64102
64766
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
64103
64767
|
var crypto$1 = __require("crypto");
|
|
@@ -71781,7 +72445,7 @@ var require_loki_logging = __commonJS({
|
|
|
71781
72445
|
[Symbol.toStringTag]: { value: "Module" }
|
|
71782
72446
|
});
|
|
71783
72447
|
require_chunk_Cek0wNdY();
|
|
71784
|
-
var require_dist10 =
|
|
72448
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
71785
72449
|
function sanitizeLabelName(raw) {
|
|
71786
72450
|
const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
|
|
71787
72451
|
return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
|
|
@@ -72346,7 +73010,7 @@ var require_native_metrics_addon = __commonJS({
|
|
|
72346
73010
|
[Symbol.toStringTag]: { value: "Module" }
|
|
72347
73011
|
});
|
|
72348
73012
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
72349
|
-
var require_dist10 =
|
|
73013
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
72350
73014
|
var node_child_process = __require("child_process");
|
|
72351
73015
|
var node_util = __require("util");
|
|
72352
73016
|
var node_os = __require("os");
|
|
@@ -73288,7 +73952,7 @@ var require_filesystem_storage_addon = __commonJS({
|
|
|
73288
73952
|
[Symbol.toStringTag]: { value: "Module" }
|
|
73289
73953
|
});
|
|
73290
73954
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
73291
|
-
var require_dist10 =
|
|
73955
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
73292
73956
|
var node_crypto = __require("crypto");
|
|
73293
73957
|
var node_fs_promises = __require("fs/promises");
|
|
73294
73958
|
var node_path = __require("path");
|
|
@@ -74404,7 +75068,8 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74404
75068
|
[Symbol.toStringTag]: { value: "Module" }
|
|
74405
75069
|
});
|
|
74406
75070
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
74407
|
-
var require_dist10 =
|
|
75071
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
75072
|
+
var require_retired_settings_keys = require_retired_settings_keys_B52KsNz8();
|
|
74408
75073
|
var node_crypto = __require("crypto");
|
|
74409
75074
|
var node_fs = __require("fs");
|
|
74410
75075
|
var node_module = __require("module");
|
|
@@ -74413,123 +75078,47 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74413
75078
|
var SQLITE_PAGE_CACHE_KIB = -65536;
|
|
74414
75079
|
var SQLITE_MMAP_SIZE_BYTES = 268435456;
|
|
74415
75080
|
var SQLITE_JOURNAL_SIZE_LIMIT_BYTES = 67108864;
|
|
74416
|
-
|
|
74417
|
-
|
|
74418
|
-
|
|
74419
|
-
|
|
74420
|
-
var RETIRED_SETTINGS_KEYS = [{
|
|
74421
|
-
namespace: "detection-pipeline",
|
|
74422
|
-
collection: "addon-settings",
|
|
74423
|
-
row: "root",
|
|
74424
|
-
keys: ["pipelineSteps", "pipelineEngine"],
|
|
74425
|
-
perNodeKeys: [
|
|
74426
|
-
"engineRuntime",
|
|
74427
|
-
"engineBackend",
|
|
74428
|
-
"engineDevice",
|
|
74429
|
-
"probedBestEngine"
|
|
74430
|
-
],
|
|
74431
|
-
reason: "global step/engine seed and the per-node engine cascade \u2014 both removed; the live per-camera dispatch path never read either"
|
|
75081
|
+
var RETIRED_COLLECTIONS = [{
|
|
75082
|
+
collection: "devices",
|
|
75083
|
+
owner: "integration-registry",
|
|
75084
|
+
reason: "the device half of the integration-registry is deleted; the fleet is `device-manager:devices` and always was (0 rows here against 974 live devices)"
|
|
74432
75085
|
}, {
|
|
74433
|
-
|
|
74434
|
-
|
|
74435
|
-
|
|
74436
|
-
keys: [],
|
|
74437
|
-
perNodeKeys: [],
|
|
74438
|
-
mapValueKeys: ["deviceLinks"],
|
|
74439
|
-
reason: "cross-device field wiring, deleted 2026-08-08 \u2014 0 links across 302 devices a month after the UI shipped. There is no reader left: the resolver, the overlay, the cycle guard, the fifth binding kind and the tab are gone"
|
|
75086
|
+
collection: "device_settings_kv",
|
|
75087
|
+
owner: "integration-registry",
|
|
75088
|
+
reason: "per-device settings of a device table that never had a row; the live per-camera store is the device-manager row plus `addon-device-settings`"
|
|
74440
75089
|
}];
|
|
74441
|
-
function
|
|
74442
|
-
return {
|
|
74443
|
-
|
|
74444
|
-
|
|
74445
|
-
namespace: spec.namespace,
|
|
74446
|
-
collection: spec.collection,
|
|
74447
|
-
key: spec.row
|
|
74448
|
-
}));
|
|
74449
|
-
},
|
|
74450
|
-
async write(spec, value) {
|
|
74451
|
-
await backend.set({
|
|
74452
|
-
namespace: spec.namespace,
|
|
74453
|
-
collection: spec.collection,
|
|
74454
|
-
key: spec.row,
|
|
74455
|
-
value
|
|
74456
|
-
});
|
|
74457
|
-
}
|
|
74458
|
-
};
|
|
74459
|
-
}
|
|
74460
|
-
function planNestedRetiredKeyPurge(blob, spec) {
|
|
74461
|
-
const nestedKeys = spec.mapValueKeys ?? [];
|
|
74462
|
-
if (nestedKeys.length === 0) return [];
|
|
74463
|
-
const found = [];
|
|
74464
|
-
for (const [mapKey, value] of Object.entries(blob)) {
|
|
74465
|
-
const inner = require_dist10.asJsonObject(value);
|
|
74466
|
-
if (inner === null) continue;
|
|
74467
|
-
for (const retired of nestedKeys) if (retired in inner) found.push(`${mapKey}.${retired}`);
|
|
74468
|
-
}
|
|
74469
|
-
return found;
|
|
74470
|
-
}
|
|
74471
|
-
function planRetiredKeyPurge(blob, spec) {
|
|
74472
|
-
const exact = new Set(spec.keys);
|
|
74473
|
-
const found = [];
|
|
74474
|
-
for (const key of Object.keys(blob)) {
|
|
74475
|
-
if (exact.has(key)) {
|
|
74476
|
-
found.push(key);
|
|
74477
|
-
continue;
|
|
74478
|
-
}
|
|
74479
|
-
for (const perNode of spec.perNodeKeys) if (key === perNode || key.startsWith(`${perNode}@`)) {
|
|
74480
|
-
found.push(key);
|
|
74481
|
-
break;
|
|
74482
|
-
}
|
|
74483
|
-
}
|
|
74484
|
-
return found;
|
|
75090
|
+
function retiredCollectionStoreOf(backend) {
|
|
75091
|
+
return { async drop(collection) {
|
|
75092
|
+
return backend.dropRetiredTable(collection);
|
|
75093
|
+
} };
|
|
74485
75094
|
}
|
|
74486
|
-
async function
|
|
74487
|
-
if (!settingsStoreIsAuthoritativeHere(env)) return [];
|
|
75095
|
+
async function purgeRetiredCollections(store, logger, specs = RETIRED_COLLECTIONS) {
|
|
74488
75096
|
const results = [];
|
|
74489
75097
|
for (const spec of specs) {
|
|
74490
|
-
let
|
|
75098
|
+
let dropped;
|
|
74491
75099
|
try {
|
|
74492
|
-
|
|
75100
|
+
dropped = await store.drop(spec.collection);
|
|
74493
75101
|
} catch (err) {
|
|
74494
|
-
logger.warn("retired
|
|
74495
|
-
addon: spec.
|
|
75102
|
+
logger.warn("retired collection \u2014 drop failed, table left in place", { meta: {
|
|
75103
|
+
addon: spec.owner,
|
|
74496
75104
|
collection: spec.collection,
|
|
74497
|
-
row: spec.row,
|
|
74498
75105
|
error: err instanceof Error ? err.message : String(err)
|
|
74499
75106
|
} });
|
|
74500
75107
|
continue;
|
|
74501
75108
|
}
|
|
74502
|
-
if (
|
|
74503
|
-
const doomed = planRetiredKeyPurge(blob, spec);
|
|
74504
|
-
const doomedNested = planNestedRetiredKeyPurge(blob, spec);
|
|
74505
|
-
if (doomed.length === 0 && doomedNested.length === 0) continue;
|
|
74506
|
-
const doomedSet = new Set(doomed);
|
|
74507
|
-
const nestedByMapKey = /* @__PURE__ */ new Map();
|
|
74508
|
-
for (const path of doomedNested) {
|
|
74509
|
-
const cut = path.indexOf(".");
|
|
74510
|
-
const mapKey = path.slice(0, cut);
|
|
74511
|
-
const inner = path.slice(cut + 1);
|
|
74512
|
-
const bucket = nestedByMapKey.get(mapKey) ?? /* @__PURE__ */ new Set();
|
|
74513
|
-
bucket.add(inner);
|
|
74514
|
-
nestedByMapKey.set(mapKey, bucket);
|
|
74515
|
-
}
|
|
74516
|
-
const next = Object.fromEntries(Object.entries(blob).filter(([key]) => !doomedSet.has(key)).map(([key, value]) => {
|
|
74517
|
-
const retired = nestedByMapKey.get(key);
|
|
74518
|
-
if (retired === void 0) return [key, value];
|
|
74519
|
-
const inner = require_dist10.asJsonObject(value);
|
|
74520
|
-
if (inner === null) return [key, value];
|
|
74521
|
-
return [key, Object.fromEntries(Object.entries(inner).filter(([k]) => !retired.has(k)))];
|
|
74522
|
-
}));
|
|
74523
|
-
await store.write(spec, next);
|
|
75109
|
+
if (dropped === null) continue;
|
|
74524
75110
|
const result = {
|
|
74525
|
-
addon: spec.namespace,
|
|
74526
75111
|
collection: spec.collection,
|
|
74527
|
-
|
|
74528
|
-
|
|
74529
|
-
nested: doomedNested
|
|
75112
|
+
owner: spec.owner,
|
|
75113
|
+
rows: dropped.rows
|
|
74530
75114
|
};
|
|
74531
75115
|
results.push(result);
|
|
74532
|
-
|
|
75116
|
+
const line = "dropped a retired collection";
|
|
75117
|
+
if (dropped.rows > 0) logger.warn(`${line} \u2014 it still held rows`, { meta: {
|
|
75118
|
+
...result,
|
|
75119
|
+
reason: spec.reason
|
|
75120
|
+
} });
|
|
75121
|
+
else logger.info(line, { meta: {
|
|
74533
75122
|
...result,
|
|
74534
75123
|
reason: spec.reason
|
|
74535
75124
|
} });
|
|
@@ -74623,6 +75212,9 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74623
75212
|
function isImposedBound(source) {
|
|
74624
75213
|
return source !== "caller";
|
|
74625
75214
|
}
|
|
75215
|
+
function isCountRow(raw) {
|
|
75216
|
+
return raw !== null && typeof raw === "object" && typeof Reflect.get(raw, "n") === "number";
|
|
75217
|
+
}
|
|
74626
75218
|
function parseRowData(raw) {
|
|
74627
75219
|
return require_dist10.asJsonObject(require_dist10.parseJsonUnknown(raw)) ?? {};
|
|
74628
75220
|
}
|
|
@@ -75307,6 +75899,31 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
75307
75899
|
}
|
|
75308
75900
|
this.structuredTables.set(table, fingerprint);
|
|
75309
75901
|
}
|
|
75902
|
+
/**
|
|
75903
|
+
* Drop a table nothing declares any more, reporting what went with it.
|
|
75904
|
+
*
|
|
75905
|
+
* The DDL lives here because the engine owns the handle AND the two memos
|
|
75906
|
+
* that outlive a table: `structuredTables` (schema fingerprint) and
|
|
75907
|
+
* `declaredCollections` (the column set `compileFilter` resolves against).
|
|
75908
|
+
* Dropping behind their backs would leave a ghost — a later declaration of
|
|
75909
|
+
* the same name would be memoised as "already materialised" against a table
|
|
75910
|
+
* that no longer exists.
|
|
75911
|
+
*
|
|
75912
|
+
* WHICH tables are retired, and the logging, are policy: they live in
|
|
75913
|
+
* `retired-collections.ts`. This method knows only how to drop one.
|
|
75914
|
+
*
|
|
75915
|
+
* Answers `null` when the table is not there — the steady state after the
|
|
75916
|
+
* first boot that ran the purge, and the case on a fresh install.
|
|
75917
|
+
*/
|
|
75918
|
+
dropRetiredTable(table) {
|
|
75919
|
+
if (this.getDb().prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`).get(table) === void 0) return null;
|
|
75920
|
+
const counted = this.getDb().prepare(`SELECT COUNT(*) AS n FROM "${table}"`).get();
|
|
75921
|
+
const rows = isCountRow(counted) ? counted.n : 0;
|
|
75922
|
+
this.getDb().exec(`DROP TABLE "${table}"`);
|
|
75923
|
+
this.structuredTables.delete(table);
|
|
75924
|
+
this.declaredCollections.delete(table);
|
|
75925
|
+
return { rows };
|
|
75926
|
+
}
|
|
75310
75927
|
async tableInsert(table, row) {
|
|
75311
75928
|
const keys = Object.keys(row);
|
|
75312
75929
|
const cols = keys.map((k) => `"${k}"`).join(", ");
|
|
@@ -75817,10 +76434,11 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
75817
76434
|
this.backend = new SqliteSettingsBackend(dbPath, { ...require_dist10.RUNTIME_DEFAULTS }, this.ctx.logger.child("Query"));
|
|
75818
76435
|
await this.backend.initialize();
|
|
75819
76436
|
try {
|
|
75820
|
-
await purgeRetiredSettingsKeys(retiredKeyStoreOf(this.backend), this.ctx.logger.child("RetiredKeys"));
|
|
76437
|
+
await require_retired_settings_keys.purgeRetiredSettingsKeys(require_retired_settings_keys.retiredKeyStoreOf(this.backend), this.ctx.logger.child("RetiredKeys"));
|
|
75821
76438
|
} catch (err) {
|
|
75822
76439
|
this.ctx.logger.warn("Retired-key purge failed", { meta: { error: require_dist10.errMsg(err) } });
|
|
75823
76440
|
}
|
|
76441
|
+
await purgeRetiredCollections(retiredCollectionStoreOf(this.backend), this.ctx.logger.child("RetiredCollections"));
|
|
75824
76442
|
const vectorIndex = this.createVectorIndex(this.backend);
|
|
75825
76443
|
await vectorIndex.loadIndexRegistry();
|
|
75826
76444
|
this.vectorIndex = vectorIndex;
|
|
@@ -75995,7 +76613,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
75995
76613
|
[Symbol.toStringTag]: { value: "Module" }
|
|
75996
76614
|
});
|
|
75997
76615
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
75998
|
-
var require_dist10 =
|
|
76616
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
75999
76617
|
var node_crypto = __require("crypto");
|
|
76000
76618
|
var node_fs_promises = __require("fs/promises");
|
|
76001
76619
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -77874,7 +78492,7 @@ var require_system_config_addon = __commonJS({
|
|
|
77874
78492
|
[Symbol.toStringTag]: { value: "Module" }
|
|
77875
78493
|
});
|
|
77876
78494
|
require_chunk_Cek0wNdY();
|
|
77877
|
-
var require_dist10 =
|
|
78495
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
77878
78496
|
var SECTION_TITLES = {
|
|
77879
78497
|
server: "Server",
|
|
77880
78498
|
auth: "Authentication"
|
|
@@ -95935,7 +96553,7 @@ var require_winston_logging = __commonJS({
|
|
|
95935
96553
|
[Symbol.toStringTag]: { value: "Module" }
|
|
95936
96554
|
});
|
|
95937
96555
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
95938
|
-
var require_dist10 =
|
|
96556
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
95939
96557
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
95940
96558
|
var node_path = __require("path");
|
|
95941
96559
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -107405,9 +108023,9 @@ var require_dist2 = __commonJS({
|
|
|
107405
108023
|
}
|
|
107406
108024
|
});
|
|
107407
108025
|
|
|
107408
|
-
// ../system/dist/manifest-python-deps-
|
|
107409
|
-
var
|
|
107410
|
-
"../system/dist/manifest-python-deps-
|
|
108026
|
+
// ../system/dist/manifest-python-deps-MA_BZGBz.js
|
|
108027
|
+
var require_manifest_python_deps_MA_BZGBz = __commonJS({
|
|
108028
|
+
"../system/dist/manifest-python-deps-MA_BZGBz.js"(exports) {
|
|
107411
108029
|
"use strict";
|
|
107412
108030
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
107413
108031
|
var node_crypto = __require("crypto");
|
|
@@ -111384,6 +112002,8 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111384
112002
|
};
|
|
111385
112003
|
goneHandler = () => {
|
|
111386
112004
|
};
|
|
112005
|
+
/** Source of {@link RegisteredChild.incarnation}; one bump per accepted connection. */
|
|
112006
|
+
nextIncarnation = 1;
|
|
111387
112007
|
eventHandler = null;
|
|
111388
112008
|
logHandler = null;
|
|
111389
112009
|
readinessHandler = null;
|
|
@@ -111658,14 +112278,20 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111658
112278
|
listChildren() {
|
|
111659
112279
|
return [...this.children.values()].map((e) => ({
|
|
111660
112280
|
childId: e.childId,
|
|
111661
|
-
caps: e.caps
|
|
112281
|
+
caps: e.caps,
|
|
112282
|
+
incarnation: e.incarnation
|
|
111662
112283
|
}));
|
|
111663
112284
|
}
|
|
111664
112285
|
/** Register the (single) child-registered handler. Only one handler is active at a time. */
|
|
111665
112286
|
onChildRegistered(handler) {
|
|
111666
112287
|
this.registeredHandler = handler;
|
|
111667
112288
|
}
|
|
111668
|
-
/**
|
|
112289
|
+
/**
|
|
112290
|
+
* Register the (single) child-gone handler. Only one handler is active at a
|
|
112291
|
+
* time. The handler receives the {@link RegisteredChild.incarnation} of the
|
|
112292
|
+
* connection that closed — carry it into whatever teardown it triggers, so a
|
|
112293
|
+
* dead generation cannot tear down its successor's work.
|
|
112294
|
+
*/
|
|
111669
112295
|
onChildGone(handler) {
|
|
111670
112296
|
this.goneHandler = handler;
|
|
111671
112297
|
}
|
|
@@ -111750,6 +112376,7 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111750
112376
|
}
|
|
111751
112377
|
onConnection(channel) {
|
|
111752
112378
|
let childId = null;
|
|
112379
|
+
const incarnation = this.nextIncarnation++;
|
|
111753
112380
|
channel.onEvent((body) => {
|
|
111754
112381
|
const msg = body;
|
|
111755
112382
|
if (msg.kind === "event") {
|
|
@@ -111778,16 +112405,24 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111778
112405
|
if (childId !== null && childId !== msg.childId) throw new Error(`child attempted to change identity from "${childId}" to "${msg.childId}"`);
|
|
111779
112406
|
childId = msg.childId;
|
|
111780
112407
|
const eventPatterns = msg.eventPatterns ?? null;
|
|
112408
|
+
const superseded = this.children.get(msg.childId);
|
|
112409
|
+
if (superseded !== void 0 && superseded.channel !== channel) this.logger?.info("local child superseded by a new connection", {
|
|
112410
|
+
childId: msg.childId,
|
|
112411
|
+
supersededIncarnation: superseded.incarnation,
|
|
112412
|
+
incarnation
|
|
112413
|
+
});
|
|
111781
112414
|
this.children.set(msg.childId, {
|
|
111782
112415
|
childId: msg.childId,
|
|
111783
112416
|
channel,
|
|
111784
112417
|
caps: msg.caps,
|
|
111785
|
-
eventPatterns
|
|
112418
|
+
eventPatterns,
|
|
112419
|
+
incarnation
|
|
111786
112420
|
});
|
|
111787
112421
|
this.logPatternSet(msg.childId, eventPatterns);
|
|
111788
112422
|
this.registeredHandler({
|
|
111789
112423
|
childId: msg.childId,
|
|
111790
|
-
caps: msg.caps
|
|
112424
|
+
caps: msg.caps,
|
|
112425
|
+
incarnation
|
|
111791
112426
|
});
|
|
111792
112427
|
return { ok: true };
|
|
111793
112428
|
}
|
|
@@ -111822,11 +112457,21 @@ var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
|
111822
112457
|
throw new Error(`unknown child request kind: ${msg.kind}`);
|
|
111823
112458
|
});
|
|
111824
112459
|
channel.onClose(() => {
|
|
111825
|
-
if (childId
|
|
111826
|
-
|
|
111827
|
-
|
|
111828
|
-
|
|
112460
|
+
if (childId === null) return;
|
|
112461
|
+
const entry = this.children.get(childId);
|
|
112462
|
+
if (entry === void 0) return;
|
|
112463
|
+
if (entry.channel !== channel) {
|
|
112464
|
+
this.logger?.info("ignoring close of a superseded local child connection", {
|
|
112465
|
+
childId,
|
|
112466
|
+
incarnation,
|
|
112467
|
+
liveIncarnation: entry.incarnation
|
|
112468
|
+
});
|
|
112469
|
+
return;
|
|
111829
112470
|
}
|
|
112471
|
+
this.children.delete(childId);
|
|
112472
|
+
this.childEventStats.delete(childId);
|
|
112473
|
+
this.loggedPatternSet.delete(childId);
|
|
112474
|
+
this.goneHandler(childId, incarnation);
|
|
111830
112475
|
});
|
|
111831
112476
|
}
|
|
111832
112477
|
};
|
|
@@ -118184,7 +118829,7 @@ var require_dist3 = __commonJS({
|
|
|
118184
118829
|
"use strict";
|
|
118185
118830
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
118186
118831
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
118187
|
-
var require_dist10 =
|
|
118832
|
+
var require_dist10 = require_dist_C1Aea3c6();
|
|
118188
118833
|
var require_builtins_alerts_alerts_addon = require_alerts_addon();
|
|
118189
118834
|
require_alerts();
|
|
118190
118835
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
@@ -118209,7 +118854,7 @@ var require_dist3 = __commonJS({
|
|
|
118209
118854
|
require_system_config();
|
|
118210
118855
|
var require_builtins_winston_logging_index = require_winston_logging();
|
|
118211
118856
|
var require_file_data_plane = require_file_data_plane_DUHPHa_Y();
|
|
118212
|
-
var require_manifest_python_deps =
|
|
118857
|
+
var require_manifest_python_deps = require_manifest_python_deps_MA_BZGBz();
|
|
118213
118858
|
var require_resource_monitor = require_resource_monitor_CdnzxBLP();
|
|
118214
118859
|
var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
|
|
118215
118860
|
var zod = require_zod();
|
|
@@ -120105,13 +120750,9 @@ var require_dist3 = __commonJS({
|
|
|
120105
120750
|
return {};
|
|
120106
120751
|
}
|
|
120107
120752
|
var integrationCounter = 0;
|
|
120108
|
-
var deviceCounter = 0;
|
|
120109
120753
|
function nextIntegrationId() {
|
|
120110
120754
|
return `int_${String(++integrationCounter).padStart(4, "0")}`;
|
|
120111
120755
|
}
|
|
120112
|
-
function nextDeviceId() {
|
|
120113
|
-
return `dev_${String(++deviceCounter).padStart(4, "0")}`;
|
|
120114
|
-
}
|
|
120115
120756
|
var INTEGRATIONS_COLUMNS = [
|
|
120116
120757
|
{
|
|
120117
120758
|
name: "id",
|
|
@@ -120178,99 +120819,8 @@ var require_dist3 = __commonJS({
|
|
|
120178
120819
|
defaultValue: "string"
|
|
120179
120820
|
}
|
|
120180
120821
|
];
|
|
120181
|
-
var DEVICES_COLUMNS = [
|
|
120182
|
-
{
|
|
120183
|
-
name: "id",
|
|
120184
|
-
type: "TEXT",
|
|
120185
|
-
primaryKey: true
|
|
120186
|
-
},
|
|
120187
|
-
{
|
|
120188
|
-
name: "integration_id",
|
|
120189
|
-
type: "TEXT",
|
|
120190
|
-
notNull: true
|
|
120191
|
-
},
|
|
120192
|
-
{
|
|
120193
|
-
name: "stable_id",
|
|
120194
|
-
type: "TEXT",
|
|
120195
|
-
notNull: true,
|
|
120196
|
-
unique: true
|
|
120197
|
-
},
|
|
120198
|
-
{
|
|
120199
|
-
name: "type",
|
|
120200
|
-
type: "TEXT",
|
|
120201
|
-
notNull: true,
|
|
120202
|
-
defaultValue: "camera"
|
|
120203
|
-
},
|
|
120204
|
-
{
|
|
120205
|
-
name: "name",
|
|
120206
|
-
type: "TEXT",
|
|
120207
|
-
notNull: true
|
|
120208
|
-
},
|
|
120209
|
-
{
|
|
120210
|
-
name: "enabled",
|
|
120211
|
-
type: "INTEGER",
|
|
120212
|
-
notNull: true,
|
|
120213
|
-
defaultValue: 1
|
|
120214
|
-
},
|
|
120215
|
-
{
|
|
120216
|
-
name: "info",
|
|
120217
|
-
type: "JSON",
|
|
120218
|
-
notNull: true,
|
|
120219
|
-
defaultValue: "{}"
|
|
120220
|
-
},
|
|
120221
|
-
{
|
|
120222
|
-
name: "created_at",
|
|
120223
|
-
type: "INTEGER",
|
|
120224
|
-
notNull: true
|
|
120225
|
-
},
|
|
120226
|
-
{
|
|
120227
|
-
name: "updated_at",
|
|
120228
|
-
type: "INTEGER",
|
|
120229
|
-
notNull: true
|
|
120230
|
-
}
|
|
120231
|
-
];
|
|
120232
|
-
var DEVICES_INDEXES = [
|
|
120233
|
-
{
|
|
120234
|
-
name: "idx_devices_integration",
|
|
120235
|
-
columns: ["integration_id"]
|
|
120236
|
-
},
|
|
120237
|
-
{
|
|
120238
|
-
name: "idx_devices_stable",
|
|
120239
|
-
columns: ["stable_id"],
|
|
120240
|
-
unique: true
|
|
120241
|
-
},
|
|
120242
|
-
{
|
|
120243
|
-
name: "idx_devices_type",
|
|
120244
|
-
columns: ["type"]
|
|
120245
|
-
}
|
|
120246
|
-
];
|
|
120247
|
-
var DEVICE_SETTINGS_COLUMNS = [
|
|
120248
|
-
{
|
|
120249
|
-
name: "device_id",
|
|
120250
|
-
type: "TEXT",
|
|
120251
|
-
notNull: true
|
|
120252
|
-
},
|
|
120253
|
-
{
|
|
120254
|
-
name: "key",
|
|
120255
|
-
type: "TEXT",
|
|
120256
|
-
notNull: true
|
|
120257
|
-
},
|
|
120258
|
-
{
|
|
120259
|
-
name: "value",
|
|
120260
|
-
type: "TEXT",
|
|
120261
|
-
notNull: true
|
|
120262
|
-
},
|
|
120263
|
-
{
|
|
120264
|
-
name: "value_type",
|
|
120265
|
-
type: "TEXT",
|
|
120266
|
-
notNull: true,
|
|
120267
|
-
defaultValue: "string"
|
|
120268
|
-
}
|
|
120269
|
-
];
|
|
120270
120822
|
var INTEGRATIONS = "integrations";
|
|
120271
120823
|
var INTEGRATION_SETTINGS = "integration_settings";
|
|
120272
|
-
var DEVICES = "devices";
|
|
120273
|
-
var DEVICE_SETTINGS = "device_settings_kv";
|
|
120274
120824
|
var IntegrationRegistry = class IntegrationRegistry2 {
|
|
120275
120825
|
store;
|
|
120276
120826
|
constructor(backend) {
|
|
@@ -120286,15 +120836,6 @@ var require_dist3 = __commonJS({
|
|
|
120286
120836
|
collection: INTEGRATION_SETTINGS,
|
|
120287
120837
|
columns: INTEGRATION_SETTINGS_COLUMNS
|
|
120288
120838
|
});
|
|
120289
|
-
await this.store.declareCollection({
|
|
120290
|
-
collection: DEVICES,
|
|
120291
|
-
columns: DEVICES_COLUMNS,
|
|
120292
|
-
indexes: DEVICES_INDEXES
|
|
120293
|
-
});
|
|
120294
|
-
await this.store.declareCollection({
|
|
120295
|
-
collection: DEVICE_SETTINGS,
|
|
120296
|
-
columns: DEVICE_SETTINGS_COLUMNS
|
|
120297
|
-
});
|
|
120298
120839
|
const ints = await this.store.query({
|
|
120299
120840
|
collection: INTEGRATIONS,
|
|
120300
120841
|
filter: {
|
|
@@ -120309,20 +120850,6 @@ var require_dist3 = __commonJS({
|
|
|
120309
120850
|
const num = parseInt(ints[0].id.replace("int_", ""), 10);
|
|
120310
120851
|
if (!isNaN(num)) integrationCounter = num;
|
|
120311
120852
|
}
|
|
120312
|
-
const devs = await this.store.query({
|
|
120313
|
-
collection: DEVICES,
|
|
120314
|
-
filter: {
|
|
120315
|
-
orderBy: {
|
|
120316
|
-
field: "id",
|
|
120317
|
-
direction: "desc"
|
|
120318
|
-
},
|
|
120319
|
-
limit: 1
|
|
120320
|
-
}
|
|
120321
|
-
});
|
|
120322
|
-
if (devs[0]) {
|
|
120323
|
-
const num = parseInt(devs[0].id.replace("dev_", ""), 10);
|
|
120324
|
-
if (!isNaN(num)) deviceCounter = num;
|
|
120325
|
-
}
|
|
120326
120853
|
}
|
|
120327
120854
|
async createIntegration(input) {
|
|
120328
120855
|
const id = nextIntegrationId();
|
|
@@ -120406,15 +120933,6 @@ var require_dist3 = __commonJS({
|
|
|
120406
120933
|
async deleteIntegration(id) {
|
|
120407
120934
|
const integration = await this.getIntegration(id);
|
|
120408
120935
|
if (integration !== null && IntegrationRegistry2.isFixed(integration)) throw new Error(`integration "${id}" is fixed \u2014 it belongs to the addon that declared it and cannot be deleted`);
|
|
120409
|
-
const devices = await this.listDevices(id);
|
|
120410
|
-
for (const d of devices) await this.store.deleteWhere({
|
|
120411
|
-
collection: DEVICE_SETTINGS,
|
|
120412
|
-
filter: { where: { device_id: d.id } }
|
|
120413
|
-
});
|
|
120414
|
-
await this.store.deleteWhere({
|
|
120415
|
-
collection: DEVICES,
|
|
120416
|
-
filter: { where: { integration_id: id } }
|
|
120417
|
-
});
|
|
120418
120936
|
await this.store.deleteWhere({
|
|
120419
120937
|
collection: INTEGRATION_SETTINGS,
|
|
120420
120938
|
filter: { where: { integration_id: id } }
|
|
@@ -120459,139 +120977,6 @@ var require_dist3 = __commonJS({
|
|
|
120459
120977
|
async setIntegrationSettings(integrationId, settings) {
|
|
120460
120978
|
for (const [key, value] of Object.entries(settings)) await this.setIntegrationSetting(integrationId, key, value);
|
|
120461
120979
|
}
|
|
120462
|
-
async createDevice(input) {
|
|
120463
|
-
const id = nextDeviceId();
|
|
120464
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
120465
|
-
await this.store.insert({
|
|
120466
|
-
collection: DEVICES,
|
|
120467
|
-
record: {
|
|
120468
|
-
id,
|
|
120469
|
-
data: {
|
|
120470
|
-
integration_id: input.integrationId,
|
|
120471
|
-
stable_id: input.stableId,
|
|
120472
|
-
type: input.type,
|
|
120473
|
-
name: input.name,
|
|
120474
|
-
enabled: input.enabled !== false ? 1 : 0,
|
|
120475
|
-
info: input.info ?? {},
|
|
120476
|
-
created_at: now,
|
|
120477
|
-
updated_at: now
|
|
120478
|
-
}
|
|
120479
|
-
}
|
|
120480
|
-
});
|
|
120481
|
-
if (input.settings) await this.setDeviceSettings(id, input.settings);
|
|
120482
|
-
return {
|
|
120483
|
-
id,
|
|
120484
|
-
integrationId: input.integrationId,
|
|
120485
|
-
stableId: input.stableId,
|
|
120486
|
-
type: input.type,
|
|
120487
|
-
name: input.name,
|
|
120488
|
-
enabled: input.enabled !== false,
|
|
120489
|
-
info: input.info ?? {},
|
|
120490
|
-
createdAt: now,
|
|
120491
|
-
updatedAt: now
|
|
120492
|
-
};
|
|
120493
|
-
}
|
|
120494
|
-
async getDevice(id) {
|
|
120495
|
-
const rows = await this.store.query({
|
|
120496
|
-
collection: DEVICES,
|
|
120497
|
-
filter: {
|
|
120498
|
-
where: { id },
|
|
120499
|
-
limit: 1
|
|
120500
|
-
}
|
|
120501
|
-
});
|
|
120502
|
-
return rows[0] ? this.mapDevice(rows[0].data) : null;
|
|
120503
|
-
}
|
|
120504
|
-
async getDeviceByStableId(stableId) {
|
|
120505
|
-
const rows = await this.store.query({
|
|
120506
|
-
collection: DEVICES,
|
|
120507
|
-
filter: {
|
|
120508
|
-
where: { stable_id: stableId },
|
|
120509
|
-
limit: 1
|
|
120510
|
-
}
|
|
120511
|
-
});
|
|
120512
|
-
return rows[0] ? this.mapDevice(rows[0].data) : null;
|
|
120513
|
-
}
|
|
120514
|
-
async listDevices(integrationId) {
|
|
120515
|
-
return (await this.store.query({
|
|
120516
|
-
collection: DEVICES,
|
|
120517
|
-
filter: {
|
|
120518
|
-
...integrationId ? { where: { integration_id: integrationId } } : {},
|
|
120519
|
-
orderBy: {
|
|
120520
|
-
field: "created_at",
|
|
120521
|
-
direction: "asc"
|
|
120522
|
-
}
|
|
120523
|
-
}
|
|
120524
|
-
})).map((r) => this.mapDevice(r.data));
|
|
120525
|
-
}
|
|
120526
|
-
async listCameras() {
|
|
120527
|
-
return (await this.store.query({
|
|
120528
|
-
collection: DEVICES,
|
|
120529
|
-
filter: {
|
|
120530
|
-
where: { type: "camera" },
|
|
120531
|
-
orderBy: {
|
|
120532
|
-
field: "created_at",
|
|
120533
|
-
direction: "asc"
|
|
120534
|
-
}
|
|
120535
|
-
}
|
|
120536
|
-
})).map((r) => this.mapDevice(r.data));
|
|
120537
|
-
}
|
|
120538
|
-
async updateDevice(id, updates) {
|
|
120539
|
-
const updateRow = { updated_at: Math.floor(Date.now() / 1e3) };
|
|
120540
|
-
if (updates.name !== void 0) updateRow["name"] = updates.name;
|
|
120541
|
-
if (updates.enabled !== void 0) updateRow["enabled"] = updates.enabled ? 1 : 0;
|
|
120542
|
-
if (updates.info !== void 0) updateRow["info"] = updates.info;
|
|
120543
|
-
await this.store.updateWhere({
|
|
120544
|
-
collection: DEVICES,
|
|
120545
|
-
filter: { where: { id } },
|
|
120546
|
-
data: updateRow
|
|
120547
|
-
});
|
|
120548
|
-
return this.getDevice(id);
|
|
120549
|
-
}
|
|
120550
|
-
async deleteDevice(id) {
|
|
120551
|
-
await this.store.deleteWhere({
|
|
120552
|
-
collection: DEVICE_SETTINGS,
|
|
120553
|
-
filter: { where: { device_id: id } }
|
|
120554
|
-
});
|
|
120555
|
-
await this.store.deleteWhere({
|
|
120556
|
-
collection: DEVICES,
|
|
120557
|
-
filter: { where: { id } }
|
|
120558
|
-
});
|
|
120559
|
-
return true;
|
|
120560
|
-
}
|
|
120561
|
-
async getDeviceSettings(deviceId) {
|
|
120562
|
-
const result = {};
|
|
120563
|
-
const rows = await this.store.query({
|
|
120564
|
-
collection: DEVICE_SETTINGS,
|
|
120565
|
-
filter: { where: { device_id: deviceId } }
|
|
120566
|
-
});
|
|
120567
|
-
for (const row of rows) result[String(row.data["key"])] = deserializeSetting(row.data["value"], String(row.data["value_type"]));
|
|
120568
|
-
return result;
|
|
120569
|
-
}
|
|
120570
|
-
async setDeviceSetting(deviceId, key, value) {
|
|
120571
|
-
const s = serializeSetting(value);
|
|
120572
|
-
await this.store.deleteWhere({
|
|
120573
|
-
collection: DEVICE_SETTINGS,
|
|
120574
|
-
filter: { where: {
|
|
120575
|
-
device_id: deviceId,
|
|
120576
|
-
key
|
|
120577
|
-
} }
|
|
120578
|
-
});
|
|
120579
|
-
await this.store.insert({
|
|
120580
|
-
collection: DEVICE_SETTINGS,
|
|
120581
|
-
record: {
|
|
120582
|
-
id: `${deviceId}:${key}`,
|
|
120583
|
-
data: {
|
|
120584
|
-
device_id: deviceId,
|
|
120585
|
-
key,
|
|
120586
|
-
value: s.value,
|
|
120587
|
-
value_type: s.valueType
|
|
120588
|
-
}
|
|
120589
|
-
}
|
|
120590
|
-
});
|
|
120591
|
-
}
|
|
120592
|
-
async setDeviceSettings(deviceId, settings) {
|
|
120593
|
-
for (const [key, value] of Object.entries(settings)) await this.setDeviceSetting(deviceId, key, value);
|
|
120594
|
-
}
|
|
120595
120980
|
mapIntegration(row) {
|
|
120596
120981
|
return {
|
|
120597
120982
|
id: String(row["id"]),
|
|
@@ -120603,19 +120988,6 @@ var require_dist3 = __commonJS({
|
|
|
120603
120988
|
updatedAt: Number(row["updated_at"])
|
|
120604
120989
|
};
|
|
120605
120990
|
}
|
|
120606
|
-
mapDevice(row) {
|
|
120607
|
-
return {
|
|
120608
|
-
id: String(row["id"]),
|
|
120609
|
-
integrationId: String(row["integration_id"]),
|
|
120610
|
-
stableId: String(row["stable_id"]),
|
|
120611
|
-
type: String(row["type"]),
|
|
120612
|
-
name: String(row["name"]),
|
|
120613
|
-
enabled: row["enabled"] === 1,
|
|
120614
|
-
info: readInfo(row["info"]),
|
|
120615
|
-
createdAt: Number(row["created_at"]),
|
|
120616
|
-
updatedAt: Number(row["updated_at"])
|
|
120617
|
-
};
|
|
120618
|
-
}
|
|
120619
120991
|
};
|
|
120620
120992
|
var execFileAsync$2 = (0, node_util.promisify)(node_child_process.execFile);
|
|
120621
120993
|
var NATIVE_SCAN_DEPTH = 4;
|
|
@@ -123109,6 +123481,7 @@ var require_dist3 = __commonJS({
|
|
|
123109
123481
|
const state = {
|
|
123110
123482
|
definition,
|
|
123111
123483
|
providers: /* @__PURE__ */ new Map(),
|
|
123484
|
+
providerOwners: /* @__PURE__ */ new Map(),
|
|
123112
123485
|
activeAddonId: null,
|
|
123113
123486
|
router: null,
|
|
123114
123487
|
disabledProviders: /* @__PURE__ */ new Set()
|
|
@@ -123166,8 +123539,15 @@ var require_dist3 = __commonJS({
|
|
|
123166
123539
|
* Double-register of the same pair is always a programmer error —
|
|
123167
123540
|
* two legitimately different addons implementing the same cap keep
|
|
123168
123541
|
* using different `addonId`s and the user picks one via `configReader`.
|
|
123169
|
-
|
|
123170
|
-
|
|
123542
|
+
*
|
|
123543
|
+
* `owner` (optional) stamps the registration with the incarnation that made
|
|
123544
|
+
* it — see {@link ProviderOwnerToken}. Once stamped, only an unregister
|
|
123545
|
+
* presenting the SAME token can delete it. Registration paths whose key is
|
|
123546
|
+
* reused across process generations (the UDS child manifest, the D3
|
|
123547
|
+
* `registerNode` handshake) MUST pass one; ownerless registrations keep the
|
|
123548
|
+
* historical unconditional behaviour.
|
|
123549
|
+
*/
|
|
123550
|
+
registerProvider(capabilityName, addonId, provider, owner) {
|
|
123171
123551
|
let state = this.capabilities.get(capabilityName);
|
|
123172
123552
|
if (!state) {
|
|
123173
123553
|
const colonIdx = capabilityName.indexOf(":");
|
|
@@ -123199,6 +123579,7 @@ var require_dist3 = __commonJS({
|
|
|
123199
123579
|
if (missing.length > 0) throw new Error(`CapabilityRegistry: provider "${addonId}" for capability "${capabilityName}" declares exposesDeviceSettings: true but is missing DeviceSettingsContribution method(s): ${missing.join(", ")}. Implement them on the provider (or drop the flag from the cap def).`);
|
|
123200
123580
|
}
|
|
123201
123581
|
state.providers.set(addonId, provider);
|
|
123582
|
+
if (owner !== void 0) state.providerOwners.set(addonId, owner);
|
|
123202
123583
|
if (state.definition.mode === "singleton") {
|
|
123203
123584
|
const userChoice = this.configReader?.(capabilityName);
|
|
123204
123585
|
const preferred = state.definition.preferredProvider;
|
|
@@ -123229,7 +123610,8 @@ var require_dist3 = __commonJS({
|
|
|
123229
123610
|
tags: { addonId },
|
|
123230
123611
|
meta: {
|
|
123231
123612
|
capability: capabilityName,
|
|
123232
|
-
mode: state.definition.mode
|
|
123613
|
+
mode: state.definition.mode,
|
|
123614
|
+
...owner === void 0 ? {} : { owner }
|
|
123233
123615
|
}
|
|
123234
123616
|
});
|
|
123235
123617
|
this.emitEvent("capability:provider-registered", {
|
|
@@ -123276,11 +123658,70 @@ var require_dist3 = __commonJS({
|
|
|
123276
123658
|
const declarers = this.manifestDeclarers.get(capabilityName);
|
|
123277
123659
|
return declarers ? [...declarers] : [];
|
|
123278
123660
|
}
|
|
123279
|
-
/**
|
|
123280
|
-
|
|
123661
|
+
/**
|
|
123662
|
+
* The owner token a registration declared, or `null` when the pair is not
|
|
123663
|
+
* registered or was registered without one.
|
|
123664
|
+
*/
|
|
123665
|
+
getProviderOwner(capabilityName, addonId) {
|
|
123666
|
+
return this.capabilities.get(capabilityName)?.providerOwners.get(addonId) ?? null;
|
|
123667
|
+
}
|
|
123668
|
+
/**
|
|
123669
|
+
* Hand an EXISTING registration to a new owner, without touching the provider
|
|
123670
|
+
* instance, the active-singleton selection or any event.
|
|
123671
|
+
*
|
|
123672
|
+
* The takeover case: a replacement runner handshakes with an UNCHANGED cap
|
|
123673
|
+
* set, so the manifest diff correctly leaves the registration alone — but the
|
|
123674
|
+
* registration is still stamped with the dead generation's token, and nothing
|
|
123675
|
+
* the LIVE generation can present would ever match it. Re-stamping keeps the
|
|
123676
|
+
* capability cleanable when the successor eventually dies.
|
|
123677
|
+
*
|
|
123678
|
+
* Returns `false` when the pair is not registered (nothing to hand over) —
|
|
123679
|
+
* the caller decides whether that is a register-instead or a diagnosis.
|
|
123680
|
+
*/
|
|
123681
|
+
reassignProviderOwner(capabilityName, addonId, owner) {
|
|
123682
|
+
const state = this.capabilities.get(capabilityName);
|
|
123683
|
+
if (!state || !state.providers.has(addonId)) return false;
|
|
123684
|
+
const previous = state.providerOwners.get(addonId);
|
|
123685
|
+
if (previous === owner) return true;
|
|
123686
|
+
state.providerOwners.set(addonId, owner);
|
|
123687
|
+
this.logger.info("Capability registration ownership reassigned", {
|
|
123688
|
+
tags: { addonId },
|
|
123689
|
+
meta: {
|
|
123690
|
+
capability: capabilityName,
|
|
123691
|
+
owner,
|
|
123692
|
+
previousOwner: previous ?? null
|
|
123693
|
+
}
|
|
123694
|
+
});
|
|
123695
|
+
return true;
|
|
123696
|
+
}
|
|
123697
|
+
/**
|
|
123698
|
+
* Unregister a provider. For singleton: clears active. For collection:
|
|
123699
|
+
* removes from list.
|
|
123700
|
+
*
|
|
123701
|
+
* `owner` is the caller's PROOF that it owns the registration it is deleting.
|
|
123702
|
+
* When the registration declared an owner (see {@link ProviderOwnerToken})
|
|
123703
|
+
* and the caller presents a different one — or none at all — this is a
|
|
123704
|
+
* logged no-op: the caller belongs to a generation that no longer holds the
|
|
123705
|
+
* key. This is what stops a dead runner's teardown from erasing its
|
|
123706
|
+
* successor's live registration ([D188](../../../../docs/decisions/adr-0188-an-unregister-carries-proof-of-ownership.md)).
|
|
123707
|
+
*/
|
|
123708
|
+
unregisterProvider(capabilityName, addonId, owner) {
|
|
123281
123709
|
const state = this.capabilities.get(capabilityName);
|
|
123282
123710
|
if (!state) return;
|
|
123711
|
+
const registeredOwner = state.providerOwners.get(addonId);
|
|
123712
|
+
if (registeredOwner !== void 0 && registeredOwner !== owner) {
|
|
123713
|
+
this.logger.info("Unregister ignored \u2014 registration is owned by another incarnation", {
|
|
123714
|
+
tags: { addonId },
|
|
123715
|
+
meta: {
|
|
123716
|
+
capability: capabilityName,
|
|
123717
|
+
registeredOwner,
|
|
123718
|
+
presentedOwner: owner ?? null
|
|
123719
|
+
}
|
|
123720
|
+
});
|
|
123721
|
+
return;
|
|
123722
|
+
}
|
|
123283
123723
|
state.providers.delete(addonId);
|
|
123724
|
+
state.providerOwners.delete(addonId);
|
|
123284
123725
|
if (state.definition.mode === "singleton" && state.activeAddonId === addonId) {
|
|
123285
123726
|
const userChoice = this.configReader?.(capabilityName);
|
|
123286
123727
|
const preferred = state.definition.preferredProvider;
|
|
@@ -198254,6 +198695,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198254
198695
|
function safeSegment(name2) {
|
|
198255
198696
|
return name2.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
198256
198697
|
}
|
|
198698
|
+
var EXACT_SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
198257
198699
|
var StagingArea = class {
|
|
198258
198700
|
stagingDir;
|
|
198259
198701
|
deps;
|
|
@@ -198271,8 +198713,12 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198271
198713
|
if (input.signal.aborted) throw new Error("staging aborted after fetch");
|
|
198272
198714
|
await this.deps.extract(tgz, destDir);
|
|
198273
198715
|
const raw = JSON.parse(node_fs.readFileSync(node_path.join(destDir, "package.json"), "utf-8"));
|
|
198274
|
-
if (raw.version !==
|
|
198275
|
-
|
|
198716
|
+
if (typeof raw.version !== "string" || raw.version.length === 0) throw new Error(`staging validation failed: ${input.name} staged a package.json with no version`);
|
|
198717
|
+
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}`);
|
|
198718
|
+
return {
|
|
198719
|
+
stagedPath: destDir,
|
|
198720
|
+
resolvedVersion: raw.version
|
|
198721
|
+
};
|
|
198276
198722
|
}
|
|
198277
198723
|
cleanup(jobId) {
|
|
198278
198724
|
node_fs.rmSync(node_path.join(this.stagingDir, jobId), {
|
|
@@ -238430,7 +238876,7 @@ var require_dist9 = __commonJS({
|
|
|
238430
238876
|
postBufferSec: zod.z.number().min(0).optional()
|
|
238431
238877
|
});
|
|
238432
238878
|
var DEFAULT_EVENTS_BAND_BUFFER_SEC = {
|
|
238433
|
-
preBufferSec:
|
|
238879
|
+
preBufferSec: 15,
|
|
238434
238880
|
postBufferSec: 30
|
|
238435
238881
|
};
|
|
238436
238882
|
var VISIT_MERGE_GAP_MS = DEFAULT_EVENTS_BAND_BUFFER_SEC.postBufferSec * 1e3;
|
|
@@ -239044,12 +239490,17 @@ var require_dist9 = __commonJS({
|
|
|
239044
239490
|
"tflite",
|
|
239045
239491
|
"pt"
|
|
239046
239492
|
];
|
|
239493
|
+
var FAMILY_LABEL = {
|
|
239494
|
+
yolo26: "YOLO26",
|
|
239495
|
+
yolov9: "YOLOv9"
|
|
239496
|
+
};
|
|
239047
239497
|
function familyLabel(family) {
|
|
239048
|
-
return family.toUpperCase();
|
|
239498
|
+
return FAMILY_LABEL[family] ?? family.toUpperCase();
|
|
239049
239499
|
}
|
|
239500
|
+
var RESOLUTION_IN_NAME = /\s*@\s*\d+\s*(?:[x×]\s*\d+)?/gi;
|
|
239050
239501
|
function tierLabel(family, baseName) {
|
|
239051
239502
|
const fl = familyLabel(family);
|
|
239052
|
-
const stripped = baseName.replace(new RegExp(`^${fl}\\s+`, "i"), "").trim();
|
|
239503
|
+
const stripped = baseName.replace(new RegExp(`^${fl}\\s+`, "i"), "").replace(RESOLUTION_IN_NAME, "").trim();
|
|
239053
239504
|
return stripped.length > 0 ? stripped : baseName;
|
|
239054
239505
|
}
|
|
239055
239506
|
function variantLabel(precision, optimization) {
|
|
@@ -239066,9 +239517,11 @@ var require_dist9 = __commonJS({
|
|
|
239066
239517
|
return sizes.length > 0 ? Math.min(...sizes) : 0;
|
|
239067
239518
|
}
|
|
239068
239519
|
var TIER_ORDER = [
|
|
239520
|
+
"t",
|
|
239069
239521
|
"n",
|
|
239070
239522
|
"s",
|
|
239071
239523
|
"m",
|
|
239524
|
+
"c",
|
|
239072
239525
|
"l",
|
|
239073
239526
|
"x"
|
|
239074
239527
|
];
|
|
@@ -239105,7 +239558,7 @@ var require_dist9 = __commonJS({
|
|
|
239105
239558
|
if (a.optimization !== b.optimization) return a.optimization === "fast" ? 1 : -1;
|
|
239106
239559
|
return PRECISION_ORDER.indexOf(a.precision) - PRECISION_ORDER.indexOf(b.precision);
|
|
239107
239560
|
});
|
|
239108
|
-
const base = options.find((o) => o.precision === "fp32" && o.optimization === "standard"
|
|
239561
|
+
const base = options.find((o) => o.precision === "fp32" && o.optimization === "standard") ?? options[0];
|
|
239109
239562
|
if (base === void 0) continue;
|
|
239110
239563
|
const baseEntry = entries2.find((e) => e.id === base.modelId);
|
|
239111
239564
|
tierList.push({
|
|
@@ -239135,15 +239588,9 @@ var require_dist9 = __commonJS({
|
|
|
239135
239588
|
function resolveVariantModelId(models, selection) {
|
|
239136
239589
|
const precision = selection.precision ?? "fp32";
|
|
239137
239590
|
const optimization = selection.optimization ?? "standard";
|
|
239138
|
-
|
|
239139
|
-
|
|
239140
|
-
|
|
239141
|
-
if ((m.group.precision ?? "fp32") !== precision) continue;
|
|
239142
|
-
if ((m.group.optimization ?? "standard") !== optimization) continue;
|
|
239143
|
-
if (m.group.resolution !== selection.resolution) continue;
|
|
239144
|
-
return m.id;
|
|
239145
|
-
}
|
|
239146
|
-
return null;
|
|
239591
|
+
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);
|
|
239592
|
+
if (selection.resolution !== void 0) return candidates.find((m) => m.group?.resolution === selection.resolution)?.id ?? null;
|
|
239593
|
+
return candidates.toSorted((a, b) => (b.group?.resolution ?? Infinity) - (a.group?.resolution ?? Infinity))[0]?.id ?? null;
|
|
239147
239594
|
}
|
|
239148
239595
|
function describeModelVariant(models, modelId) {
|
|
239149
239596
|
const entry = models.find((m) => m.id === modelId);
|
|
@@ -240550,11 +240997,15 @@ export default function block(ctx: AddonContext) {
|
|
|
240550
240997
|
): Promise<void> {
|
|
240551
240998
|
const resolved: Record<string, unknown> = {}
|
|
240552
240999
|
for (const [k, v] of Object.entries(args)) resolved[k] = substitute(v, vars)
|
|
240553
|
-
const client = (ctx.api
|
|
240554
|
-
|
|
241000
|
+
const client: unknown = Reflect.get(ctx.api, capToRouterKey(cap))
|
|
241001
|
+
const proc: unknown =
|
|
241002
|
+
typeof client === 'object' && client !== null ? Reflect.get(client, method) : undefined
|
|
241003
|
+
const mutate: unknown =
|
|
241004
|
+
typeof proc === 'object' && proc !== null ? Reflect.get(proc, 'mutate') : undefined
|
|
241005
|
+
if (typeof mutate !== 'function') {
|
|
240555
241006
|
throw new Error('no such device method: ' + cap + '.' + method)
|
|
240556
241007
|
}
|
|
240557
|
-
await
|
|
241008
|
+
await mutate({ ...resolved, deviceId })
|
|
240558
241009
|
}
|
|
240559
241010
|
|
|
240560
241011
|
async function evaluateConditions(): Promise<boolean> {
|
|
@@ -246401,128 +246852,103 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
246401
246852
|
var AUDIO_MACRO_LABELS = [
|
|
246402
246853
|
{
|
|
246403
246854
|
id: "speech",
|
|
246404
|
-
name: "Speech"
|
|
246405
|
-
icon: "\u{1F5E3}\uFE0F"
|
|
246855
|
+
name: "Speech"
|
|
246406
246856
|
},
|
|
246407
246857
|
{
|
|
246408
246858
|
id: "scream",
|
|
246409
|
-
name: "Scream / Shout"
|
|
246410
|
-
icon: "\u{1F631}"
|
|
246859
|
+
name: "Scream / Shout"
|
|
246411
246860
|
},
|
|
246412
246861
|
{
|
|
246413
246862
|
id: "crying",
|
|
246414
|
-
name: "Crying / Baby"
|
|
246415
|
-
icon: "\u{1F622}"
|
|
246863
|
+
name: "Crying / Baby"
|
|
246416
246864
|
},
|
|
246417
246865
|
{
|
|
246418
246866
|
id: "laughter",
|
|
246419
|
-
name: "Laughter"
|
|
246420
|
-
icon: "\u{1F602}"
|
|
246867
|
+
name: "Laughter"
|
|
246421
246868
|
},
|
|
246422
246869
|
{
|
|
246423
246870
|
id: "music",
|
|
246424
|
-
name: "Music"
|
|
246425
|
-
icon: "\u{1F3B5}"
|
|
246871
|
+
name: "Music"
|
|
246426
246872
|
},
|
|
246427
246873
|
{
|
|
246428
246874
|
id: "dog",
|
|
246429
|
-
name: "Dog"
|
|
246430
|
-
icon: "\u{1F415}"
|
|
246875
|
+
name: "Dog"
|
|
246431
246876
|
},
|
|
246432
246877
|
{
|
|
246433
246878
|
id: "cat",
|
|
246434
|
-
name: "Cat"
|
|
246435
|
-
icon: "\u{1F408}"
|
|
246879
|
+
name: "Cat"
|
|
246436
246880
|
},
|
|
246437
246881
|
{
|
|
246438
246882
|
id: "bird",
|
|
246439
|
-
name: "Bird"
|
|
246440
|
-
icon: "\u{1F426}"
|
|
246883
|
+
name: "Bird"
|
|
246441
246884
|
},
|
|
246442
246885
|
{
|
|
246443
246886
|
id: "animal",
|
|
246444
|
-
name: "Animal (other)"
|
|
246445
|
-
icon: "\u{1F43E}"
|
|
246887
|
+
name: "Animal (other)"
|
|
246446
246888
|
},
|
|
246447
246889
|
{
|
|
246448
246890
|
id: "alarm",
|
|
246449
|
-
name: "Alarm / Siren"
|
|
246450
|
-
icon: "\u{1F6A8}"
|
|
246891
|
+
name: "Alarm / Siren"
|
|
246451
246892
|
},
|
|
246452
246893
|
{
|
|
246453
246894
|
id: "doorbell",
|
|
246454
|
-
name: "Doorbell / Knock"
|
|
246455
|
-
icon: "\u{1F514}"
|
|
246895
|
+
name: "Doorbell / Knock"
|
|
246456
246896
|
},
|
|
246457
246897
|
{
|
|
246458
246898
|
id: "glass_breaking",
|
|
246459
|
-
name: "Glass Breaking"
|
|
246460
|
-
icon: "\u{1F4A5}"
|
|
246899
|
+
name: "Glass Breaking"
|
|
246461
246900
|
},
|
|
246462
246901
|
{
|
|
246463
246902
|
id: "gunshot",
|
|
246464
|
-
name: "Gunshot / Explosion"
|
|
246465
|
-
icon: "\u{1F4A3}"
|
|
246903
|
+
name: "Gunshot / Explosion"
|
|
246466
246904
|
},
|
|
246467
246905
|
{
|
|
246468
246906
|
id: "vehicle",
|
|
246469
|
-
name: "Vehicle"
|
|
246470
|
-
icon: "\u{1F697}"
|
|
246907
|
+
name: "Vehicle"
|
|
246471
246908
|
},
|
|
246472
246909
|
{
|
|
246473
246910
|
id: "siren",
|
|
246474
|
-
name: "Emergency Siren"
|
|
246475
|
-
icon: "\u{1F691}"
|
|
246911
|
+
name: "Emergency Siren"
|
|
246476
246912
|
},
|
|
246477
246913
|
{
|
|
246478
246914
|
id: "fire",
|
|
246479
|
-
name: "Fire / Smoke"
|
|
246480
|
-
icon: "\u{1F525}"
|
|
246915
|
+
name: "Fire / Smoke"
|
|
246481
246916
|
},
|
|
246482
246917
|
{
|
|
246483
246918
|
id: "water",
|
|
246484
|
-
name: "Water"
|
|
246485
|
-
icon: "\u{1F4A7}"
|
|
246919
|
+
name: "Water"
|
|
246486
246920
|
},
|
|
246487
246921
|
{
|
|
246488
246922
|
id: "wind",
|
|
246489
|
-
name: "Wind / Weather"
|
|
246490
|
-
icon: "\u{1F32C}\uFE0F"
|
|
246923
|
+
name: "Wind / Weather"
|
|
246491
246924
|
},
|
|
246492
246925
|
{
|
|
246493
246926
|
id: "door",
|
|
246494
|
-
name: "Door"
|
|
246495
|
-
icon: "\u{1F6AA}"
|
|
246927
|
+
name: "Door"
|
|
246496
246928
|
},
|
|
246497
246929
|
{
|
|
246498
246930
|
id: "footsteps",
|
|
246499
|
-
name: "Footsteps"
|
|
246500
|
-
icon: "\u{1F463}"
|
|
246931
|
+
name: "Footsteps"
|
|
246501
246932
|
},
|
|
246502
246933
|
{
|
|
246503
246934
|
id: "crowd",
|
|
246504
|
-
name: "Crowd / Chatter"
|
|
246505
|
-
icon: "\u{1F465}"
|
|
246935
|
+
name: "Crowd / Chatter"
|
|
246506
246936
|
},
|
|
246507
246937
|
{
|
|
246508
246938
|
id: "telephone",
|
|
246509
|
-
name: "Telephone"
|
|
246510
|
-
icon: "\u{1F4DE}"
|
|
246939
|
+
name: "Telephone"
|
|
246511
246940
|
},
|
|
246512
246941
|
{
|
|
246513
246942
|
id: "engine",
|
|
246514
|
-
name: "Engine / Motor"
|
|
246515
|
-
icon: "\u2699\uFE0F"
|
|
246943
|
+
name: "Engine / Motor"
|
|
246516
246944
|
},
|
|
246517
246945
|
{
|
|
246518
246946
|
id: "tools",
|
|
246519
|
-
name: "Tools / Construction"
|
|
246520
|
-
icon: "\u{1F528}"
|
|
246947
|
+
name: "Tools / Construction"
|
|
246521
246948
|
},
|
|
246522
246949
|
{
|
|
246523
246950
|
id: "silence",
|
|
246524
|
-
name: "Silence"
|
|
246525
|
-
icon: "\u{1F92B}"
|
|
246951
|
+
name: "Silence"
|
|
246526
246952
|
}
|
|
246527
246953
|
];
|
|
246528
246954
|
var YAMNET_TO_MACRO = {
|
|
@@ -249110,6 +249536,35 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
249110
249536
|
*/
|
|
249111
249537
|
hasFace: zod.z.boolean().optional(),
|
|
249112
249538
|
/**
|
|
249539
|
+
* This track has a face row IN THE GALLERY: a crop **and** an embedding — a
|
|
249540
|
+
* face an operator could ASSIGN to an identity.
|
|
249541
|
+
*
|
|
249542
|
+
* The STRICT twin of {@link hasFace}, and the pair only earns its keep
|
|
249543
|
+
* because the two disagree. `hasFace` is stamped at the TOP of the face
|
|
249544
|
+
* branch, before every gate, and means no more than "a face detector produced
|
|
249545
|
+
* a face detail". This one is stamped at the single moment the gallery row
|
|
249546
|
+
* LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
|
|
249547
|
+
* past the embedding-magnitude verdict, the `minFacePx` detection gate, the
|
|
249548
|
+
* candidate gate, the imageless-track drop (no crop was ever captured) and
|
|
249549
|
+
* the crop-store drop. Everything between the detector and that insert can
|
|
249550
|
+
* legitimately refuse the face, so a flag written any earlier promises the
|
|
249551
|
+
* operator something to assign and delivers nothing.
|
|
249552
|
+
*
|
|
249553
|
+
* **Independent of recognition.** A face collected but never auto-matched is
|
|
249554
|
+
* still assignable — it is in fact the face an operator most wants to reach —
|
|
249555
|
+
* so this is NOT gated on `recognizedIdentityId`. Recognition lands in
|
|
249556
|
+
* `subLabel`; this says only that the raw material exists.
|
|
249557
|
+
*
|
|
249558
|
+
* **Set once, never cleared.** A track that produced a gallery row produced
|
|
249559
|
+
* one; deleting the row later is the gallery's business, not this flag's.
|
|
249560
|
+
*
|
|
249561
|
+
* **Absent ≠ false**, the same rule as {@link hasFace}: every row written
|
|
249562
|
+
* before the column omits it, and so does every server that predates the
|
|
249563
|
+
* field. A consumer must test `=== true` and render nothing otherwise —
|
|
249564
|
+
* never infer "no assignable face".
|
|
249565
|
+
*/
|
|
249566
|
+
hasEmbeddedFace: zod.z.boolean().optional(),
|
|
249567
|
+
/**
|
|
249113
249568
|
* This subject CONTAINS a folded rider — a person the rider-pairing step
|
|
249114
249569
|
* ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
|
|
249115
249570
|
* so the passage is tracked once and as a VEHICLE.
|
|
@@ -250870,6 +251325,32 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
250870
251325
|
/** FRAME-space bbox (already mapped back from crop space). */
|
|
250871
251326
|
bbox: NativeCropBboxSchema.optional(),
|
|
250872
251327
|
embedding: zod.z.string().optional(),
|
|
251328
|
+
/**
|
|
251329
|
+
* L2 magnitude of the RAW pre-normalization embedding. Additive and optional
|
|
251330
|
+
* so an older runner that does not send it degrades to "unmeasurable", which
|
|
251331
|
+
* the consuming gate treats as accept — never as reject.
|
|
251332
|
+
*/
|
|
251333
|
+
embeddingMagnitude: zod.z.number().optional(),
|
|
251334
|
+
/**
|
|
251335
|
+
* Model that produced `embedding` — the SAME contract `labelModelId` has for
|
|
251336
|
+
* `label`, and for the same reason: the producing node is the only side that
|
|
251337
|
+
* knows which model actually ran, and re-deriving it downstream is exactly
|
|
251338
|
+
* the inference this model exists to forbid.
|
|
251339
|
+
*
|
|
251340
|
+
* Additive and optional, so a runner that predates it degrades to
|
|
251341
|
+
* "unmeasurable" rather than to a wrong answer — the consumer falls back to
|
|
251342
|
+
* its previous assumption instead of refusing.
|
|
251343
|
+
*
|
|
251344
|
+
* Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
|
|
251345
|
+
* to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
|
|
251346
|
+
* with nothing on the wire it took the id from the GALLERY, so the gate
|
|
251347
|
+
* compared the gallery against itself and could never fire. That turned the
|
|
251348
|
+
* one guard protecting the face feature space into a tautology, and it is
|
|
251349
|
+
* why swapping `face-embedding`'s model would NOT have stranded the enrolled
|
|
251350
|
+
* gallery as the catalog comment predicts — it would have scored the new
|
|
251351
|
+
* model's vectors against the old model's samples in one cosine space.
|
|
251352
|
+
*/
|
|
251353
|
+
embeddingModelId: zod.z.string().optional(),
|
|
250873
251354
|
label: zod.z.string().optional(),
|
|
250874
251355
|
/**
|
|
250875
251356
|
* The tier `label` occupies, copied VERBATIM from the producing step's
|
|
@@ -251846,6 +252327,13 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
251846
252327
|
/** Unix timestamp (ms) when this snapshot was composed server-side. */
|
|
251847
252328
|
fetchedAt: zod.z.number()
|
|
251848
252329
|
});
|
|
252330
|
+
var INFERENCE_DEVICE_EXCLUSION_REASONS = [
|
|
252331
|
+
"disabled",
|
|
252332
|
+
"unavailable",
|
|
252333
|
+
"cannot-host-camera-root",
|
|
252334
|
+
"accelerator-preferred"
|
|
252335
|
+
];
|
|
252336
|
+
var InferenceDeviceExclusionReasonSchema = zod.z.enum(INFERENCE_DEVICE_EXCLUSION_REASONS);
|
|
251849
252337
|
var NodeInferenceDeviceSchema = zod.z.object({
|
|
251850
252338
|
/** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
|
|
251851
252339
|
key: zod.z.string(),
|
|
@@ -251876,7 +252364,17 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
251876
252364
|
* available per format; this is the stored selection that becomes the
|
|
251877
252365
|
* default for EVERY camera landing on this accelerator.
|
|
251878
252366
|
*/
|
|
251879
|
-
steps: zod.z.record(zod.z.string(), DeviceStepConfigSchema).optional()
|
|
252367
|
+
steps: zod.z.record(zod.z.string(), DeviceStepConfigSchema).optional(),
|
|
252368
|
+
/**
|
|
252369
|
+
* `null` when the device IS a camera-root candidate on this node; otherwise
|
|
252370
|
+
* the reason the dispatcher drops it. Computed by the SAME
|
|
252371
|
+
* `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
|
|
252372
|
+
* never disagree with the election — deriving it in the UI from
|
|
252373
|
+
* `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
|
|
252374
|
+
* the node's model catalog) and `accelerator-preferred` (needs the node-wide
|
|
252375
|
+
* "an accelerator is serving" predicate).
|
|
252376
|
+
*/
|
|
252377
|
+
exclusion: InferenceDeviceExclusionReasonSchema.nullable()
|
|
251880
252378
|
});
|
|
251881
252379
|
var NodeInferenceDevicesSchema = zod.z.object({
|
|
251882
252380
|
nodeId: zod.z.string(),
|
|
@@ -252694,6 +253192,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
252694
253192
|
isBattery: zod.z.boolean(),
|
|
252695
253193
|
reason: zod.z.enum([
|
|
252696
253194
|
"disabled",
|
|
253195
|
+
"offline",
|
|
252697
253196
|
"sleeping",
|
|
252698
253197
|
"unreachable",
|
|
252699
253198
|
"waking"
|
|
@@ -252784,9 +253283,17 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
252784
253283
|
sleeping: zod.z.boolean(),
|
|
252785
253284
|
/** Current device state rendered over the cached frame. State images
|
|
252786
253285
|
* remain authoritative even when their photographic background is
|
|
252787
|
-
* old; null means the link must carry a current camera frame.
|
|
253286
|
+
* old; null means the link must carry a current camera frame.
|
|
253287
|
+
*
|
|
253288
|
+
* **This is the ONLY source a surface may derive an inactive tile
|
|
253289
|
+
* from.** A client that reads a device's `online`/`disabled` flags and
|
|
253290
|
+
* draws its own verdict will disagree with the pixels it is painting
|
|
253291
|
+
* — the frame comes from here, and so must the sentence over it
|
|
253292
|
+
* (D190). Pair with `ageMs` for the age of the photograph behind the
|
|
253293
|
+
* state. */
|
|
252788
253294
|
stateReason: zod.z.enum([
|
|
252789
253295
|
"disabled",
|
|
253296
|
+
"offline",
|
|
252790
253297
|
"sleeping",
|
|
252791
253298
|
"unreachable",
|
|
252792
253299
|
"waking"
|
|
@@ -256899,7 +257406,35 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
256899
257406
|
*/
|
|
256900
257407
|
priority: zod.z.number()
|
|
256901
257408
|
});
|
|
256902
|
-
var
|
|
257409
|
+
var LocalPortSourceEnum = zod.z.enum([
|
|
257410
|
+
"server-config",
|
|
257411
|
+
"server-env",
|
|
257412
|
+
"caller-hint",
|
|
257413
|
+
"default"
|
|
257414
|
+
]);
|
|
257415
|
+
var AdvertisedLocalPortSchema = zod.z.object({
|
|
257416
|
+
port: zod.z.number().int().min(1).max(65535),
|
|
257417
|
+
source: LocalPortSourceEnum
|
|
257418
|
+
});
|
|
257419
|
+
var GetConnectionEndpointsResultSchema = zod.z.object({
|
|
257420
|
+
endpoints: zod.z.array(ConnectionEndpointSchema).readonly(),
|
|
257421
|
+
/**
|
|
257422
|
+
* The port the hub built the LAN/loopback URLs with, and where that number
|
|
257423
|
+
* came from.
|
|
257424
|
+
*
|
|
257425
|
+
* Returned rather than merely applied, because "the URL is right" and "the
|
|
257426
|
+
* client can KNOW the URL is right" are different properties. A client that
|
|
257427
|
+
* only sees a corrected URL cannot distinguish a hub that fixed the port from
|
|
257428
|
+
* a hub that echoed the port the client sent, so it cannot decide whether to
|
|
257429
|
+
* race the candidate or discard it. With `source` it can: anything but
|
|
257430
|
+
* `caller-hint` is the hub's own socket.
|
|
257431
|
+
*
|
|
257432
|
+
* Absent on hubs predating this field — a client that finds it missing is
|
|
257433
|
+
* talking to an echoing hub and must degrade exactly as it does for
|
|
257434
|
+
* `caller-hint`.
|
|
257435
|
+
*/
|
|
257436
|
+
localPort: AdvertisedLocalPortSchema
|
|
257437
|
+
});
|
|
256903
257438
|
var NotificationEndpointSchema = zod.z.object({
|
|
256904
257439
|
/** The operator's explicit choice, or null for AUTO. */
|
|
256905
257440
|
baseUrl: zod.z.string().nullable(),
|
|
@@ -256941,10 +257476,35 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
256941
257476
|
* Honours `getAllowedAddresses()` when set — addresses outside
|
|
256942
257477
|
* the allowlist are dropped (the public tunnel + loopback are
|
|
256943
257478
|
* always included as escape hatches).
|
|
257479
|
+
*
|
|
257480
|
+
* **The port is the hub's, not the caller's** (D62 — a function's fact
|
|
257481
|
+
* belongs to whoever already owns it). This method used to take a `port`
|
|
257482
|
+
* and echo it onto every LAN and loopback URL it advertised, which made
|
|
257483
|
+
* every client a second authority on a socket only the hub binds. A viewer
|
|
257484
|
+
* configured with a port-less public URL (`https://camstack.example.top`)
|
|
257485
|
+
* infers 443 from the scheme, asks with 443, and was handed
|
|
257486
|
+
* `https://192.168.1.9:443` for a hub listening on 4443. That candidate
|
|
257487
|
+
* cannot ever answer, and losing its race reads in the log exactly like
|
|
257488
|
+
* "the LAN was tried and it was slower" — so an operator sitting on his own
|
|
257489
|
+
* WiFi stayed on the public tunnel for weeks, with the LAN candidate born
|
|
257490
|
+
* dead on every single connect.
|
|
257491
|
+
*
|
|
257492
|
+
* The hub now resolves its own listen port and reports it as `localPort`,
|
|
257493
|
+
* with the SOURCE of the number, so a client can tell a fact from an echo.
|
|
257494
|
+
*
|
|
257495
|
+
* Out of scope on purpose: a LAN URL that is not the hub's own socket (a
|
|
257496
|
+
* reverse proxy in front of it on another port) is not this method's to
|
|
257497
|
+
* invent either — pin it with `setNotificationEndpoint` / a configured
|
|
257498
|
+
* origin, which is an operator statement rather than a guess.
|
|
256944
257499
|
*/
|
|
256945
257500
|
getConnectionEndpoints: require_sleep.method(zod.z.object({
|
|
256946
|
-
/**
|
|
256947
|
-
|
|
257501
|
+
/**
|
|
257502
|
+
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
257503
|
+
* written against the echoing contract keep working; the hub uses it
|
|
257504
|
+
* only when it cannot read its own port, and says so via
|
|
257505
|
+
* `localPort.source === 'caller-hint'`.
|
|
257506
|
+
*/
|
|
257507
|
+
port: zod.z.number().int().min(1).max(65535).optional(),
|
|
256948
257508
|
/** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
|
|
256949
257509
|
* candidate. Default `true`. */
|
|
256950
257510
|
includeLoopback: zod.z.boolean().optional(),
|
|
@@ -263188,10 +263748,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
263188
263748
|
} };
|
|
263189
263749
|
}
|
|
263190
263750
|
var LAST_FETCHED_FIELD = "lastFetchedAt";
|
|
263751
|
+
var RUNTIME_STATE_REFRESH_MISS_COOLDOWN_MS = 6e4;
|
|
263752
|
+
var OPERATOR_WRITTEN_STALE_MS = 10 * 6e4;
|
|
263191
263753
|
function createRuntimeStateBridge(params) {
|
|
263192
263754
|
const { runtimeState, cap, ownDeviceId, refresh, staleMs, empty, logger } = params;
|
|
263193
263755
|
const missCooldownMs = params.refreshMissCooldownMs ?? 6e4;
|
|
263756
|
+
const staleRead = params.staleRead ?? "await-refresh";
|
|
263194
263757
|
let missCooldownUntil = 0;
|
|
263758
|
+
let refreshInFlight = null;
|
|
263195
263759
|
const readFetchedAt = () => {
|
|
263196
263760
|
const value = runtimeState.getCapState(cap.name)?.[LAST_FETCHED_FIELD];
|
|
263197
263761
|
return typeof value === "number" ? value : 0;
|
|
@@ -263206,14 +263770,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
263206
263770
|
}
|
|
263207
263771
|
});
|
|
263208
263772
|
};
|
|
263209
|
-
const
|
|
263210
|
-
const slice = runtimeState.getCapState(cap.name);
|
|
263211
|
-
const fetchedAt = readFetchedAt();
|
|
263212
|
-
if (slice && Date.now() - fetchedAt <= staleMs) {
|
|
263213
|
-
missCooldownUntil = 0;
|
|
263214
|
-
return;
|
|
263215
|
-
}
|
|
263216
|
-
if (Date.now() < missCooldownUntil) return;
|
|
263773
|
+
const runRefresh = async (fetchedAt) => {
|
|
263217
263774
|
try {
|
|
263218
263775
|
await refresh();
|
|
263219
263776
|
} catch (err) {
|
|
@@ -263229,6 +263786,34 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
263229
263786
|
}
|
|
263230
263787
|
openMissCooldown(void 0);
|
|
263231
263788
|
};
|
|
263789
|
+
const startRefresh = (fetchedAt) => {
|
|
263790
|
+
const existing = refreshInFlight;
|
|
263791
|
+
if (existing !== null) return existing;
|
|
263792
|
+
const started = runRefresh(fetchedAt).catch(() => void 0).finally(() => {
|
|
263793
|
+
refreshInFlight = null;
|
|
263794
|
+
});
|
|
263795
|
+
refreshInFlight = started;
|
|
263796
|
+
return started;
|
|
263797
|
+
};
|
|
263798
|
+
const ensureFresh = async () => {
|
|
263799
|
+
const slice = runtimeState.getCapState(cap.name);
|
|
263800
|
+
const fetchedAt = readFetchedAt();
|
|
263801
|
+
if (slice && Date.now() - fetchedAt <= staleMs) {
|
|
263802
|
+
missCooldownUntil = 0;
|
|
263803
|
+
return;
|
|
263804
|
+
}
|
|
263805
|
+
if (Date.now() < missCooldownUntil) return;
|
|
263806
|
+
if (staleRead === "serve-and-revalidate") {
|
|
263807
|
+
if (refreshInFlight !== null) return;
|
|
263808
|
+
if (slice && fetchedAt > 0) {
|
|
263809
|
+
startRefresh(fetchedAt);
|
|
263810
|
+
return;
|
|
263811
|
+
}
|
|
263812
|
+
await startRefresh(fetchedAt);
|
|
263813
|
+
return;
|
|
263814
|
+
}
|
|
263815
|
+
await runRefresh(fetchedAt);
|
|
263816
|
+
};
|
|
263232
263817
|
const projectStatus = () => {
|
|
263233
263818
|
const slice = runtimeState.getCapState(cap.name);
|
|
263234
263819
|
if (!slice) return empty();
|
|
@@ -273776,6 +274361,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273776
274361
|
function normalizeAudioLabel(value) {
|
|
273777
274362
|
return value.startsWith(AUDIO_KIND_PREFIX) ? value.slice(6) : value;
|
|
273778
274363
|
}
|
|
274364
|
+
function audioKindId(macroId) {
|
|
274365
|
+
return macroId.startsWith(AUDIO_KIND_PREFIX) ? macroId : `${AUDIO_KIND_PREFIX}${macroId}`;
|
|
274366
|
+
}
|
|
273779
274367
|
var NC_AUDIO_DB_MIN = -96;
|
|
273780
274368
|
var NC_AUDIO_DB_MAX = 0;
|
|
273781
274369
|
var NC_AUDIO_DB_STEP = 3;
|
|
@@ -273823,8 +274411,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273823
274411
|
samplingSeconds: clampInt(patch.samplingSeconds ?? base.samplingSeconds, 1, 300)
|
|
273824
274412
|
};
|
|
273825
274413
|
}
|
|
273826
|
-
var UNKNOWN_LABEL_ICON = "\u{1F508}";
|
|
273827
|
-
var ICON_BY_ID = new Map(AUDIO_MACRO_LABELS.flatMap((macro2) => macro2.icon === void 0 ? [] : [[macro2.id, macro2.icon]]));
|
|
273828
274414
|
function audioLabelChoices(taxonomy, selected) {
|
|
273829
274415
|
const fromHub = taxonomy?.audioKinds ?? [];
|
|
273830
274416
|
const choices = fromHub.length > 0 ? fromHub.map((entry) => {
|
|
@@ -273832,13 +274418,13 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273832
274418
|
return {
|
|
273833
274419
|
value: id,
|
|
273834
274420
|
label: entry.label,
|
|
273835
|
-
|
|
274421
|
+
iconId: audioKindId(id),
|
|
273836
274422
|
unknown: false
|
|
273837
274423
|
};
|
|
273838
274424
|
}) : AUDIO_MACRO_LABELS.map((macro2) => ({
|
|
273839
274425
|
value: macro2.id,
|
|
273840
274426
|
label: macro2.name,
|
|
273841
|
-
|
|
274427
|
+
iconId: audioKindId(macro2.id),
|
|
273842
274428
|
unknown: false
|
|
273843
274429
|
}));
|
|
273844
274430
|
const known = new Set(choices.map((choice) => choice.value));
|
|
@@ -273850,7 +274436,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273850
274436
|
choices.push({
|
|
273851
274437
|
value: id,
|
|
273852
274438
|
label: id,
|
|
273853
|
-
|
|
274439
|
+
iconId: audioKindId(id),
|
|
273854
274440
|
unknown: true
|
|
273855
274441
|
});
|
|
273856
274442
|
}
|
|
@@ -274574,6 +275160,74 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
274574
275160
|
if (map !== void 0) return map[String(deviceId)] ?? 0;
|
|
274575
275161
|
return rule.lastGeneratedAt ?? 0;
|
|
274576
275162
|
}
|
|
275163
|
+
var CLUSTER_MODEL_SCOPED_STEPS = [{
|
|
275164
|
+
stepId: "face-embedding",
|
|
275165
|
+
label: "Face recognition model",
|
|
275166
|
+
defaultModelId: "arcface-r100",
|
|
275167
|
+
indexName: "the enrolled face gallery",
|
|
275168
|
+
options: [
|
|
275169
|
+
{
|
|
275170
|
+
id: "arcface-r100",
|
|
275171
|
+
label: "ArcFace (ResNet34)"
|
|
275172
|
+
},
|
|
275173
|
+
{
|
|
275174
|
+
id: "auraface-r100",
|
|
275175
|
+
label: "AuraFace R100"
|
|
275176
|
+
},
|
|
275177
|
+
{
|
|
275178
|
+
id: "inception-resnet-v1",
|
|
275179
|
+
label: "Inception ResNet V1"
|
|
275180
|
+
}
|
|
275181
|
+
]
|
|
275182
|
+
}, {
|
|
275183
|
+
stepId: "clip-embedding",
|
|
275184
|
+
label: "Semantic search model",
|
|
275185
|
+
defaultModelId: "mobileclip-s1",
|
|
275186
|
+
indexName: "the object semantic-search index",
|
|
275187
|
+
options: [{
|
|
275188
|
+
id: "mobileclip-s1",
|
|
275189
|
+
label: "MobileCLIP S1"
|
|
275190
|
+
}, {
|
|
275191
|
+
id: "mobileclip-s2",
|
|
275192
|
+
label: "MobileCLIP S2"
|
|
275193
|
+
}]
|
|
275194
|
+
}];
|
|
275195
|
+
var CLUSTER_MODEL_SECTION_ID = "cluster-step-models";
|
|
275196
|
+
function clusterModelSettingKey(stepId) {
|
|
275197
|
+
return `clusterModel:${stepId}`;
|
|
275198
|
+
}
|
|
275199
|
+
function isClusterScopedStep(stepId) {
|
|
275200
|
+
return CLUSTER_MODEL_SCOPED_STEPS.some((s) => s.stepId === stepId);
|
|
275201
|
+
}
|
|
275202
|
+
var DEFAULT_CLUSTER_STEP_MODELS = Object.freeze(Object.fromEntries(CLUSTER_MODEL_SCOPED_STEPS.map((s) => [s.stepId, s.defaultModelId])));
|
|
275203
|
+
var ChosenModelIdSchema = zod.z.string().min(1);
|
|
275204
|
+
function readClusterStepModels(config) {
|
|
275205
|
+
const out = {};
|
|
275206
|
+
for (const step2 of CLUSTER_MODEL_SCOPED_STEPS) {
|
|
275207
|
+
const parsed = ChosenModelIdSchema.safeParse(config[clusterModelSettingKey(step2.stepId)]);
|
|
275208
|
+
out[step2.stepId] = parsed.success ? parsed.data : step2.defaultModelId;
|
|
275209
|
+
}
|
|
275210
|
+
return out;
|
|
275211
|
+
}
|
|
275212
|
+
function isHydratedField$2(entry) {
|
|
275213
|
+
return typeof entry === "object" && entry !== null && "key" in entry;
|
|
275214
|
+
}
|
|
275215
|
+
function pickClusterStepModels(view) {
|
|
275216
|
+
if (view === null) return DEFAULT_CLUSTER_STEP_MODELS;
|
|
275217
|
+
const flat = {};
|
|
275218
|
+
const wanted = new Set(CLUSTER_MODEL_SCOPED_STEPS.map((s) => clusterModelSettingKey(s.stepId)));
|
|
275219
|
+
for (const section of view.sections) for (const entry of section.fields) {
|
|
275220
|
+
if (!isHydratedField$2(entry) || typeof entry.key !== "string") continue;
|
|
275221
|
+
if (wanted.has(entry.key)) flat[entry.key] = entry.value;
|
|
275222
|
+
}
|
|
275223
|
+
return readClusterStepModels(flat);
|
|
275224
|
+
}
|
|
275225
|
+
function resolveClusterStepModelId(stepId, models) {
|
|
275226
|
+
const step2 = CLUSTER_MODEL_SCOPED_STEPS.find((s) => s.stepId === stepId);
|
|
275227
|
+
if (step2 === void 0) return null;
|
|
275228
|
+
const chosen = models[stepId];
|
|
275229
|
+
return chosen !== void 0 && chosen !== "" ? chosen : step2.defaultModelId;
|
|
275230
|
+
}
|
|
274577
275231
|
var DETAIL_CROP_SECTION_ID = "detail-crop";
|
|
274578
275232
|
var DETAIL_CROP_PADDING_KEY = "detailCropPaddingRatio";
|
|
274579
275233
|
var DETAIL_CROP_SQUARE_KEY = "detailCropSquare";
|
|
@@ -274621,6 +275275,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
274621
275275
|
}
|
|
274622
275276
|
return readDetailCropConvention(flat);
|
|
274623
275277
|
}
|
|
275278
|
+
var FULL_IMAGE_BBOX = Object.freeze({
|
|
275279
|
+
x: 0,
|
|
275280
|
+
y: 0,
|
|
275281
|
+
w: 1,
|
|
275282
|
+
h: 1
|
|
275283
|
+
});
|
|
274624
275284
|
var DETAIL_CROP_PADDING_FIELD = {
|
|
274625
275285
|
min: 0,
|
|
274626
275286
|
max: 1,
|
|
@@ -276356,6 +277016,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276356
277016
|
exports.CAP_NAMES_WITH_STATUS = CAP_NAMES_WITH_STATUS;
|
|
276357
277017
|
exports.CAP_NODE_PIN_CONTEXT_KEY = require_sleep.CAP_NODE_PIN_CONTEXT_KEY;
|
|
276358
277018
|
exports.CAP_PROVIDER_KIND_MAP = CAP_PROVIDER_KIND_MAP;
|
|
277019
|
+
exports.CLUSTER_MODEL_SCOPED_STEPS = CLUSTER_MODEL_SCOPED_STEPS;
|
|
277020
|
+
exports.CLUSTER_MODEL_SECTION_ID = CLUSTER_MODEL_SECTION_ID;
|
|
276359
277021
|
exports.COCO_80_LABELS = COCO_80_LABELS;
|
|
276360
277022
|
exports.COCO_TO_MACRO = COCO_TO_MACRO;
|
|
276361
277023
|
exports.CONNECTION_TEST_TIMEOUT_MS = CONNECTION_TEST_TIMEOUT_MS;
|
|
@@ -276443,6 +277105,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276443
277105
|
exports.DECLARED_INTEGRATION_FIXED_KEY = DECLARED_INTEGRATION_FIXED_KEY;
|
|
276444
277106
|
exports.DEFAULT_ADDON_PLACEMENT = DEFAULT_ADDON_PLACEMENT;
|
|
276445
277107
|
exports.DEFAULT_AUDIO_ANALYZER_CONFIG = DEFAULT_AUDIO_ANALYZER_CONFIG;
|
|
277108
|
+
exports.DEFAULT_CLUSTER_STEP_MODELS = DEFAULT_CLUSTER_STEP_MODELS;
|
|
276446
277109
|
exports.DEFAULT_DECODER_HWACCEL_CONFIG = DEFAULT_DECODER_HWACCEL_CONFIG;
|
|
276447
277110
|
exports.DEFAULT_DETAIL_CROP_CONVENTION = DEFAULT_DETAIL_CROP_CONVENTION;
|
|
276448
277111
|
exports.DEFAULT_EVENTS_BAND_BUFFER_SEC = DEFAULT_EVENTS_BAND_BUFFER_SEC;
|
|
@@ -276561,6 +277224,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276561
277224
|
exports.ExpressionLiteralBindingSchema = ExpressionLiteralBindingSchema;
|
|
276562
277225
|
exports.ExpressionParseError = ExpressionParseError;
|
|
276563
277226
|
exports.ExpressionSourceSchema = ExpressionSourceSchema;
|
|
277227
|
+
exports.FULL_IMAGE_BBOX = FULL_IMAGE_BBOX;
|
|
276564
277228
|
exports.FanControlStatusSchema = FanControlStatusSchema;
|
|
276565
277229
|
exports.FanDirectionSchema = FanDirectionSchema;
|
|
276566
277230
|
exports.FeatureManifestSchema = FeatureManifestSchema;
|
|
@@ -276587,6 +277251,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276587
277251
|
exports.HumidifierStatusSchema = HumidifierStatusSchema;
|
|
276588
277252
|
exports.HumiditySensorStatusSchema = HumiditySensorStatusSchema;
|
|
276589
277253
|
exports.HvacModeSchema = HvacModeSchema;
|
|
277254
|
+
exports.INFERENCE_DEVICE_EXCLUSION_REASONS = INFERENCE_DEVICE_EXCLUSION_REASONS;
|
|
276590
277255
|
exports.ImageContractSchema = ImageContractSchema;
|
|
276591
277256
|
exports.ImageContractStateSchema = ImageContractStateSchema;
|
|
276592
277257
|
exports.ImageRotateSchema = ImageRotateSchema;
|
|
@@ -276594,6 +277259,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276594
277259
|
exports.ImageSettingsPatchSchema = ImageSettingsPatchSchema;
|
|
276595
277260
|
exports.ImageSettingsStatusSchema = ImageSettingsStatusSchema;
|
|
276596
277261
|
exports.ImageStatusSchema = ImageStatusSchema;
|
|
277262
|
+
exports.InferenceDeviceExclusionReasonSchema = InferenceDeviceExclusionReasonSchema;
|
|
276597
277263
|
exports.IngestOwnerSchema = IngestOwnerSchema;
|
|
276598
277264
|
exports.InstalledPackageSchema = InstalledPackageSchema;
|
|
276599
277265
|
exports.IntegrationLiteSchema = IntegrationLiteSchema;
|
|
@@ -276806,6 +277472,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276806
277472
|
exports.NotificationSchema = NotificationSchema;
|
|
276807
277473
|
exports.NotifierStatusSchema = NotifierStatusSchema;
|
|
276808
277474
|
exports.NumericSensorStatusSchema = NumericSensorStatusSchema;
|
|
277475
|
+
exports.OPERATOR_WRITTEN_STALE_MS = OPERATOR_WRITTEN_STALE_MS;
|
|
276809
277476
|
exports.OPS_LOG_DEFAULT_LIMIT = OPS_LOG_DEFAULT_LIMIT;
|
|
276810
277477
|
exports.OPS_LOG_RING_DEFAULT_MAX = OPS_LOG_RING_DEFAULT_MAX;
|
|
276811
277478
|
exports.OauthIntegrationDescriptorSchema = OauthIntegrationDescriptorSchema;
|
|
@@ -276888,6 +277555,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276888
277555
|
exports.RESTORED_CAP_NAMES = RESTORED_CAP_NAMES;
|
|
276889
277556
|
exports.RUNTIME_DEFAULTS = RUNTIME_DEFAULTS;
|
|
276890
277557
|
exports.RUNTIME_STATE_POLICY = RUNTIME_STATE_POLICY;
|
|
277558
|
+
exports.RUNTIME_STATE_REFRESH_MISS_COOLDOWN_MS = RUNTIME_STATE_REFRESH_MISS_COOLDOWN_MS;
|
|
276891
277559
|
exports.RUNTIME_TO_FORMAT = RUNTIME_TO_FORMAT;
|
|
276892
277560
|
exports.RawStateResultSchema = require_sleep.RawStateResultSchema;
|
|
276893
277561
|
exports.ReadGopBytesResultSchema = ReadGopBytesResultSchema;
|
|
@@ -277180,6 +277848,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277180
277848
|
exports.audioAnalyzerCapability = audioAnalyzerCapability;
|
|
277181
277849
|
exports.audioCodecCapability = audioCodecCapability;
|
|
277182
277850
|
exports.audioIsFailClosed = audioIsFailClosed;
|
|
277851
|
+
exports.audioKindId = audioKindId;
|
|
277183
277852
|
exports.audioLabelChoices = audioLabelChoices;
|
|
277184
277853
|
exports.audioMetricsCapability = audioMetricsCapability;
|
|
277185
277854
|
exports.audioModeOf = audioModeOf;
|
|
@@ -277218,6 +277887,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277218
277887
|
exports.classifyStream = classifyStream;
|
|
277219
277888
|
exports.classifyStreams = classifyStreams;
|
|
277220
277889
|
exports.climateControlCapability = climateControlCapability;
|
|
277890
|
+
exports.clusterModelSettingKey = clusterModelSettingKey;
|
|
277221
277891
|
exports.collectHydratedFieldEntries = require_sleep.collectHydratedFieldEntries;
|
|
277222
277892
|
exports.collectHydratedFieldValues = require_sleep.collectHydratedFieldValues;
|
|
277223
277893
|
exports.colorCapability = colorCapability;
|
|
@@ -277336,6 +278006,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277336
278006
|
exports.isAudioRule = isAudioRule;
|
|
277337
278007
|
exports.isBaseConditionKey = isBaseConditionKey;
|
|
277338
278008
|
exports.isBatteryPresenceFault = isBatteryPresenceFault;
|
|
278009
|
+
exports.isClusterScopedStep = isClusterScopedStep;
|
|
277339
278010
|
exports.isCollectionArrayMethod = isCollectionArrayMethod;
|
|
277340
278011
|
exports.isDeployableToAgent = isDeployableToAgent;
|
|
277341
278012
|
exports.isDetectionMacroClass = isDetectionMacroClass;
|
|
@@ -277419,6 +278090,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277419
278090
|
exports.patchAudio = patchAudio;
|
|
277420
278091
|
exports.petFeederCapability = petFeederCapability;
|
|
277421
278092
|
exports.pickAccessoryControl = pickAccessoryControl;
|
|
278093
|
+
exports.pickClusterStepModels = pickClusterStepModels;
|
|
277422
278094
|
exports.pickDetailCropConvention = pickDetailCropConvention;
|
|
277423
278095
|
exports.pickNativeLeaseOverride = pickNativeLeaseOverride;
|
|
277424
278096
|
exports.pickPreferredRtspEntry = pickPreferredRtspEntry;
|
|
@@ -277442,6 +278114,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277442
278114
|
exports.ptzAutotrackCapability = ptzAutotrackCapability;
|
|
277443
278115
|
exports.ptzCapability = ptzCapability;
|
|
277444
278116
|
exports.pythonScriptForBackend = pythonScriptForBackend;
|
|
278117
|
+
exports.readClusterStepModels = readClusterStepModels;
|
|
277445
278118
|
exports.readDetailCropConvention = readDetailCropConvention;
|
|
277446
278119
|
exports.readDeviceStateFrom = readDeviceStateFrom;
|
|
277447
278120
|
exports.readNativeLeaseOverride = readNativeLeaseOverride;
|
|
@@ -277459,6 +278132,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
277459
278132
|
exports.resolveAddonPlacement = resolveAddonPlacement;
|
|
277460
278133
|
exports.resolveAddonRuntime = resolveAddonRuntime;
|
|
277461
278134
|
exports.resolveCapMount = require_sleep.resolveCapMount;
|
|
278135
|
+
exports.resolveClusterStepModelId = resolveClusterStepModelId;
|
|
277462
278136
|
exports.resolveDetectionRuntime = resolveDetectionRuntime;
|
|
277463
278137
|
exports.resolveDeviceControlKind = resolveDeviceControlKind;
|
|
277464
278138
|
exports.resolveDeviceProfile = resolveDeviceProfile;
|
|
@@ -277768,22 +278442,20 @@ var require_update_availability_emitter = __commonJS({
|
|
|
277768
278442
|
this.persistIfDirty();
|
|
277769
278443
|
if (!latestChanged)
|
|
277770
278444
|
return;
|
|
277771
|
-
this.emitList(target);
|
|
278445
|
+
this.emitList(target, scope);
|
|
277772
278446
|
}
|
|
277773
278447
|
/** Publish a partial candidate set without treating omissions as up-to-date. */
|
|
277774
278448
|
publishCandidates(candidates) {
|
|
277775
|
-
|
|
277776
|
-
const targets = /* @__PURE__ */ new Set();
|
|
278449
|
+
const changed = /* @__PURE__ */ new Map();
|
|
277777
278450
|
for (const candidate of candidates) {
|
|
277778
|
-
|
|
277779
|
-
|
|
277780
|
-
|
|
278451
|
+
if (!this.note(candidate))
|
|
278452
|
+
continue;
|
|
278453
|
+
const scope = scopeOf(candidate);
|
|
278454
|
+
changed.set(`${candidate.target}:${scope}`, { target: candidate.target, scope });
|
|
277781
278455
|
}
|
|
277782
278456
|
this.persistIfDirty();
|
|
277783
|
-
|
|
277784
|
-
|
|
277785
|
-
for (const target of targets)
|
|
277786
|
-
this.emitList(target);
|
|
278457
|
+
for (const { target, scope } of changed.values())
|
|
278458
|
+
this.emitList(target, scope);
|
|
277787
278459
|
}
|
|
277788
278460
|
note(candidate) {
|
|
277789
278461
|
const key = this.key(candidate);
|
|
@@ -277809,20 +278481,20 @@ var require_update_availability_emitter = __commonJS({
|
|
|
277809
278481
|
held: Object.fromEntries(this.held)
|
|
277810
278482
|
});
|
|
277811
278483
|
}
|
|
277812
|
-
|
|
277813
|
-
|
|
278484
|
+
/** Announce ONE scope's full list. `scope` is the node id, `hub` for the hub. */
|
|
278485
|
+
emitList(target, scope) {
|
|
278486
|
+
const list = [...this.held.values()].filter((candidate) => candidate.target === target && scopeOf(candidate) === scope);
|
|
277814
278487
|
if (list.length === 0)
|
|
277815
278488
|
return;
|
|
277816
278489
|
const packages = list.map((candidate) => ({
|
|
277817
278490
|
packageName: candidate.packageName,
|
|
277818
278491
|
currentVersion: candidate.currentVersion,
|
|
277819
278492
|
latestVersion: candidate.latestVersion,
|
|
277820
|
-
|
|
278493
|
+
nodeId: scope
|
|
277821
278494
|
}));
|
|
277822
|
-
const nodeIds = unique(packages.map((pkg) => pkg.nodeId).filter((id) => id !== void 0));
|
|
277823
278495
|
const head = list[0];
|
|
277824
278496
|
this.eventBus.emit({
|
|
277825
|
-
id: `update.available:${target}:${packages.map((pkg) => `${pkg.
|
|
278497
|
+
id: `update.available:${target}:${scope}:${packages.map((pkg) => `${pkg.packageName}@${pkg.latestVersion}`).toSorted().join(",")}`,
|
|
277826
278498
|
timestamp: /* @__PURE__ */ new Date(),
|
|
277827
278499
|
source: this.source,
|
|
277828
278500
|
category: types_1.EventCategory.UpdateAvailable,
|
|
@@ -277832,24 +278504,24 @@ var require_update_availability_emitter = __commonJS({
|
|
|
277832
278504
|
currentVersion: head.currentVersion,
|
|
277833
278505
|
latestVersion: head.latestVersion,
|
|
277834
278506
|
packages,
|
|
277835
|
-
|
|
277836
|
-
|
|
278507
|
+
nodeId: scope,
|
|
278508
|
+
nodeIds: [scope]
|
|
277837
278509
|
}
|
|
277838
278510
|
});
|
|
277839
278511
|
}
|
|
277840
278512
|
key(candidate) {
|
|
277841
|
-
return `${candidate.target}:${candidate
|
|
278513
|
+
return `${candidate.target}:${scopeOf(candidate)}:${candidate.packageName}`;
|
|
277842
278514
|
}
|
|
277843
278515
|
};
|
|
277844
278516
|
exports.UpdateAvailabilityEmitter = UpdateAvailabilityEmitter;
|
|
278517
|
+
function scopeOf(candidate) {
|
|
278518
|
+
return candidate.nodeId ?? "hub";
|
|
278519
|
+
}
|
|
277845
278520
|
function stampNode(candidate, nodeId) {
|
|
277846
278521
|
if (nodeId === void 0 || candidate.nodeId !== void 0)
|
|
277847
278522
|
return candidate;
|
|
277848
278523
|
return { ...candidate, nodeId };
|
|
277849
278524
|
}
|
|
277850
|
-
function unique(values) {
|
|
277851
|
-
return [...new Set(values)];
|
|
277852
|
-
}
|
|
277853
278525
|
}
|
|
277854
278526
|
});
|
|
277855
278527
|
|
|
@@ -279204,7 +279876,13 @@ var require_server_root = __commonJS({
|
|
|
279204
279876
|
fromVersion: runningVersion
|
|
279205
279877
|
}
|
|
279206
279878
|
});
|
|
279207
|
-
this.restartServerFn(`${RESTART_REASON_PREFIX}: ${this.spec.packageName}@${target}
|
|
279879
|
+
this.restartServerFn(`${RESTART_REASON_PREFIX}: ${this.spec.packageName}@${target}`, {
|
|
279880
|
+
packageName: this.spec.packageName,
|
|
279881
|
+
...runningVersion !== null ? {
|
|
279882
|
+
fromVersion: runningVersion
|
|
279883
|
+
} : {},
|
|
279884
|
+
toVersion: target
|
|
279885
|
+
});
|
|
279208
279886
|
return {
|
|
279209
279887
|
accepted: true,
|
|
279210
279888
|
targetVersion: target,
|
|
@@ -279489,6 +280167,47 @@ var require_server_root = __commonJS({
|
|
|
279489
280167
|
}
|
|
279490
280168
|
});
|
|
279491
280169
|
|
|
280170
|
+
// ../../server/backend/dist/core/updates/restart-marker.js
|
|
280171
|
+
var require_restart_marker = __commonJS({
|
|
280172
|
+
"../../server/backend/dist/core/updates/restart-marker.js"(exports) {
|
|
280173
|
+
"use strict";
|
|
280174
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
280175
|
+
exports.buildRestartMarker = buildRestartMarker;
|
|
280176
|
+
exports.isServerUpdateReason = isServerUpdateReason;
|
|
280177
|
+
exports.versionFromReason = versionFromReason;
|
|
280178
|
+
var SERVER_UPDATE_PREFIX = "server-update:";
|
|
280179
|
+
var ROOT_PACKAGE_NAME = "@camstack/server";
|
|
280180
|
+
function buildRestartMarker(input) {
|
|
280181
|
+
const { requestedBy, update, requestedAt } = input;
|
|
280182
|
+
const toVersion = update?.toVersion ?? versionFromReason(requestedBy);
|
|
280183
|
+
const isUpdate = update !== void 0 || isServerUpdateReason(requestedBy) && toVersion !== void 0;
|
|
280184
|
+
if (!isUpdate) {
|
|
280185
|
+
return {
|
|
280186
|
+
kind: "manual",
|
|
280187
|
+
requestedAt,
|
|
280188
|
+
...requestedBy !== void 0 ? { requestedBy } : {}
|
|
280189
|
+
};
|
|
280190
|
+
}
|
|
280191
|
+
return {
|
|
280192
|
+
kind: "framework-update",
|
|
280193
|
+
requestedAt,
|
|
280194
|
+
...requestedBy !== void 0 ? { requestedBy } : {},
|
|
280195
|
+
packageName: update?.packageName ?? ROOT_PACKAGE_NAME,
|
|
280196
|
+
...update?.fromVersion !== void 0 ? { fromVersion: update.fromVersion } : {},
|
|
280197
|
+
...toVersion !== void 0 ? { toVersion } : {}
|
|
280198
|
+
};
|
|
280199
|
+
}
|
|
280200
|
+
function isServerUpdateReason(requestedBy) {
|
|
280201
|
+
return requestedBy !== void 0 && requestedBy.startsWith(SERVER_UPDATE_PREFIX);
|
|
280202
|
+
}
|
|
280203
|
+
function versionFromReason(requestedBy) {
|
|
280204
|
+
if (requestedBy === void 0)
|
|
280205
|
+
return void 0;
|
|
280206
|
+
return /@([^@\s]+)$/.exec(requestedBy)?.[1];
|
|
280207
|
+
}
|
|
280208
|
+
}
|
|
280209
|
+
});
|
|
280210
|
+
|
|
279492
280211
|
// ../../server/backend/dist/agent/agent-update-service.js
|
|
279493
280212
|
var require_agent_update_service = __commonJS({
|
|
279494
280213
|
"../../server/backend/dist/agent/agent-update-service.js"(exports) {
|
|
@@ -279546,6 +280265,7 @@ var require_agent_update_service = __commonJS({
|
|
|
279546
280265
|
var update_availability_emitter_js_1 = require_update_availability_emitter();
|
|
279547
280266
|
var update_availability_store_js_1 = require_update_availability_store();
|
|
279548
280267
|
var index_js_1 = require_server_root();
|
|
280268
|
+
var restart_marker_js_1 = require_restart_marker();
|
|
279549
280269
|
exports.AGENT_RUNTIME_ADDON_ID = "agent-runtime";
|
|
279550
280270
|
exports.AGENT_RESTART_GRACE_MS = 2e3;
|
|
279551
280271
|
function isRunningInContainer(existsSyncFn) {
|
|
@@ -279609,8 +280329,8 @@ var require_agent_update_service = __commonJS({
|
|
|
279609
280329
|
seedDir: "CAMSTACK_SEED_SERVER_DIR"
|
|
279610
280330
|
},
|
|
279611
280331
|
logger: options.logger,
|
|
279612
|
-
restartServer: (requestedBy) => {
|
|
279613
|
-
writeAgentRestartMarker(options.dataDir, requestedBy);
|
|
280332
|
+
restartServer: (requestedBy, update) => {
|
|
280333
|
+
writeAgentRestartMarker(options.dataDir, requestedBy, update);
|
|
279614
280334
|
restartAgent(requestedBy);
|
|
279615
280335
|
},
|
|
279616
280336
|
dataDir: options.dataDir,
|
|
@@ -279688,18 +280408,13 @@ var require_agent_update_service = __commonJS({
|
|
|
279688
280408
|
}
|
|
279689
280409
|
};
|
|
279690
280410
|
exports.AgentUpdateService = AgentUpdateService;
|
|
279691
|
-
function writeAgentRestartMarker(dataDir, requestedBy) {
|
|
279692
|
-
const toVersion = /@([^@\s]+)$/.exec(requestedBy)?.[1];
|
|
280411
|
+
function writeAgentRestartMarker(dataDir, requestedBy, update) {
|
|
279693
280412
|
try {
|
|
279694
|
-
(0, system_1.writePendingRestart)(dataDir, {
|
|
279695
|
-
kind: requestedBy.startsWith("server-update:") ? "framework-update" : "manual",
|
|
280413
|
+
(0, system_1.writePendingRestart)(dataDir, (0, restart_marker_js_1.buildRestartMarker)({
|
|
279696
280414
|
requestedAt: Date.now(),
|
|
279697
280415
|
requestedBy,
|
|
279698
|
-
...
|
|
279699
|
-
|
|
279700
|
-
...toVersion !== void 0 ? { toVersion } : {}
|
|
279701
|
-
} : {}
|
|
279702
|
-
});
|
|
280416
|
+
...update !== void 0 ? { update } : {}
|
|
280417
|
+
}));
|
|
279703
280418
|
} catch {
|
|
279704
280419
|
}
|
|
279705
280420
|
}
|
|
@@ -388570,6 +389285,7 @@ var require_addon_package_service = __commonJS({
|
|
|
388570
389285
|
var types_1 = require_dist9();
|
|
388571
389286
|
var update_availability_emitter_js_1 = require_update_availability_emitter();
|
|
388572
389287
|
var update_check_scheduler_js_1 = require_update_check_scheduler();
|
|
389288
|
+
var restart_marker_js_1 = require_restart_marker();
|
|
388573
389289
|
var package_dir_utils_js_1 = require_package_dir_utils();
|
|
388574
389290
|
var execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
388575
389291
|
exports.SYSTEM_PACKAGE = "@camstack/system";
|
|
@@ -389497,26 +390213,24 @@ var require_addon_package_service = __commonJS({
|
|
|
389497
390213
|
/**
|
|
389498
390214
|
* Gracefully restart the server process.
|
|
389499
390215
|
*
|
|
389500
|
-
* Writes
|
|
389501
|
-
*
|
|
389502
|
-
*
|
|
390216
|
+
* Writes the restart marker so the post-boot service can emit
|
|
390217
|
+
* `system.restart-completed` on the next boot (powers the admin-UI
|
|
390218
|
+
* "Server restarted" toast AND the operator's "Server aggiornato"
|
|
390219
|
+
* notification). `update` is passed by `RootUpdateService` for an
|
|
390220
|
+
* update-driven restart and is the ONLY source of the version being left
|
|
390221
|
+
* behind — see `updates/restart-marker.ts`. Emits `system.restarting`
|
|
389503
390222
|
* with the same payload so subscribers can show a reconnect overlay
|
|
389504
390223
|
* immediately. Then delegates to `scheduleSelfRestart` which picks
|
|
389505
390224
|
* the right exit path (Electron `app.relaunch` vs supervisor-driven
|
|
389506
390225
|
* `process.exit`).
|
|
389507
390226
|
*/
|
|
389508
|
-
restartServer(requestedBy) {
|
|
390227
|
+
restartServer(requestedBy, update) {
|
|
389509
390228
|
this.logger.info("Server restart requested -- initiating graceful shutdown");
|
|
389510
|
-
const
|
|
389511
|
-
const payload = {
|
|
389512
|
-
kind: isServerUpdateRequest(requestedBy) ? "framework-update" : "manual",
|
|
390229
|
+
const payload = (0, restart_marker_js_1.buildRestartMarker)({
|
|
389513
390230
|
requestedAt: Date.now(),
|
|
389514
390231
|
...requestedBy !== void 0 ? { requestedBy } : {},
|
|
389515
|
-
...
|
|
389516
|
-
|
|
389517
|
-
...toVersion !== void 0 ? { toVersion } : {}
|
|
389518
|
-
} : {}
|
|
389519
|
-
};
|
|
390232
|
+
...update !== void 0 ? { update } : {}
|
|
390233
|
+
});
|
|
389520
390234
|
try {
|
|
389521
390235
|
(0, system_1.writePendingRestart)(this.resolveDataDir(), payload);
|
|
389522
390236
|
} catch (err) {
|
|
@@ -390007,22 +390721,23 @@ var require_addon_package_service = __commonJS({
|
|
|
390007
390721
|
* re-announces them on the next good poll.
|
|
390008
390722
|
*/
|
|
390009
390723
|
publishAddonAvailability(updates, failures, nodeId) {
|
|
390724
|
+
const scope = nodeId ?? this.resolveNodeId();
|
|
390010
390725
|
const candidates = updates.map((update) => ({
|
|
390011
390726
|
target: "addon",
|
|
390012
390727
|
packageName: update.name,
|
|
390013
390728
|
currentVersion: update.currentVersion,
|
|
390014
390729
|
latestVersion: update.latestVersion,
|
|
390015
|
-
|
|
390730
|
+
nodeId: scope
|
|
390016
390731
|
}));
|
|
390017
390732
|
if (failures > 0) {
|
|
390018
390733
|
this.logger.warn("Addon update check incomplete \u2014 availability published as partial", {
|
|
390019
|
-
tags: { nodeId:
|
|
390734
|
+
tags: { nodeId: scope },
|
|
390020
390735
|
meta: { failures, published: candidates.length }
|
|
390021
390736
|
});
|
|
390022
390737
|
this.updateAvailability.publishCandidates(candidates);
|
|
390023
390738
|
return;
|
|
390024
390739
|
}
|
|
390025
|
-
this.updateAvailability.publishSnapshot("addon", candidates,
|
|
390740
|
+
this.updateAvailability.publishSnapshot("addon", candidates, scope);
|
|
390026
390741
|
}
|
|
390027
390742
|
/** Fetch npm search results for camstack addon packages (cached 5 min) */
|
|
390028
390743
|
async fetchSearchFromNpm() {
|
|
@@ -390172,15 +390887,6 @@ var require_addon_package_service = __commonJS({
|
|
|
390172
390887
|
}
|
|
390173
390888
|
};
|
|
390174
390889
|
exports.AddonPackageService = AddonPackageService;
|
|
390175
|
-
function isServerUpdateRequest(requestedBy) {
|
|
390176
|
-
return requestedBy !== void 0 && requestedBy.startsWith("server-update:");
|
|
390177
|
-
}
|
|
390178
|
-
function toVersionFromRestartReason(requestedBy) {
|
|
390179
|
-
if (requestedBy === void 0)
|
|
390180
|
-
return void 0;
|
|
390181
|
-
const match = /@([^@\s]+)$/.exec(requestedBy);
|
|
390182
|
-
return match?.[1];
|
|
390183
|
-
}
|
|
390184
390890
|
async function extractTgzStripped(tgz, destDir) {
|
|
390185
390891
|
fs.mkdirSync(destDir, { recursive: true });
|
|
390186
390892
|
const tmpFile = path.join(os.tmpdir(), `camstack-tgz-${(0, node_crypto_1.randomUUID)()}.tgz`);
|
|
@@ -401689,14 +402395,6 @@ var require_integration_visibility = __commonJS({
|
|
|
401689
402395
|
function createFilteredIntegrationRegistry(deps) {
|
|
401690
402396
|
const { raw, logger } = deps;
|
|
401691
402397
|
const isInstalled = (addonId) => deps.isAddonInstalled((0, types_1.bareAddonId)(addonId));
|
|
401692
|
-
let activeIntegrationIds = null;
|
|
401693
|
-
const ensureActiveIds = async () => {
|
|
401694
|
-
if (activeIntegrationIds)
|
|
401695
|
-
return activeIntegrationIds;
|
|
401696
|
-
const all = await raw.listIntegrations();
|
|
401697
|
-
activeIntegrationIds = new Set(all.filter((i) => isInstalled(i.addonId)).map((i) => i.id));
|
|
401698
|
-
return activeIntegrationIds;
|
|
401699
|
-
};
|
|
401700
402398
|
const logHidden = (integration, phase) => {
|
|
401701
402399
|
logger.warn("integration hidden \u2014 its addon is not installed, so this read answers null even though the row exists", {
|
|
401702
402400
|
tags: { integrationId: integration.id, addonId: integration.addonId },
|
|
@@ -401742,39 +402440,7 @@ var require_integration_visibility = __commonJS({
|
|
|
401742
402440
|
// Integration settings: passthrough (already gated by getIntegration)
|
|
401743
402441
|
getIntegrationSettings: (id) => raw.getIntegrationSettings(id),
|
|
401744
402442
|
setIntegrationSetting: (id, key, value) => raw.setIntegrationSetting(id, key, value),
|
|
401745
|
-
setIntegrationSettings: (id, settings) => raw.setIntegrationSettings(id, settings)
|
|
401746
|
-
// Devices: filter out devices belonging to hidden integrations
|
|
401747
|
-
createDevice: (input) => raw.createDevice(input),
|
|
401748
|
-
getDevice: async (id) => {
|
|
401749
|
-
const d = await raw.getDevice(id);
|
|
401750
|
-
if (!d)
|
|
401751
|
-
return null;
|
|
401752
|
-
const ids = await ensureActiveIds();
|
|
401753
|
-
return ids.has(d.integrationId) ? d : null;
|
|
401754
|
-
},
|
|
401755
|
-
getDeviceByStableId: async (stableId) => {
|
|
401756
|
-
const d = await raw.getDeviceByStableId(stableId);
|
|
401757
|
-
if (!d)
|
|
401758
|
-
return null;
|
|
401759
|
-
const ids = await ensureActiveIds();
|
|
401760
|
-
return ids.has(d.integrationId) ? d : null;
|
|
401761
|
-
},
|
|
401762
|
-
listDevices: async (integrationId) => {
|
|
401763
|
-
const devices = await raw.listDevices(integrationId);
|
|
401764
|
-
const ids = await ensureActiveIds();
|
|
401765
|
-
return devices.filter((d) => ids.has(d.integrationId));
|
|
401766
|
-
},
|
|
401767
|
-
listCameras: async () => {
|
|
401768
|
-
const cameras = await raw.listCameras();
|
|
401769
|
-
const ids = await ensureActiveIds();
|
|
401770
|
-
return cameras.filter((d) => ids.has(d.integrationId));
|
|
401771
|
-
},
|
|
401772
|
-
updateDevice: (id, updates) => raw.updateDevice(id, updates),
|
|
401773
|
-
deleteDevice: (id) => raw.deleteDevice(id),
|
|
401774
|
-
// Device settings: passthrough
|
|
401775
|
-
getDeviceSettings: (id) => raw.getDeviceSettings(id),
|
|
401776
|
-
setDeviceSetting: (id, key, value) => raw.setDeviceSetting(id, key, value),
|
|
401777
|
-
setDeviceSettings: (id, settings) => raw.setDeviceSettings(id, settings)
|
|
402443
|
+
setIntegrationSettings: (id, settings) => raw.setIntegrationSettings(id, settings)
|
|
401778
402444
|
};
|
|
401779
402445
|
}
|
|
401780
402446
|
}
|
|
@@ -406355,6 +407021,7 @@ var require_moleculer_service = __commonJS({
|
|
|
406355
407021
|
"use strict";
|
|
406356
407022
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
406357
407023
|
exports.MoleculerService = void 0;
|
|
407024
|
+
exports.childOwnerToken = childOwnerToken;
|
|
406358
407025
|
exports.buildChildUdsManifest = buildChildUdsManifest;
|
|
406359
407026
|
var node_crypto_1 = __require("crypto");
|
|
406360
407027
|
var system_1 = require_dist3();
|
|
@@ -406381,6 +407048,21 @@ var require_moleculer_service = __commonJS({
|
|
|
406381
407048
|
* `$hub.registerNode`. Populated by `onRegisterNode` in `hubDeps`.
|
|
406382
407049
|
*/
|
|
406383
407050
|
nodeRegistry = new system_1.HubNodeRegistry();
|
|
407051
|
+
/**
|
|
407052
|
+
* nodeId → the owner token of the INCARNATION whose manifest currently backs
|
|
407053
|
+
* that node's capability registrations.
|
|
407054
|
+
*
|
|
407055
|
+
* A node id is not unique over time: `resolveRunnerId` is deterministic, so a
|
|
407056
|
+
* runner replaced by an addon update reconnects as the same `hub/<runner>`
|
|
407057
|
+
* node. Without an incarnation the predecessor's teardown and the successor's
|
|
407058
|
+
* registration are the same `(cap, addonId)` key, and the teardown wins by
|
|
407059
|
+
* arriving last — `recording`, `storage-evictable` and `recording-export`
|
|
407060
|
+
* were unregistered from a healthy process for 7 hours on 2026-08-20.
|
|
407061
|
+
* Populated only for transports that can observe an incarnation (UDS
|
|
407062
|
+
* children); remote nodes stay ownerless and behave exactly as before.
|
|
407063
|
+
* See `docs/decisions/adr-0188-an-unregister-carries-proof-of-ownership.md`.
|
|
407064
|
+
*/
|
|
407065
|
+
nodeOwners = /* @__PURE__ */ new Map();
|
|
406384
407066
|
/**
|
|
406385
407067
|
* Fixed-period agent-readiness snapshot sweep (D8 reconcile) — repairs
|
|
406386
407068
|
* agent-origin readiness deltas lost while the agent stayed connected.
|
|
@@ -406702,14 +407384,18 @@ var require_moleculer_service = __commonJS({
|
|
|
406702
407384
|
const hubNodeId = this.brokerSafe.nodeID;
|
|
406703
407385
|
const childNodeId = `${hubNodeId}/${child.childId}`;
|
|
406704
407386
|
const params = buildChildUdsManifest(childNodeId, child.childId, child.caps);
|
|
406705
|
-
this.onRegisterNode(params);
|
|
406706
|
-
logger.info("UDS child registered \u2014 manifest applied", {
|
|
407387
|
+
this.onRegisterNode(params, childOwnerToken(childNodeId, child.incarnation));
|
|
407388
|
+
logger.info("UDS child registered \u2014 manifest applied", {
|
|
407389
|
+
meta: { nodeId: childNodeId, incarnation: child.incarnation }
|
|
407390
|
+
});
|
|
406707
407391
|
});
|
|
406708
|
-
registry.onChildGone((childId) => {
|
|
407392
|
+
registry.onChildGone((childId, incarnation) => {
|
|
406709
407393
|
const hubNodeId = this.brokerSafe.nodeID;
|
|
406710
407394
|
const childNodeId = `${hubNodeId}/${childId}`;
|
|
406711
|
-
logger.info("UDS child gone \u2014 removing from registry", {
|
|
406712
|
-
|
|
407395
|
+
logger.info("UDS child gone \u2014 removing from registry", {
|
|
407396
|
+
meta: { childId, incarnation }
|
|
407397
|
+
});
|
|
407398
|
+
this.removeNodeFromRegistry(childNodeId, childOwnerToken(childNodeId, incarnation));
|
|
406713
407399
|
});
|
|
406714
407400
|
registry.onChildLog((childId, entry) => {
|
|
406715
407401
|
this.logging.writeFromWorker((0, system_1.udsChildLogToWorkerEntry)(childId, entry));
|
|
@@ -406893,10 +407579,15 @@ var require_moleculer_service = __commonJS({
|
|
|
406893
407579
|
* CapabilityRegistry update is a diff (atomic replace) rather than an
|
|
406894
407580
|
* unconditional re-register — see `applyNodeManifest` for the rationale.
|
|
406895
407581
|
*/
|
|
406896
|
-
onRegisterNode(params) {
|
|
407582
|
+
onRegisterNode(params, owner) {
|
|
406897
407583
|
const previousManifest = this.nodeRegistry.getNodeManifest(params.nodeId);
|
|
407584
|
+
const previousOwner = this.nodeOwners.get(params.nodeId);
|
|
406898
407585
|
this.nodeRegistry.registerNode(params);
|
|
406899
|
-
|
|
407586
|
+
if (owner === void 0)
|
|
407587
|
+
this.nodeOwners.delete(params.nodeId);
|
|
407588
|
+
else
|
|
407589
|
+
this.nodeOwners.set(params.nodeId, owner);
|
|
407590
|
+
this.applyNodeManifest(params, previousManifest, owner, previousOwner);
|
|
406900
407591
|
const { nodeId } = params;
|
|
406901
407592
|
if (nodeId !== "hub" && !nodeId.includes("/") && this.onAgentRegisteredCb) {
|
|
406902
407593
|
this.onAgentRegisteredCb(nodeId);
|
|
@@ -406924,7 +407615,7 @@ var require_moleculer_service = __commonJS({
|
|
|
406924
407615
|
* already; this method handles only `params.addons` (system caps).
|
|
406925
407616
|
* Native-cap wiring into device-manager is done in a later task.
|
|
406926
407617
|
*/
|
|
406927
|
-
applyNodeManifest(params, previousManifest) {
|
|
407618
|
+
applyNodeManifest(params, previousManifest, owner, previousOwner) {
|
|
406928
407619
|
const { nodeId, addons } = params;
|
|
406929
407620
|
const hubNodeId = this.brokerSafe.nodeID;
|
|
406930
407621
|
const isLocalChild = nodeId.startsWith(hubNodeId + "/");
|
|
@@ -406955,9 +407646,16 @@ var require_moleculer_service = __commonJS({
|
|
|
406955
407646
|
for (const [key, { addonId, capName }] of previous) {
|
|
406956
407647
|
if (desired.has(key))
|
|
406957
407648
|
continue;
|
|
406958
|
-
registry.unregisterProvider(capName, registryKeyFor(addonId));
|
|
407649
|
+
registry.unregisterProvider(capName, registryKeyFor(addonId), previousOwner);
|
|
406959
407650
|
this.nodeCallFns.delete(`${nodeId}::${capName}`);
|
|
406960
407651
|
}
|
|
407652
|
+
if (owner !== void 0 && previousOwner !== void 0 && owner !== previousOwner) {
|
|
407653
|
+
for (const [key, { addonId, capName }] of desired) {
|
|
407654
|
+
if (!previous.has(key))
|
|
407655
|
+
continue;
|
|
407656
|
+
registry.reassignProviderOwner(capName, registryKeyFor(addonId), owner);
|
|
407657
|
+
}
|
|
407658
|
+
}
|
|
406961
407659
|
for (const [key, { addonId, capName, capDef }] of desired) {
|
|
406962
407660
|
if (previous.has(key))
|
|
406963
407661
|
continue;
|
|
@@ -406982,7 +407680,7 @@ var require_moleculer_service = __commonJS({
|
|
|
406982
407680
|
for (const methodName of Object.keys((0, system_1.expandCapMethods)(capDef))) {
|
|
406983
407681
|
proxy[methodName] = (methodParams) => callFn(methodName, methodParams);
|
|
406984
407682
|
}
|
|
406985
|
-
registry.registerProvider(capName, registryKey, proxy);
|
|
407683
|
+
registry.registerProvider(capName, registryKey, proxy, owner);
|
|
406986
407684
|
if (capDef.mode === "singleton" && isLocalChild) {
|
|
406987
407685
|
const activeKey = registry.getSingletonAddonId(capName);
|
|
406988
407686
|
if (activeKey === null || activeKey.includes("@")) {
|
|
@@ -407015,7 +407713,14 @@ var require_moleculer_service = __commonJS({
|
|
|
407015
407713
|
* Unregisters every cap the node's last manifest declared and emits
|
|
407016
407714
|
* synthetic readiness-down events for each.
|
|
407017
407715
|
*/
|
|
407018
|
-
removeNodeFromRegistry(nodeId) {
|
|
407716
|
+
removeNodeFromRegistry(nodeId, owner) {
|
|
407717
|
+
const currentOwner = this.nodeOwners.get(nodeId);
|
|
407718
|
+
if (currentOwner !== void 0 && currentOwner !== owner) {
|
|
407719
|
+
this.logger.info("Node teardown ignored \u2014 node id reclaimed by a newer incarnation", {
|
|
407720
|
+
meta: { nodeId, currentOwner, presentedOwner: owner ?? null }
|
|
407721
|
+
});
|
|
407722
|
+
return;
|
|
407723
|
+
}
|
|
407019
407724
|
const manifest = this.nodeRegistry.getNodeManifest(nodeId);
|
|
407020
407725
|
if (!manifest)
|
|
407021
407726
|
return;
|
|
@@ -407029,7 +407734,7 @@ var require_moleculer_service = __commonJS({
|
|
|
407029
407734
|
const registryKey = isLocalChild ? addonId : `${addonId}@${nodeId}`;
|
|
407030
407735
|
if (registry) {
|
|
407031
407736
|
for (const capName of capabilities) {
|
|
407032
|
-
registry.unregisterProvider(capName, registryKey);
|
|
407737
|
+
registry.unregisterProvider(capName, registryKey, owner);
|
|
407033
407738
|
}
|
|
407034
407739
|
}
|
|
407035
407740
|
for (const capName of capabilities) {
|
|
@@ -407050,6 +407755,7 @@ var require_moleculer_service = __commonJS({
|
|
|
407050
407755
|
}
|
|
407051
407756
|
}
|
|
407052
407757
|
}
|
|
407758
|
+
this.nodeOwners.delete(nodeId);
|
|
407053
407759
|
this.nodeRegistry.removeNode(nodeId);
|
|
407054
407760
|
}
|
|
407055
407761
|
findCallFn(nodeId, capabilityName) {
|
|
@@ -407213,6 +407919,9 @@ var require_moleculer_service = __commonJS({
|
|
|
407213
407919
|
}
|
|
407214
407920
|
};
|
|
407215
407921
|
exports.MoleculerService = MoleculerService;
|
|
407922
|
+
function childOwnerToken(childNodeId, incarnation) {
|
|
407923
|
+
return `${childNodeId}#${incarnation}`;
|
|
407924
|
+
}
|
|
407216
407925
|
function buildChildUdsManifest(nodeId, childId, caps) {
|
|
407217
407926
|
const capsByAddon = /* @__PURE__ */ new Map();
|
|
407218
407927
|
for (const cap of caps) {
|
|
@@ -408573,7 +409282,7 @@ var require_manual_boot = __commonJS({
|
|
|
408573
409282
|
const replEngineService = new repl_engine_service_1.ReplEngineService(addonRegistryService, eventBusService, loggingService);
|
|
408574
409283
|
const serverUpdateService = new server_update_service_1.ServerUpdateService({
|
|
408575
409284
|
logger: loggingService.createLogger("ServerUpdate"),
|
|
408576
|
-
restartServer: (requestedBy) => addonPackageService.restartServer(requestedBy),
|
|
409285
|
+
restartServer: (requestedBy, update) => addonPackageService.restartServer(requestedBy, update),
|
|
408577
409286
|
eventBus: eventBusService,
|
|
408578
409287
|
updateAvailabilityStore: new update_availability_store_js_1.FileUpdateAvailabilityStore(availabilityDataDir, "server-update", loggingService.createLogger("UpdateAvailability"))
|
|
408579
409288
|
});
|