camstack 1.2.65 → 1.2.67
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.
|
@@ -23633,9 +23633,9 @@ var require_zod = __commonJS({
|
|
|
23633
23633
|
}
|
|
23634
23634
|
});
|
|
23635
23635
|
|
|
23636
|
-
// ../system/dist/dist-
|
|
23637
|
-
var
|
|
23638
|
-
"../system/dist/dist-
|
|
23636
|
+
// ../system/dist/dist-Keu5TDO7.js
|
|
23637
|
+
var require_dist_Keu5TDO7 = __commonJS({
|
|
23638
|
+
"../system/dist/dist-Keu5TDO7.js"(exports) {
|
|
23639
23639
|
"use strict";
|
|
23640
23640
|
var zod = require_zod();
|
|
23641
23641
|
var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
|
|
@@ -26259,6 +26259,21 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
26259
26259
|
bytesMoved: zod.z.number().int(),
|
|
26260
26260
|
/** Total files discovered up front; null while (or when) unknown. */
|
|
26261
26261
|
filesTotal: zod.z.number().int().nullable(),
|
|
26262
|
+
/**
|
|
26263
|
+
* Rows this run CORRECTED while moving them — a durable mutation the move
|
|
26264
|
+
* made that nobody asked for, so it is reported where the operator reads the
|
|
26265
|
+
* job rather than only in a log line.
|
|
26266
|
+
*
|
|
26267
|
+
* A footage segment records its byte count in its own NAME, and the durable
|
|
26268
|
+
* hour row derives its aggregates from those names. A file that does not
|
|
26269
|
+
* match its name therefore makes the ledger's sums — and with them quota and
|
|
26270
|
+
* pressure eviction — wrong by the difference, and only a rename can fix it.
|
|
26271
|
+
* On 2026-08-30 one such row also stalled a 110 749-file drain permanently.
|
|
26272
|
+
*
|
|
26273
|
+
* Absent on lanes where the question has no meaning: a media blob's size is
|
|
26274
|
+
* in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
|
|
26275
|
+
*/
|
|
26276
|
+
rowsReconciled: zod.z.number().int().nonnegative().optional(),
|
|
26262
26277
|
startedAt: zod.z.number(),
|
|
26263
26278
|
finishedAt: zod.z.number().nullable(),
|
|
26264
26279
|
error: zod.z.string().nullable()
|
|
@@ -26301,11 +26316,18 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
26301
26316
|
/** Omitted = `move`, the pre-existing behaviour. */
|
|
26302
26317
|
mode: MediaRelocateModeSchema.optional()
|
|
26303
26318
|
});
|
|
26304
|
-
var
|
|
26305
|
-
|
|
26306
|
-
|
|
26307
|
-
total: zod.z.number().int().nonnegative()
|
|
26319
|
+
var UnstampedRowsSchema = zod.z.object({
|
|
26320
|
+
present: zod.z.boolean(),
|
|
26321
|
+
rows: zod.z.number().int().nonnegative().nullable()
|
|
26308
26322
|
});
|
|
26323
|
+
var UnstampedEventMediaCountSchema = zod.z.object({
|
|
26324
|
+
media: UnstampedRowsSchema,
|
|
26325
|
+
retrainFrames: UnstampedRowsSchema,
|
|
26326
|
+
/** True when EITHER collection holds one. The refusal reads this. */
|
|
26327
|
+
anyPresent: zod.z.boolean(),
|
|
26328
|
+
/** Sum across both, or `null` when either lane could not be counted. */
|
|
26329
|
+
total: zod.z.number().int().nonnegative().nullable()
|
|
26330
|
+
}).nullable();
|
|
26309
26331
|
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: zod.z.string().min(1) });
|
|
26310
26332
|
var StorageMigrationClassSchema = zod.z.enum([
|
|
26311
26333
|
"recordings",
|
|
@@ -26352,6 +26374,10 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
26352
26374
|
/** The archive census — the **M** of "N of M" (D295). `null` = unknowable. */
|
|
26353
26375
|
filesTotal: zod.z.number().int().nonnegative().nullable(),
|
|
26354
26376
|
bytesMoved: zod.z.number().int().nonnegative(),
|
|
26377
|
+
/** Rows the mover corrected while moving them — see `RelocateJob`. Absent on
|
|
26378
|
+
* a lane that cannot reconcile. A migration that silently rewrote durable
|
|
26379
|
+
* rows would be the same failure as one that silently skipped them. */
|
|
26380
|
+
rowsReconciled: zod.z.number().int().nonnegative().optional(),
|
|
26355
26381
|
/** The MOVER's start, not the migration's: a drain restarted after an addon
|
|
26356
26382
|
* crash gets a new mover, and a rate computed from the migration's start
|
|
26357
26383
|
* would silently average in the time nothing was running. */
|
|
@@ -31507,6 +31533,15 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
31507
31533
|
* calls are sync. Bindings change rarely (only on wrapper toggle or
|
|
31508
31534
|
* device add/remove) — clients invalidate via the
|
|
31509
31535
|
* `capability.binding-changed` event.
|
|
31536
|
+
*
|
|
31537
|
+
* "A single round-trip" describes the CLIENT's side and used not to
|
|
31538
|
+
* describe the server's: until 2026-08-30 the resolver read the persisted
|
|
31539
|
+
* wrapper activations once per device, so answering this cost one
|
|
31540
|
+
* settings-door RPC per device — 1 020 on the live 1 019-device hub, and
|
|
31541
|
+
* it did not return in 240 s against `SystemMirror.init`'s 15 s budget.
|
|
31542
|
+
* The server side is now two reads for the whole fleet. Anything PERIODIC
|
|
31543
|
+
* still belongs on `getBindings` / `getBindingsBatch` (D12); this remains
|
|
31544
|
+
* a warm seed.
|
|
31510
31545
|
*/
|
|
31511
31546
|
getAllBindings: method(zod.z.object({}), zod.z.array(DeviceBindingsForDeviceSchema)),
|
|
31512
31547
|
/**
|
|
@@ -31905,6 +31940,80 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
31905
31940
|
getInfo: method(zod.z.void(), EmbeddingInfoSchema, { auth: "admin" })
|
|
31906
31941
|
}
|
|
31907
31942
|
};
|
|
31943
|
+
var FailureReasonCountSchema = zod.z.object({
|
|
31944
|
+
/**
|
|
31945
|
+
* Why the attempt did not land, in the contributor's own vocabulary —
|
|
31946
|
+
* `worker-lease-gone`, `queue-overflow`, `timeout`, `empty-read`. The same
|
|
31947
|
+
* strings that already appear in this repo's logs and, where one exists, the
|
|
31948
|
+
* same string the per-track `previewMissReason` records (D276): a second
|
|
31949
|
+
* vocabulary for the same loss would make the row and the counter
|
|
31950
|
+
* un-joinable.
|
|
31951
|
+
*/
|
|
31952
|
+
reason: zod.z.string(),
|
|
31953
|
+
count: zod.z.number().int().nonnegative()
|
|
31954
|
+
});
|
|
31955
|
+
var FailureContributionSchema = zod.z.object({
|
|
31956
|
+
/**
|
|
31957
|
+
* The failing path — `enrichment-crop`, `inference`, `plate-ocr`,
|
|
31958
|
+
* `person-over-vehicle`. Free text, for the reason `load-contribution` keeps
|
|
31959
|
+
* `unit` free: the families are owned by different addons and a shared enum
|
|
31960
|
+
* is a central list that rots invisibly.
|
|
31961
|
+
*/
|
|
31962
|
+
family: zod.z.string(),
|
|
31963
|
+
/**
|
|
31964
|
+
* The NUMERIC device id — the same value every log line carries as
|
|
31965
|
+
* `tags.deviceId`. Never nullable and never absent: a contributor that
|
|
31966
|
+
* cannot name the camera must not emit the entry, because a fleet total
|
|
31967
|
+
* cannot answer the only question anybody asks of this surface.
|
|
31968
|
+
*/
|
|
31969
|
+
deviceId: zod.z.number().int().positive(),
|
|
31970
|
+
/**
|
|
31971
|
+
* A second dimension inside the family: the model / step id for an inference
|
|
31972
|
+
* timeout, so "which camera AND which model" is one read. Absent when the
|
|
31973
|
+
* family has a single variant.
|
|
31974
|
+
*/
|
|
31975
|
+
variant: zod.z.string().optional(),
|
|
31976
|
+
/**
|
|
31977
|
+
* Epoch ms this counter started — the INCARNATION MARKER. A consumer
|
|
31978
|
+
* differencing two reads must drop the interval when it changes, because the
|
|
31979
|
+
* counter restarted from zero in a respawned runner. Same discipline as
|
|
31980
|
+
* `LoadContribution.startedAtMs`.
|
|
31981
|
+
*/
|
|
31982
|
+
sinceMs: zod.z.number(),
|
|
31983
|
+
/** Epoch ms it was read. `atMs - sinceMs` is the interval this covers. */
|
|
31984
|
+
atMs: zod.z.number(),
|
|
31985
|
+
/**
|
|
31986
|
+
* THE DENOMINATOR — every attempt on this path for this camera in the
|
|
31987
|
+
* window. A failure count published without it is the mistake this schema
|
|
31988
|
+
* exists to make impossible.
|
|
31989
|
+
*/
|
|
31990
|
+
attempts: zod.z.number().int().nonnegative(),
|
|
31991
|
+
/** Attempts that landed. `attempts - succeeded` is the loss. */
|
|
31992
|
+
succeeded: zod.z.number().int().nonnegative(),
|
|
31993
|
+
/** The loss, partitioned. Sums to `attempts - succeeded`. */
|
|
31994
|
+
reasons: zod.z.array(FailureReasonCountSchema).readonly()
|
|
31995
|
+
});
|
|
31996
|
+
var failureContributionCapability = {
|
|
31997
|
+
name: "failure-contribution",
|
|
31998
|
+
scope: "system",
|
|
31999
|
+
mode: "collection",
|
|
32000
|
+
internal: true,
|
|
32001
|
+
methods: {
|
|
32002
|
+
/**
|
|
32003
|
+
* This addon's per-camera failure counters, read live from bounded in-RAM
|
|
32004
|
+
* state it already keeps. Inert: no persistence, no sampling, no timer.
|
|
32005
|
+
*
|
|
32006
|
+
* READING NEVER RESETS. The counters are CUMULATIVE since `sinceMs`, and a
|
|
32007
|
+
* consumer that wants a rate differences two reads. A draining read would
|
|
32008
|
+
* make two operators with the page open each destroy half of the other's
|
|
32009
|
+
* numbers, and `load-contribution` already settled the same question the
|
|
32010
|
+
* same way for `cpuSeconds`.
|
|
32011
|
+
*/
|
|
32012
|
+
list: method(zod.z.void(), zod.z.array(FailureContributionSchema).readonly())
|
|
32013
|
+
},
|
|
32014
|
+
/** In-process only — enumerated through `addons.listCapabilityProviders`. */
|
|
32015
|
+
mount: { kind: "skip" }
|
|
32016
|
+
};
|
|
31908
32017
|
var DirEntrySchema = zod.z.object({
|
|
31909
32018
|
name: zod.z.string(),
|
|
31910
32019
|
path: zod.z.string()
|
|
@@ -32442,145 +32551,6 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
32442
32551
|
})
|
|
32443
32552
|
}
|
|
32444
32553
|
};
|
|
32445
|
-
var LogChannelApplyResultSchema = zod.z.object({
|
|
32446
|
-
/** How many declared channels are armed in this process after the call. */
|
|
32447
|
-
armed: zod.z.number().int().min(0),
|
|
32448
|
-
/**
|
|
32449
|
-
* Names the document armed that this process does not declare. Reported
|
|
32450
|
-
* rather than swallowed: a name here is either a typo or an addon that has
|
|
32451
|
-
* not booted, and both deserve a line instead of silence.
|
|
32452
|
-
*/
|
|
32453
|
-
unknown: zod.z.array(zod.z.string()).readonly()
|
|
32454
|
-
});
|
|
32455
|
-
var logChannelsCapability = {
|
|
32456
|
-
name: "log-channels",
|
|
32457
|
-
scope: "system",
|
|
32458
|
-
mode: "collection",
|
|
32459
|
-
internal: true,
|
|
32460
|
-
methods: {
|
|
32461
|
-
/** The channels this addon declares. Inert: no value, no state. */
|
|
32462
|
-
list: method(zod.z.void(), zod.z.array(LogChannelDescriptorSchema).readonly()),
|
|
32463
|
-
/**
|
|
32464
|
-
* Refresh this process's mirror from the document's FULL set of armed
|
|
32465
|
-
* windows.
|
|
32466
|
-
*
|
|
32467
|
-
* Full and not incremental on purpose: the document is the authority, so a
|
|
32468
|
-
* channel it does not name is disarmed here. An incremental apply would
|
|
32469
|
-
* let a disarm get lost in transit and leave a channel running that
|
|
32470
|
-
* nobody can see is running.
|
|
32471
|
-
*/
|
|
32472
|
-
apply: method(zod.z.object({ windows: zod.z.array(LogChannelWindowSchema).readonly() }), LogChannelApplyResultSchema, { kind: "mutation" })
|
|
32473
|
-
},
|
|
32474
|
-
/** In-process only — enumerated through `addons.listCapabilityProviders`. */
|
|
32475
|
-
mount: { kind: "skip" }
|
|
32476
|
-
};
|
|
32477
|
-
var LogLevelSchema = zod.z.enum([
|
|
32478
|
-
"debug",
|
|
32479
|
-
"info",
|
|
32480
|
-
"warn",
|
|
32481
|
-
"error"
|
|
32482
|
-
]);
|
|
32483
|
-
var LogEntrySchema = zod.z.object({
|
|
32484
|
-
timestamp: zod.z.date(),
|
|
32485
|
-
level: LogLevelSchema,
|
|
32486
|
-
scope: zod.z.array(zod.z.string()),
|
|
32487
|
-
message: zod.z.string(),
|
|
32488
|
-
meta: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
|
|
32489
|
-
tags: zod.z.record(zod.z.string(), zod.z.string()).optional()
|
|
32490
|
-
});
|
|
32491
|
-
var logDestinationCapability = {
|
|
32492
|
-
name: "log-destination",
|
|
32493
|
-
scope: "system",
|
|
32494
|
-
mode: "collection",
|
|
32495
|
-
internal: true,
|
|
32496
|
-
methods: {
|
|
32497
|
-
write: method(LogEntrySchema, zod.z.void(), { kind: "mutation" }),
|
|
32498
|
-
query: method(zod.z.object({
|
|
32499
|
-
scope: zod.z.array(zod.z.string()).optional(),
|
|
32500
|
-
level: LogLevelSchema.optional(),
|
|
32501
|
-
since: zod.z.date().optional(),
|
|
32502
|
-
until: zod.z.date().optional(),
|
|
32503
|
-
limit: zod.z.number().optional(),
|
|
32504
|
-
tags: zod.z.record(zod.z.string(), zod.z.string()).optional()
|
|
32505
|
-
}), zod.z.array(LogEntrySchema).readonly())
|
|
32506
|
-
},
|
|
32507
|
-
/** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
|
|
32508
|
-
mount: { kind: "skip" }
|
|
32509
|
-
};
|
|
32510
|
-
var FailureReasonCountSchema = zod.z.object({
|
|
32511
|
-
/**
|
|
32512
|
-
* Why the attempt did not land, in the contributor's own vocabulary —
|
|
32513
|
-
* `worker-lease-gone`, `queue-overflow`, `timeout`, `empty-read`. The same
|
|
32514
|
-
* strings that already appear in this repo's logs and, where one exists, the
|
|
32515
|
-
* same string the per-track `previewMissReason` records (D276): a second
|
|
32516
|
-
* vocabulary for the same loss would make the row and the counter
|
|
32517
|
-
* un-joinable.
|
|
32518
|
-
*/
|
|
32519
|
-
reason: zod.z.string(),
|
|
32520
|
-
count: zod.z.number().int().nonnegative()
|
|
32521
|
-
});
|
|
32522
|
-
var FailureContributionSchema = zod.z.object({
|
|
32523
|
-
/**
|
|
32524
|
-
* The failing path — `enrichment-crop`, `inference`, `plate-ocr`,
|
|
32525
|
-
* `person-over-vehicle`. Free text, for the reason `load-contribution` keeps
|
|
32526
|
-
* `unit` free: the families are owned by different addons and a shared enum
|
|
32527
|
-
* is a central list that rots invisibly.
|
|
32528
|
-
*/
|
|
32529
|
-
family: zod.z.string(),
|
|
32530
|
-
/**
|
|
32531
|
-
* The NUMERIC device id — the same value every log line carries as
|
|
32532
|
-
* `tags.deviceId`. Never nullable and never absent: a contributor that
|
|
32533
|
-
* cannot name the camera must not emit the entry, because a fleet total
|
|
32534
|
-
* cannot answer the only question anybody asks of this surface.
|
|
32535
|
-
*/
|
|
32536
|
-
deviceId: zod.z.number().int().positive(),
|
|
32537
|
-
/**
|
|
32538
|
-
* A second dimension inside the family: the model / step id for an inference
|
|
32539
|
-
* timeout, so "which camera AND which model" is one read. Absent when the
|
|
32540
|
-
* family has a single variant.
|
|
32541
|
-
*/
|
|
32542
|
-
variant: zod.z.string().optional(),
|
|
32543
|
-
/**
|
|
32544
|
-
* Epoch ms this counter started — the INCARNATION MARKER. A consumer
|
|
32545
|
-
* differencing two reads must drop the interval when it changes, because the
|
|
32546
|
-
* counter restarted from zero in a respawned runner. Same discipline as
|
|
32547
|
-
* `LoadContribution.startedAtMs`.
|
|
32548
|
-
*/
|
|
32549
|
-
sinceMs: zod.z.number(),
|
|
32550
|
-
/** Epoch ms it was read. `atMs - sinceMs` is the interval this covers. */
|
|
32551
|
-
atMs: zod.z.number(),
|
|
32552
|
-
/**
|
|
32553
|
-
* THE DENOMINATOR — every attempt on this path for this camera in the
|
|
32554
|
-
* window. A failure count published without it is the mistake this schema
|
|
32555
|
-
* exists to make impossible.
|
|
32556
|
-
*/
|
|
32557
|
-
attempts: zod.z.number().int().nonnegative(),
|
|
32558
|
-
/** Attempts that landed. `attempts - succeeded` is the loss. */
|
|
32559
|
-
succeeded: zod.z.number().int().nonnegative(),
|
|
32560
|
-
/** The loss, partitioned. Sums to `attempts - succeeded`. */
|
|
32561
|
-
reasons: zod.z.array(FailureReasonCountSchema).readonly()
|
|
32562
|
-
});
|
|
32563
|
-
var failureContributionCapability = {
|
|
32564
|
-
name: "failure-contribution",
|
|
32565
|
-
scope: "system",
|
|
32566
|
-
mode: "collection",
|
|
32567
|
-
internal: true,
|
|
32568
|
-
methods: {
|
|
32569
|
-
/**
|
|
32570
|
-
* This addon's per-camera failure counters, read live from bounded in-RAM
|
|
32571
|
-
* state it already keeps. Inert: no persistence, no sampling, no timer.
|
|
32572
|
-
*
|
|
32573
|
-
* READING NEVER RESETS. The counters are CUMULATIVE since `sinceMs`, and a
|
|
32574
|
-
* consumer that wants a rate differences two reads. A draining read would
|
|
32575
|
-
* make two operators with the page open each destroy half of the other's
|
|
32576
|
-
* numbers, and `load-contribution` already settled the same question the
|
|
32577
|
-
* same way for `cpuSeconds`.
|
|
32578
|
-
*/
|
|
32579
|
-
list: method(zod.z.void(), zod.z.array(FailureContributionSchema).readonly())
|
|
32580
|
-
},
|
|
32581
|
-
/** In-process only — enumerated through `addons.listCapabilityProviders`. */
|
|
32582
|
-
mount: { kind: "skip" }
|
|
32583
|
-
};
|
|
32584
32554
|
var LoadContributionSchema = zod.z.object({
|
|
32585
32555
|
role: zod.z.enum([
|
|
32586
32556
|
"decode",
|
|
@@ -32658,6 +32628,71 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
32658
32628
|
/** In-process only — enumerated through `addons.listCapabilityProviders`. */
|
|
32659
32629
|
mount: { kind: "skip" }
|
|
32660
32630
|
};
|
|
32631
|
+
var LogChannelApplyResultSchema = zod.z.object({
|
|
32632
|
+
/** How many declared channels are armed in this process after the call. */
|
|
32633
|
+
armed: zod.z.number().int().min(0),
|
|
32634
|
+
/**
|
|
32635
|
+
* Names the document armed that this process does not declare. Reported
|
|
32636
|
+
* rather than swallowed: a name here is either a typo or an addon that has
|
|
32637
|
+
* not booted, and both deserve a line instead of silence.
|
|
32638
|
+
*/
|
|
32639
|
+
unknown: zod.z.array(zod.z.string()).readonly()
|
|
32640
|
+
});
|
|
32641
|
+
var logChannelsCapability = {
|
|
32642
|
+
name: "log-channels",
|
|
32643
|
+
scope: "system",
|
|
32644
|
+
mode: "collection",
|
|
32645
|
+
internal: true,
|
|
32646
|
+
methods: {
|
|
32647
|
+
/** The channels this addon declares. Inert: no value, no state. */
|
|
32648
|
+
list: method(zod.z.void(), zod.z.array(LogChannelDescriptorSchema).readonly()),
|
|
32649
|
+
/**
|
|
32650
|
+
* Refresh this process's mirror from the document's FULL set of armed
|
|
32651
|
+
* windows.
|
|
32652
|
+
*
|
|
32653
|
+
* Full and not incremental on purpose: the document is the authority, so a
|
|
32654
|
+
* channel it does not name is disarmed here. An incremental apply would
|
|
32655
|
+
* let a disarm get lost in transit and leave a channel running that
|
|
32656
|
+
* nobody can see is running.
|
|
32657
|
+
*/
|
|
32658
|
+
apply: method(zod.z.object({ windows: zod.z.array(LogChannelWindowSchema).readonly() }), LogChannelApplyResultSchema, { kind: "mutation" })
|
|
32659
|
+
},
|
|
32660
|
+
/** In-process only — enumerated through `addons.listCapabilityProviders`. */
|
|
32661
|
+
mount: { kind: "skip" }
|
|
32662
|
+
};
|
|
32663
|
+
var LogLevelSchema = zod.z.enum([
|
|
32664
|
+
"debug",
|
|
32665
|
+
"info",
|
|
32666
|
+
"warn",
|
|
32667
|
+
"error"
|
|
32668
|
+
]);
|
|
32669
|
+
var LogEntrySchema = zod.z.object({
|
|
32670
|
+
timestamp: zod.z.date(),
|
|
32671
|
+
level: LogLevelSchema,
|
|
32672
|
+
scope: zod.z.array(zod.z.string()),
|
|
32673
|
+
message: zod.z.string(),
|
|
32674
|
+
meta: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
|
|
32675
|
+
tags: zod.z.record(zod.z.string(), zod.z.string()).optional()
|
|
32676
|
+
});
|
|
32677
|
+
var logDestinationCapability = {
|
|
32678
|
+
name: "log-destination",
|
|
32679
|
+
scope: "system",
|
|
32680
|
+
mode: "collection",
|
|
32681
|
+
internal: true,
|
|
32682
|
+
methods: {
|
|
32683
|
+
write: method(LogEntrySchema, zod.z.void(), { kind: "mutation" }),
|
|
32684
|
+
query: method(zod.z.object({
|
|
32685
|
+
scope: zod.z.array(zod.z.string()).optional(),
|
|
32686
|
+
level: LogLevelSchema.optional(),
|
|
32687
|
+
since: zod.z.date().optional(),
|
|
32688
|
+
until: zod.z.date().optional(),
|
|
32689
|
+
limit: zod.z.number().optional(),
|
|
32690
|
+
tags: zod.z.record(zod.z.string(), zod.z.string()).optional()
|
|
32691
|
+
}), zod.z.array(LogEntrySchema).readonly())
|
|
32692
|
+
},
|
|
32693
|
+
/** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
|
|
32694
|
+
mount: { kind: "skip" }
|
|
32695
|
+
};
|
|
32661
32696
|
var LoginStageEnum = zod.z.enum(["primary", "second-factor"]);
|
|
32662
32697
|
var RedirectLoginMethodSchema = zod.z.object({
|
|
32663
32698
|
kind: zod.z.literal("redirect"),
|
|
@@ -36745,9 +36780,13 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
36745
36780
|
var MediaFileSchema = zod.z.object({
|
|
36746
36781
|
key: zod.z.string(),
|
|
36747
36782
|
kind: MediaFileKindEnum,
|
|
36748
|
-
base64: zod.z.string(),
|
|
36749
36783
|
sizeBytes: zod.z.number(),
|
|
36750
36784
|
timestamp: zod.z.number()
|
|
36785
|
+
}).extend({
|
|
36786
|
+
/** `/addon/<addonId>/event-media/<encoded stored key>`. Always present. */
|
|
36787
|
+
url: zod.z.string(),
|
|
36788
|
+
/** @deprecated Transitional — see the schema docblock. Use {@link url}. */
|
|
36789
|
+
base64: zod.z.string()
|
|
36751
36790
|
});
|
|
36752
36791
|
var MediaFileInfoSchema = MediaFileSchema.omit({ base64: true });
|
|
36753
36792
|
var RetrainMacroClassSchema = zod.z.enum([
|
|
@@ -37480,6 +37519,17 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
37480
37519
|
* happens to stamp it. This count is what the migration planner's
|
|
37481
37520
|
* non-blocking gate reads; `relocateMedia({ mode: 'seal' })` is what drives
|
|
37482
37521
|
* it to zero.
|
|
37522
|
+
*
|
|
37523
|
+
* TWO indexed statements per collection, not a walk. It used to page the
|
|
37524
|
+
* whole collection at 200 rows per RPC ordered by an unindexed column, so
|
|
37525
|
+
* on the live hub — 1 254 576 rows — it hit the 60 s RPC deadline every
|
|
37526
|
+
* time it was called, and the migration it gates could never start. The
|
|
37527
|
+
* cheap question (`present`: is there at least one) is asked first and
|
|
37528
|
+
* separately from the expensive one (`rows`), because only the first has
|
|
37529
|
+
* to be answerable for the gate to do its job.
|
|
37530
|
+
*
|
|
37531
|
+
* **`null` is "not measurable", never zero** — at either level. An
|
|
37532
|
+
* unreadable collection must not read as a sealed one.
|
|
37483
37533
|
*/
|
|
37484
37534
|
countUnstampedEventMedia: method(zod.z.object({}), UnstampedEventMediaCountSchema, { auth: "admin" }),
|
|
37485
37535
|
/**
|
|
@@ -37803,6 +37853,26 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
37803
37853
|
deviceId: zod.z.number()
|
|
37804
37854
|
}), zod.z.array(MediaFileInfoSchema).readonly()),
|
|
37805
37855
|
/**
|
|
37856
|
+
* What media an EVENT has, without any of it — the twin `getEventMedia`
|
|
37857
|
+
* never had.
|
|
37858
|
+
*
|
|
37859
|
+
* `listTrackMedia` above got this treatment because a track's media is
|
|
37860
|
+
* 5-8 MB. An event's is worse per row, not better: an old-style event owns
|
|
37861
|
+
* a `crop` AND a native-resolution `fullFrameBoxed`, and the track DETAIL
|
|
37862
|
+
* modal — the one surface that legitimately shows the big kinds — unions
|
|
37863
|
+
* both listings to build its filmstrip. It then renders every tile from
|
|
37864
|
+
* the `event-media` plane by key and throws the bytes away. Measured on
|
|
37865
|
+
* the live hub: one event's `fullFrameBoxed` is 2 824 077 B, base64'd to
|
|
37866
|
+
* ~3.8 MB, allocated whole in hub-main's heap, for a list of keys.
|
|
37867
|
+
*
|
|
37868
|
+
* Same `deviceId` authorization subject as `getEventMedia`, and the same
|
|
37869
|
+
* rows — this is a projection of that method, never a different question.
|
|
37870
|
+
*/
|
|
37871
|
+
listEventMedia: method(zod.z.object({
|
|
37872
|
+
eventId: zod.z.string(),
|
|
37873
|
+
deviceId: zod.z.number()
|
|
37874
|
+
}), zod.z.array(MediaFileInfoSchema).readonly()),
|
|
37875
|
+
/**
|
|
37806
37876
|
* Search object events by text query using CLIP cosine similarity.
|
|
37807
37877
|
* Encodes `text` via the `embedding-encoder` cap, queries the
|
|
37808
37878
|
* `ObjectEmbeddingStore` with optional prefilters, ranks all matching
|
|
@@ -43098,7 +43168,7 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
43098
43168
|
var MediaFileLiteSchema$1 = zod.z.object({
|
|
43099
43169
|
key: zod.z.string(),
|
|
43100
43170
|
kind: zod.z.string(),
|
|
43101
|
-
|
|
43171
|
+
url: zod.z.string(),
|
|
43102
43172
|
sizeBytes: zod.z.number(),
|
|
43103
43173
|
timestamp: zod.z.number()
|
|
43104
43174
|
});
|
|
@@ -45991,7 +46061,7 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
45991
46061
|
var MediaFileLiteSchema = zod.z.object({
|
|
45992
46062
|
key: zod.z.string(),
|
|
45993
46063
|
kind: zod.z.string(),
|
|
45994
|
-
|
|
46064
|
+
url: zod.z.string(),
|
|
45995
46065
|
sizeBytes: zod.z.number(),
|
|
45996
46066
|
timestamp: zod.z.number()
|
|
45997
46067
|
});
|
|
@@ -53299,6 +53369,12 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
53299
53369
|
addonId: null,
|
|
53300
53370
|
access: "view"
|
|
53301
53371
|
},
|
|
53372
|
+
"pipelineAnalytics.listEventMedia": {
|
|
53373
|
+
capName: "pipeline-analytics",
|
|
53374
|
+
capScope: "device",
|
|
53375
|
+
addonId: null,
|
|
53376
|
+
access: "view"
|
|
53377
|
+
},
|
|
53302
53378
|
"pipelineAnalytics.listGroups": {
|
|
53303
53379
|
capName: "pipeline-analytics",
|
|
53304
53380
|
capScope: "device",
|
|
@@ -56922,6 +56998,11 @@ var require_dist_Ck2jkBZk = __commonJS({
|
|
|
56922
56998
|
form: "array",
|
|
56923
56999
|
optional: false
|
|
56924
57000
|
}],
|
|
57001
|
+
"pipelineAnalytics.listEventMedia": [{
|
|
57002
|
+
name: "deviceId",
|
|
57003
|
+
form: "single",
|
|
57004
|
+
optional: false
|
|
57005
|
+
}],
|
|
56925
57006
|
"pipelineAnalytics.listGroups": [{
|
|
56926
57007
|
name: "deviceIds",
|
|
56927
57008
|
form: "array",
|
|
@@ -59066,7 +59147,7 @@ var require_alerts_addon = __commonJS({
|
|
|
59066
59147
|
[Symbol.toStringTag]: { value: "Module" }
|
|
59067
59148
|
});
|
|
59068
59149
|
require_chunk_Cek0wNdY();
|
|
59069
|
-
var require_dist10 =
|
|
59150
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
59070
59151
|
function selectExpired(alerts, cutoffMs) {
|
|
59071
59152
|
return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
|
|
59072
59153
|
}
|
|
@@ -59885,7 +59966,7 @@ var require_console_logging = __commonJS({
|
|
|
59885
59966
|
[Symbol.toStringTag]: { value: "Module" }
|
|
59886
59967
|
});
|
|
59887
59968
|
require_chunk_Cek0wNdY();
|
|
59888
|
-
var require_dist10 =
|
|
59969
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
59889
59970
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
59890
59971
|
var LEVEL_RANK = {
|
|
59891
59972
|
debug: 0,
|
|
@@ -59979,7 +60060,7 @@ var require_core_blocks_addon = __commonJS({
|
|
|
59979
60060
|
"use strict";
|
|
59980
60061
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
59981
60062
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
59982
|
-
var require_dist10 =
|
|
60063
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
59983
60064
|
var node_crypto = __require("crypto");
|
|
59984
60065
|
var node_fs_promises = __require("fs/promises");
|
|
59985
60066
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -60876,11 +60957,11 @@ var require_core_blocks = __commonJS({
|
|
|
60876
60957
|
}
|
|
60877
60958
|
});
|
|
60878
60959
|
|
|
60879
|
-
// ../system/dist/retired-settings-keys-
|
|
60880
|
-
var
|
|
60881
|
-
"../system/dist/retired-settings-keys-
|
|
60960
|
+
// ../system/dist/retired-settings-keys-BBohF-eC.js
|
|
60961
|
+
var require_retired_settings_keys_BBohF_eC = __commonJS({
|
|
60962
|
+
"../system/dist/retired-settings-keys-BBohF-eC.js"(exports) {
|
|
60882
60963
|
"use strict";
|
|
60883
|
-
var require_dist10 =
|
|
60964
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
60884
60965
|
function settingsStoreIsAuthoritativeHere(env) {
|
|
60885
60966
|
const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
|
|
60886
60967
|
return raw === "" || raw === "hub";
|
|
@@ -63094,8 +63175,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
63094
63175
|
[Symbol.toStringTag]: { value: "Module" }
|
|
63095
63176
|
});
|
|
63096
63177
|
require_chunk_Cek0wNdY();
|
|
63097
|
-
var require_dist10 =
|
|
63098
|
-
var require_retired_settings_keys =
|
|
63178
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
63179
|
+
var require_retired_settings_keys = require_retired_settings_keys_BBohF_eC();
|
|
63099
63180
|
var node_crypto = __require("crypto");
|
|
63100
63181
|
var _camstack_types_node = require_node();
|
|
63101
63182
|
var JOB_HISTORY = 20;
|
|
@@ -63558,10 +63639,17 @@ var require_device_manager_addon = __commonJS({
|
|
|
63558
63639
|
const live = deps.ctx.kernel?.deviceRegistry?.getById(deviceId)?.type;
|
|
63559
63640
|
return typeof live === "string" && live.length > 0 ? live : void 0;
|
|
63560
63641
|
}
|
|
63561
|
-
async function resolveDevicePresence(deps, row, deviceId) {
|
|
63642
|
+
async function resolveDevicePresence(deps, row, deviceId, ledgerNonEmpty) {
|
|
63562
63643
|
if (deps.ctx.kernel?.deviceRegistry?.getById(deviceId)) return "present";
|
|
63563
63644
|
if (row !== null) return "present";
|
|
63564
|
-
return await
|
|
63645
|
+
return await ledgerNonEmpty() ? "absent" : "unknown";
|
|
63646
|
+
}
|
|
63647
|
+
function onceLedgerNonEmpty(deps) {
|
|
63648
|
+
let pending = null;
|
|
63649
|
+
return () => {
|
|
63650
|
+
if (pending === null) pending = deps.rows.count().then((n) => n > 0);
|
|
63651
|
+
return pending;
|
|
63652
|
+
};
|
|
63565
63653
|
}
|
|
63566
63654
|
function capAppliesToDeviceType(def, deviceType) {
|
|
63567
63655
|
if (deviceType === void 0) return true;
|
|
@@ -63570,14 +63658,17 @@ var require_device_manager_addon = __commonJS({
|
|
|
63570
63658
|
return declared.some((t) => t === deviceType);
|
|
63571
63659
|
}
|
|
63572
63660
|
async function getBindings(deps, input) {
|
|
63573
|
-
const row = await deps.rows.get(input.deviceId);
|
|
63574
|
-
return resolveBindingsForDevice(deps, input.deviceId, row
|
|
63661
|
+
const [row, store] = await Promise.all([deps.rows.get(input.deviceId), readBindingsStore(deps)]);
|
|
63662
|
+
return resolveBindingsForDevice(deps, input.deviceId, row, {
|
|
63663
|
+
store,
|
|
63664
|
+
ledgerNonEmpty: onceLedgerNonEmpty(deps)
|
|
63665
|
+
});
|
|
63575
63666
|
}
|
|
63576
|
-
async function resolveBindingsForDevice(deps, deviceId, row) {
|
|
63667
|
+
async function resolveBindingsForDevice(deps, deviceId, row, pass) {
|
|
63577
63668
|
const storeKey = String(deviceId);
|
|
63578
|
-
const perDevice =
|
|
63669
|
+
const perDevice = pass.store.deviceBindings[storeKey] ?? {};
|
|
63579
63670
|
const deviceType = resolveDeviceType(deps, row, deviceId);
|
|
63580
|
-
const presence = await resolveDevicePresence(deps, row, deviceId);
|
|
63671
|
+
const presence = await resolveDevicePresence(deps, row, deviceId, pass.ledgerNonEmpty);
|
|
63581
63672
|
const entries = [];
|
|
63582
63673
|
const seenCaps = /* @__PURE__ */ new Set();
|
|
63583
63674
|
const resolveRemote = (capName) => deps.remoteNativeCaps.get(deviceId)?.get(capName) ?? resolveRemoteNativeCapFromRegistry(deps, capName, deviceId);
|
|
@@ -63677,13 +63768,17 @@ var require_device_manager_addon = __commonJS({
|
|
|
63677
63768
|
}
|
|
63678
63769
|
async function getBindingsBatch(deps, input) {
|
|
63679
63770
|
const ids = [...new Set(input.deviceIds)];
|
|
63680
|
-
const rows = await deps.rows.getMany(ids);
|
|
63771
|
+
const [rows, store] = await Promise.all([deps.rows.getMany(ids), readBindingsStore(deps)]);
|
|
63772
|
+
const pass = {
|
|
63773
|
+
store,
|
|
63774
|
+
ledgerNonEmpty: onceLedgerNonEmpty(deps)
|
|
63775
|
+
};
|
|
63681
63776
|
const out = [];
|
|
63682
|
-
for (const deviceId of ids) out.push(await resolveBindingsForDevice(deps, deviceId, rows.get(deviceId) ?? null));
|
|
63777
|
+
for (const deviceId of ids) out.push(await resolveBindingsForDevice(deps, deviceId, rows.get(deviceId) ?? null, pass));
|
|
63683
63778
|
return out;
|
|
63684
63779
|
}
|
|
63685
63780
|
async function getAllBindings(deps) {
|
|
63686
|
-
const fleet = await deps.rows.listAll();
|
|
63781
|
+
const [fleet, store] = await Promise.all([deps.rows.listAll(), readBindingsStore(deps)]);
|
|
63687
63782
|
const rowById = /* @__PURE__ */ new Map();
|
|
63688
63783
|
const ids = /* @__PURE__ */ new Set();
|
|
63689
63784
|
for (const row of fleet) {
|
|
@@ -63696,8 +63791,12 @@ var require_device_manager_addon = __commonJS({
|
|
|
63696
63791
|
deps.ctx.logger.warn("getAllBindings found no devices \u2014 warm boot will see nothing", { meta: { hasRegistry: deps.ctx.kernel?.deviceRegistry !== void 0 } });
|
|
63697
63792
|
return [];
|
|
63698
63793
|
}
|
|
63794
|
+
const pass = {
|
|
63795
|
+
store,
|
|
63796
|
+
ledgerNonEmpty: onceLedgerNonEmpty(deps)
|
|
63797
|
+
};
|
|
63699
63798
|
const out = [];
|
|
63700
|
-
for (const deviceId of [...ids].sort((a, b) => a - b)) out.push(await resolveBindingsForDevice(deps, deviceId, rowById.get(deviceId) ?? null));
|
|
63799
|
+
for (const deviceId of [...ids].sort((a, b) => a - b)) out.push(await resolveBindingsForDevice(deps, deviceId, rowById.get(deviceId) ?? null, pass));
|
|
63701
63800
|
return out;
|
|
63702
63801
|
}
|
|
63703
63802
|
async function lookupPersistedStableId(deps, deviceId) {
|
|
@@ -64257,7 +64356,12 @@ var require_device_manager_addon = __commonJS({
|
|
|
64257
64356
|
...sourceInfoGetDevice !== void 0 ? { sourceInfo: sourceInfoGetDevice } : {}
|
|
64258
64357
|
};
|
|
64259
64358
|
}
|
|
64260
|
-
|
|
64359
|
+
function persistedChildIds(childRows, liveChildren) {
|
|
64360
|
+
const live = /* @__PURE__ */ new Set();
|
|
64361
|
+
for (const device of liveChildren) live.add(device.id);
|
|
64362
|
+
return childRows.filter((row) => !live.has(row.meta.id)).map((row) => row.meta.id);
|
|
64363
|
+
}
|
|
64364
|
+
function projectChildren(pctx, parentDeviceId, ownerAddonId, childRows, liveChildren, configs) {
|
|
64261
64365
|
const results = [];
|
|
64262
64366
|
const seen = /* @__PURE__ */ new Set();
|
|
64263
64367
|
const rowById = /* @__PURE__ */ new Map();
|
|
@@ -64273,9 +64377,9 @@ var require_device_manager_addon = __commonJS({
|
|
|
64273
64377
|
const childStableId = m.stableId;
|
|
64274
64378
|
const key = String(m.id);
|
|
64275
64379
|
if (seen.has(key)) continue;
|
|
64276
|
-
const persistedConfig =
|
|
64380
|
+
const persistedConfig = configs.get(m.id) ?? {};
|
|
64277
64381
|
const metadata = row.metadata;
|
|
64278
|
-
const sourceInfoChild = buildSourceInfoFromConfig(persistedConfig
|
|
64382
|
+
const sourceInfoChild = buildSourceInfoFromConfig(persistedConfig, childStableId, ownerAddonId);
|
|
64279
64383
|
results.push({
|
|
64280
64384
|
id: m.id,
|
|
64281
64385
|
stableId: childStableId,
|
|
@@ -64290,7 +64394,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
64290
64394
|
probed: pctx.host.resolveDeviceProbed(m.id),
|
|
64291
64395
|
features: persistedFeatures(m.features),
|
|
64292
64396
|
isCamera: false,
|
|
64293
|
-
config: persistedConfig
|
|
64397
|
+
config: persistedConfig,
|
|
64294
64398
|
metadata,
|
|
64295
64399
|
...m.integrationId !== void 0 ? { integrationId: m.integrationId } : {},
|
|
64296
64400
|
...m.linkDeviceId !== void 0 ? { linkDeviceId: m.linkDeviceId } : {},
|
|
@@ -64317,7 +64421,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
64317
64421
|
}
|
|
64318
64422
|
const childRows = await pctx.metaStore.rows.listByParent(parentDeviceId);
|
|
64319
64423
|
const liveChildren = pctx.registry?.getChildren(parentDeviceId) ?? [];
|
|
64320
|
-
|
|
64424
|
+
const configs = await readDeviceConfigs(pctx, persistedChildIds(childRows, liveChildren));
|
|
64425
|
+
return [...projectChildren(pctx, parentDeviceId, ownerAddonId, childRows, liveChildren, configs)];
|
|
64321
64426
|
}
|
|
64322
64427
|
async function getChildrenBatch(pctx, input) {
|
|
64323
64428
|
const parentIds = [...new Set(input.parentDeviceIds)];
|
|
@@ -64345,11 +64450,14 @@ var require_device_manager_addon = __commonJS({
|
|
|
64345
64450
|
if (bucket === void 0) liveByParent.set(parentId, [device]);
|
|
64346
64451
|
else bucket.push(device);
|
|
64347
64452
|
}
|
|
64453
|
+
const configIds = [];
|
|
64454
|
+
for (const parentId of owners.keys()) configIds.push(...persistedChildIds(rowsByParent.get(parentId) ?? [], liveByParent.get(parentId) ?? []));
|
|
64455
|
+
const configs = await readDeviceConfigs(pctx, configIds);
|
|
64348
64456
|
for (const [parentId, ownerAddonId] of owners) {
|
|
64349
64457
|
const childRows = rowsByParent.get(parentId) ?? [];
|
|
64350
64458
|
const liveChildren = liveByParent.get(parentId) ?? [];
|
|
64351
64459
|
if (childRows.length === 0 && liveChildren.length === 0) continue;
|
|
64352
|
-
const projected =
|
|
64460
|
+
const projected = projectChildren(pctx, parentId, ownerAddonId, childRows, liveChildren, configs);
|
|
64353
64461
|
if (projected.length > 0) out[String(parentId)] = [...projected];
|
|
64354
64462
|
}
|
|
64355
64463
|
return out;
|
|
@@ -65391,18 +65499,20 @@ var require_device_manager_addon = __commonJS({
|
|
|
65391
65499
|
}
|
|
65392
65500
|
async function setWrapperActive(deps, input) {
|
|
65393
65501
|
const storeKey = String(input.deviceId);
|
|
65394
|
-
|
|
65395
|
-
|
|
65396
|
-
|
|
65397
|
-
|
|
65398
|
-
|
|
65399
|
-
|
|
65400
|
-
|
|
65401
|
-
|
|
65402
|
-
|
|
65403
|
-
|
|
65404
|
-
|
|
65405
|
-
|
|
65502
|
+
await deps.bindingsDeps.withAddonStoreWriteLock(async () => {
|
|
65503
|
+
const store = await readBindingsStore(deps.bindingsDeps);
|
|
65504
|
+
const perDevice = { ...store.deviceBindings[storeKey] };
|
|
65505
|
+
if (input.active) perDevice[input.capName] = { wrapperAddonId: input.wrapperAddonId };
|
|
65506
|
+
else perDevice[input.capName] = { wrapperAddonId: null };
|
|
65507
|
+
const nextDeviceBindings = Object.keys(perDevice).length > 0 ? {
|
|
65508
|
+
...store.deviceBindings,
|
|
65509
|
+
[storeKey]: perDevice
|
|
65510
|
+
} : (() => {
|
|
65511
|
+
const { [storeKey]: _drop, ...rest } = store.deviceBindings;
|
|
65512
|
+
return rest;
|
|
65513
|
+
})();
|
|
65514
|
+
await writeBindingsStore(deps.bindingsDeps, { deviceBindings: nextDeviceBindings });
|
|
65515
|
+
});
|
|
65406
65516
|
deps.ctx.eventBus.emit({
|
|
65407
65517
|
id: (0, node_crypto.randomUUID)(),
|
|
65408
65518
|
timestamp: /* @__PURE__ */ new Date(),
|
|
@@ -65681,12 +65791,13 @@ var require_device_manager_addon = __commonJS({
|
|
|
65681
65791
|
});
|
|
65682
65792
|
await pctx.settings.clearDeviceStore(deviceId);
|
|
65683
65793
|
await pctx.settings.clearDeviceRuntimeState(deviceId);
|
|
65684
|
-
const bindingsStore = await readBindingsStore(pctx.bindingsDeps);
|
|
65685
65794
|
const bindingKey = String(deviceId);
|
|
65686
|
-
|
|
65795
|
+
await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
|
|
65796
|
+
const bindingsStore = await readBindingsStore(pctx.bindingsDeps);
|
|
65797
|
+
if (!bindingsStore.deviceBindings[bindingKey]) return;
|
|
65687
65798
|
const { [bindingKey]: _removedBindings, ...restBindings } = bindingsStore.deviceBindings;
|
|
65688
65799
|
await writeBindingsStore(pctx.bindingsDeps, { deviceBindings: restBindings });
|
|
65689
|
-
}
|
|
65800
|
+
});
|
|
65690
65801
|
pctx.host.remoteNativeCaps.delete(deviceId);
|
|
65691
65802
|
pctx.host.capabilityRegistry?.unregisterAllNativeForDevice(deviceId);
|
|
65692
65803
|
pctx.metaStore.idToAddonId.delete(deviceId);
|
|
@@ -66028,7 +66139,9 @@ var require_device_manager_addon = __commonJS({
|
|
|
66028
66139
|
...def,
|
|
66029
66140
|
unit: require_dist10.normalizeUnit(def.unit) ?? def.unit
|
|
66030
66141
|
} : def]));
|
|
66031
|
-
await pctx.
|
|
66142
|
+
await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
|
|
66143
|
+
await pctx.settings.writeAddonStore({ roleDisplayDefaults: normalized });
|
|
66144
|
+
});
|
|
66032
66145
|
}
|
|
66033
66146
|
async function applyInitialMeta(pctx, input) {
|
|
66034
66147
|
const { deviceId, name, location, type, integrationId, linkDeviceId, role } = input;
|
|
@@ -66171,16 +66284,21 @@ var require_device_manager_addon = __commonJS({
|
|
|
66171
66284
|
async function addLocation(pctx, input) {
|
|
66172
66285
|
const trimmed = input.name.trim();
|
|
66173
66286
|
if (trimmed.length === 0) throw new Error("[device-manager] addLocation: name must be non-empty");
|
|
66174
|
-
|
|
66175
|
-
|
|
66176
|
-
|
|
66287
|
+
await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
|
|
66288
|
+
const current = (await pctx.metaStore.readStore()).locations ?? [];
|
|
66289
|
+
if (current.some((l) => l.toLowerCase() === trimmed.toLowerCase())) return;
|
|
66290
|
+
await pctx.settings.writeAddonStore({ locations: [...current, trimmed] });
|
|
66291
|
+
});
|
|
66177
66292
|
}
|
|
66178
66293
|
async function removeLocation(pctx, input) {
|
|
66179
66294
|
const trimmed = input.name.trim();
|
|
66180
66295
|
if (trimmed.length === 0) return;
|
|
66181
|
-
|
|
66182
|
-
|
|
66183
|
-
|
|
66296
|
+
await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
|
|
66297
|
+
const current = (await pctx.metaStore.readStore()).locations ?? [];
|
|
66298
|
+
const remaining = current.filter((l) => l.toLowerCase() !== trimmed.toLowerCase());
|
|
66299
|
+
if (remaining.length === current.length) return;
|
|
66300
|
+
await pctx.settings.writeAddonStore({ locations: remaining });
|
|
66301
|
+
});
|
|
66184
66302
|
if (input.cascade !== true) return;
|
|
66185
66303
|
const cleared = await pctx.metaStore.withMetaWriteLock(async () => {
|
|
66186
66304
|
const out = [];
|
|
@@ -66221,23 +66339,40 @@ var require_device_manager_addon = __commonJS({
|
|
|
66221
66339
|
function isRoleDisplayDefaults(value) {
|
|
66222
66340
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
66223
66341
|
}
|
|
66342
|
+
function createWriteLock() {
|
|
66343
|
+
let chain = Promise.resolve();
|
|
66344
|
+
return async (fn) => {
|
|
66345
|
+
const previous = chain;
|
|
66346
|
+
let release = () => {
|
|
66347
|
+
};
|
|
66348
|
+
chain = new Promise((resolve) => {
|
|
66349
|
+
release = resolve;
|
|
66350
|
+
});
|
|
66351
|
+
try {
|
|
66352
|
+
await previous.catch(() => {
|
|
66353
|
+
});
|
|
66354
|
+
return await fn();
|
|
66355
|
+
} finally {
|
|
66356
|
+
release();
|
|
66357
|
+
}
|
|
66358
|
+
};
|
|
66359
|
+
}
|
|
66224
66360
|
var DeviceMetaStore = class {
|
|
66225
66361
|
settings;
|
|
66226
66362
|
registry;
|
|
66227
66363
|
rows;
|
|
66364
|
+
withAddonStoreWriteLock;
|
|
66228
66365
|
/** Synchronous ownership cache, keyed by NUMERIC deviceId → owning addonId.
|
|
66229
66366
|
* The persisted row store is authoritative but reads are async; hub-side
|
|
66230
66367
|
* callers (e.g. `CapabilityRegistry.getNativeProvider` fallback) need
|
|
66231
66368
|
* ownership without awaiting. Kept in sync with every register/remove and
|
|
66232
66369
|
* warmed from persistence on boot. */
|
|
66233
66370
|
idToAddonId = /* @__PURE__ */ new Map();
|
|
66234
|
-
|
|
66235
|
-
* chain (see `withMetaWriteLock`). Per-instance state. */
|
|
66236
|
-
metaWriteChain = Promise.resolve();
|
|
66237
|
-
constructor(settings, registry, rows) {
|
|
66371
|
+
constructor(settings, registry, rows, withAddonStoreWriteLock = createWriteLock()) {
|
|
66238
66372
|
this.settings = settings;
|
|
66239
66373
|
this.registry = registry;
|
|
66240
66374
|
this.rows = rows;
|
|
66375
|
+
this.withAddonStoreWriteLock = withAddonStoreWriteLock;
|
|
66241
66376
|
}
|
|
66242
66377
|
/** The read currently in flight, or null. Never a settled value — see
|
|
66243
66378
|
* {@link readStore}. */
|
|
@@ -66268,22 +66403,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
66268
66403
|
this.inFlightRead = read;
|
|
66269
66404
|
return read;
|
|
66270
66405
|
};
|
|
66271
|
-
withMetaWriteLock =
|
|
66272
|
-
const previous = this.metaWriteChain;
|
|
66273
|
-
let release = () => {
|
|
66274
|
-
};
|
|
66275
|
-
const next = new Promise((resolve) => {
|
|
66276
|
-
release = resolve;
|
|
66277
|
-
});
|
|
66278
|
-
this.metaWriteChain = next;
|
|
66279
|
-
try {
|
|
66280
|
-
await previous.catch(() => {
|
|
66281
|
-
});
|
|
66282
|
-
return await fn();
|
|
66283
|
-
} finally {
|
|
66284
|
-
release();
|
|
66285
|
-
}
|
|
66286
|
-
};
|
|
66406
|
+
withMetaWriteLock = createWriteLock();
|
|
66287
66407
|
/** The whole persisted row for one device, or `null`. */
|
|
66288
66408
|
getRow = async (deviceId) => this.rows.get(deviceId);
|
|
66289
66409
|
/**
|
|
@@ -66317,11 +66437,24 @@ var require_device_manager_addon = __commonJS({
|
|
|
66317
66437
|
ids.delete(parentId);
|
|
66318
66438
|
return [...ids];
|
|
66319
66439
|
};
|
|
66320
|
-
|
|
66440
|
+
/**
|
|
66441
|
+
* Mint the next numeric device id.
|
|
66442
|
+
*
|
|
66443
|
+
* A read-modify-write of `nextDeviceId`, and it must hold the addon-store
|
|
66444
|
+
* lock for the whole of it. Serialising the WRITES is not enough and never
|
|
66445
|
+
* was: two callers that both read `N` both write `N + 1` and both return
|
|
66446
|
+
* `N`, so two devices receive the same id. (`writeAddonStore` is itself
|
|
66447
|
+
* read-modify-write over the addon's whole key range, so an unlocked bump
|
|
66448
|
+
* can also be reverted wholesale by a concurrent `setWrapperActive`.)
|
|
66449
|
+
*
|
|
66450
|
+
* Callers hold {@link withMetaWriteLock} — this is the one nesting, and it
|
|
66451
|
+
* only ever goes meta ⊃ addon-store.
|
|
66452
|
+
*/
|
|
66453
|
+
allocateNextDeviceId = async () => this.withAddonStoreWriteLock(async () => {
|
|
66321
66454
|
const current = (await this.readStore()).nextDeviceId ?? 1;
|
|
66322
66455
|
await this.settings.writeAddonStore({ nextDeviceId: current + 1 });
|
|
66323
66456
|
return current;
|
|
66324
|
-
};
|
|
66457
|
+
});
|
|
66325
66458
|
};
|
|
66326
66459
|
var CENSUS_STACK_FRAMES = 3;
|
|
66327
66460
|
function siteFromStack(stack) {
|
|
@@ -67515,6 +67648,17 @@ var require_device_manager_addon = __commonJS({
|
|
|
67515
67648
|
* it through {@link bindingsDeps} or the `ProviderContext`.
|
|
67516
67649
|
*/
|
|
67517
67650
|
deviceRows = null;
|
|
67651
|
+
/**
|
|
67652
|
+
* The ONE lock ordering every read-modify-write of this addon's
|
|
67653
|
+
* `addon-settings` key range (`deviceBindings`, `locations`, `nextDeviceId`,
|
|
67654
|
+
* `roleDisplayDefaults`).
|
|
67655
|
+
*
|
|
67656
|
+
* It lives on the addon rather than on `DeviceMetaStore` because the writers
|
|
67657
|
+
* do not all go through that class — `setWrapperActive` and `removeDevice`'s
|
|
67658
|
+
* bindings purge reach the store through `BindingsDeps` — and two locks over
|
|
67659
|
+
* one key range is the same defect one level up. Handed to both.
|
|
67660
|
+
*/
|
|
67661
|
+
addonStoreWriteLock = createWriteLock();
|
|
67518
67662
|
/** Build the dependency context the extracted binding resolvers consume. */
|
|
67519
67663
|
get bindingsDeps() {
|
|
67520
67664
|
const rows = this.deviceRows;
|
|
@@ -67523,7 +67667,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
67523
67667
|
ctx: this.ctx,
|
|
67524
67668
|
capabilityRegistry: this.capabilityRegistry,
|
|
67525
67669
|
remoteNativeCaps: this.remoteNativeCaps,
|
|
67526
|
-
rows
|
|
67670
|
+
rows,
|
|
67671
|
+
withAddonStoreWriteLock: this.addonStoreWriteLock
|
|
67527
67672
|
};
|
|
67528
67673
|
}
|
|
67529
67674
|
async getBindings(input) {
|
|
@@ -67592,7 +67737,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
67592
67737
|
} catch (err) {
|
|
67593
67738
|
this.ctx.logger.warn("retired-row purge failed", { meta: { error: require_dist10.errMsg(err) } });
|
|
67594
67739
|
}
|
|
67595
|
-
const metaStore = new DeviceMetaStore(settings, registry, deviceRows);
|
|
67740
|
+
const metaStore = new DeviceMetaStore(settings, registry, deviceRows, this.addonStoreWriteLock);
|
|
67596
67741
|
this.stateMirrorImpl = new DeviceStateMirror(this.ctx);
|
|
67597
67742
|
const stateMirror = this.stateMirrorImpl;
|
|
67598
67743
|
const resolvePersistedById = metaStore.resolvePersistedById;
|
|
@@ -67844,7 +67989,7 @@ var require_hub_forwarder = __commonJS({
|
|
|
67844
67989
|
[Symbol.toStringTag]: { value: "Module" }
|
|
67845
67990
|
});
|
|
67846
67991
|
require_chunk_Cek0wNdY();
|
|
67847
|
-
var require_dist10 =
|
|
67992
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
67848
67993
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
67849
67994
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
67850
67995
|
var HubForwarderDestination = class {
|
|
@@ -67981,7 +68126,7 @@ var require_liveness_monitor_addon = __commonJS({
|
|
|
67981
68126
|
"use strict";
|
|
67982
68127
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
67983
68128
|
require_chunk_Cek0wNdY();
|
|
67984
|
-
var require_dist10 =
|
|
68129
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
67985
68130
|
var NO_DEVICES = "liveness:no-devices";
|
|
67986
68131
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
67987
68132
|
var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
|
|
@@ -68171,7 +68316,7 @@ var require_local_auth_addon = __commonJS({
|
|
|
68171
68316
|
[Symbol.toStringTag]: { value: "Module" }
|
|
68172
68317
|
});
|
|
68173
68318
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
68174
|
-
var require_dist10 =
|
|
68319
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
68175
68320
|
var node_crypto = __require("crypto");
|
|
68176
68321
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
68177
68322
|
var crypto$1 = __require("crypto");
|
|
@@ -75984,7 +76129,7 @@ var require_loki_logging = __commonJS({
|
|
|
75984
76129
|
[Symbol.toStringTag]: { value: "Module" }
|
|
75985
76130
|
});
|
|
75986
76131
|
require_chunk_Cek0wNdY();
|
|
75987
|
-
var require_dist10 =
|
|
76132
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
75988
76133
|
function sanitizeLabelName(raw) {
|
|
75989
76134
|
const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
|
|
75990
76135
|
return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
|
|
@@ -76549,7 +76694,7 @@ var require_native_metrics_addon = __commonJS({
|
|
|
76549
76694
|
[Symbol.toStringTag]: { value: "Module" }
|
|
76550
76695
|
});
|
|
76551
76696
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
76552
|
-
var require_dist10 =
|
|
76697
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
76553
76698
|
var node_fs_promises = __require("fs/promises");
|
|
76554
76699
|
var node_child_process = __require("child_process");
|
|
76555
76700
|
var node_util = __require("util");
|
|
@@ -79171,7 +79316,7 @@ var require_filesystem_storage_addon = __commonJS({
|
|
|
79171
79316
|
[Symbol.toStringTag]: { value: "Module" }
|
|
79172
79317
|
});
|
|
79173
79318
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
79174
|
-
var require_dist10 =
|
|
79319
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
79175
79320
|
var node_crypto = __require("crypto");
|
|
79176
79321
|
var node_fs_promises = __require("fs/promises");
|
|
79177
79322
|
var node_path = __require("path");
|
|
@@ -80287,8 +80432,8 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
80287
80432
|
[Symbol.toStringTag]: { value: "Module" }
|
|
80288
80433
|
});
|
|
80289
80434
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
80290
|
-
var require_dist10 =
|
|
80291
|
-
var require_retired_settings_keys =
|
|
80435
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
80436
|
+
var require_retired_settings_keys = require_retired_settings_keys_BBohF_eC();
|
|
80292
80437
|
var node_crypto = __require("crypto");
|
|
80293
80438
|
var node_fs = __require("fs");
|
|
80294
80439
|
var node_module = __require("module");
|
|
@@ -80619,11 +80764,16 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
80619
80764
|
const resolve = (field) => {
|
|
80620
80765
|
const expr = fieldExprFor(field, shape);
|
|
80621
80766
|
if (expr === null && mode === "mutate") throw new UnsafeFilterError(`filter refers to "${field}", which this collection cannot express \u2014 refusing to run a bulk mutation with a dropped predicate`);
|
|
80767
|
+
if (expr === null && mode === "measure") throw new UnsafeFilterError(`filter refers to "${field}", which this collection cannot express \u2014 refusing to answer with a number counted over a different question`);
|
|
80622
80768
|
return expr;
|
|
80623
80769
|
};
|
|
80624
80770
|
for (const [field, value] of Object.entries(filter?.where ?? {})) {
|
|
80625
80771
|
const expr = resolve(field);
|
|
80626
80772
|
if (expr === null) continue;
|
|
80773
|
+
if (value === null) {
|
|
80774
|
+
clauses.push(`${expr} IS NULL`);
|
|
80775
|
+
continue;
|
|
80776
|
+
}
|
|
80627
80777
|
clauses.push(`${expr} = ?`);
|
|
80628
80778
|
params.push(serialize2(value));
|
|
80629
80779
|
}
|
|
@@ -80646,6 +80796,10 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
80646
80796
|
for (const [field, value] of Object.entries(filter?.whereNot ?? {})) {
|
|
80647
80797
|
const expr = resolve(field);
|
|
80648
80798
|
if (expr === null) continue;
|
|
80799
|
+
if (value === null) {
|
|
80800
|
+
clauses.push(`${expr} IS NOT NULL`);
|
|
80801
|
+
continue;
|
|
80802
|
+
}
|
|
80649
80803
|
clauses.push(`(${expr} IS NULL OR ${expr} != ?)`);
|
|
80650
80804
|
params.push(serialize2(value));
|
|
80651
80805
|
}
|
|
@@ -81158,13 +81312,17 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
81158
81312
|
* (visibility, kind, device, time window, expiry) — so `total` counted rows
|
|
81159
81313
|
* the page could never show, including other users'.
|
|
81160
81314
|
*
|
|
81161
|
-
* `
|
|
81162
|
-
*
|
|
81315
|
+
* `measure` mode. `count` compiles the same predicates `query` does — the
|
|
81316
|
+
* two must never disagree about WHICH rows they are talking about — but it
|
|
81317
|
+
* refuses a predicate it cannot express rather than skipping it. `query`
|
|
81318
|
+
* hands back rows and a caller can see the filter did not bite; a count
|
|
81319
|
+
* hands back a number, and the whole collection and the intended subset are
|
|
81320
|
+
* the same shape.
|
|
81163
81321
|
*/
|
|
81164
81322
|
async count({ namespace, collection, filter }) {
|
|
81165
81323
|
const scoped = this.scopedName(namespace, collection);
|
|
81166
81324
|
const decl = this.requireDeclared(scoped);
|
|
81167
|
-
const { whereSql, params } = compileFilter$1(filter, this.shapeOf(decl), "
|
|
81325
|
+
const { whereSql, params } = compileFilter$1(filter, this.shapeOf(decl), "measure", (v) => this.serializeColumnValue(v));
|
|
81168
81326
|
const sql = `SELECT COUNT(*) AS cnt FROM "${scoped}"${whereSql}`;
|
|
81169
81327
|
return this.measured({
|
|
81170
81328
|
op: "count",
|
|
@@ -81182,8 +81340,10 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
81182
81340
|
* projection — an unresolvable field THROWS rather than being dropped, because
|
|
81183
81341
|
* a missing aggregate comes back as a number that looks real.
|
|
81184
81342
|
*
|
|
81185
|
-
* `
|
|
81186
|
-
* which rows it is talking about
|
|
81343
|
+
* `measure` mode on the filter, so this agrees with `query` and `count`
|
|
81344
|
+
* about which rows it is talking about — and, since 2026-08-30, refuses an
|
|
81345
|
+
* unresolvable PREDICATE for the same reason it already refused an
|
|
81346
|
+
* unresolvable FIELD. Both come back as a number that looks real.
|
|
81187
81347
|
*/
|
|
81188
81348
|
async aggregate({ namespace, collection, fields, filter }) {
|
|
81189
81349
|
const scoped = this.scopedName(namespace, collection);
|
|
@@ -81195,7 +81355,7 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
81195
81355
|
if (col === null) throw new UnsafeFilterError(`aggregate cannot read "${f.field}" on "${scoped}" \u2014 it is not a column of this collection, and answering without it would return a number that looks real`);
|
|
81196
81356
|
selects.push(`${f.op.toUpperCase()}(${col}) AS "a${i}"`);
|
|
81197
81357
|
});
|
|
81198
|
-
const { whereSql, params } = compileFilter$1(filter, shape, "
|
|
81358
|
+
const { whereSql, params } = compileFilter$1(filter, shape, "measure", (v) => this.serializeColumnValue(v));
|
|
81199
81359
|
const sql = `SELECT ${selects.join(", ")} FROM "${scoped}"${whereSql}`;
|
|
81200
81360
|
const row = this.measured({
|
|
81201
81361
|
op: "aggregate",
|
|
@@ -81219,7 +81379,7 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
81219
81379
|
const shape = this.shapeOf(decl);
|
|
81220
81380
|
const col = fieldExprFor(field, shape);
|
|
81221
81381
|
if (col === null) return [];
|
|
81222
|
-
const { whereSql: where, params } = compileFilter$1(filter, shape, "
|
|
81382
|
+
const { whereSql: where, params } = compileFilter$1(filter, shape, "measure", (v) => this.serializeColumnValue(v));
|
|
81223
81383
|
const sql = `SELECT ${`CAST((${col} - ?) / ? AS INTEGER)`} AS bucket, COUNT(*) AS count FROM "${scoped}"${where} GROUP BY bucket ORDER BY bucket`;
|
|
81224
81384
|
return this.measured({
|
|
81225
81385
|
op: "histogram",
|
|
@@ -81500,8 +81660,10 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
81500
81660
|
* connection whose `sqlite_stat1` visibility and temp schema match.
|
|
81501
81661
|
*
|
|
81502
81662
|
* `EXPLAIN QUERY PLAN` prepares and plans; it does not execute the statement,
|
|
81503
|
-
* so it costs no page reads of its own.
|
|
81504
|
-
* slow-call path, once per shape per window
|
|
81663
|
+
* so it costs no page reads of its own. In production it is called only from
|
|
81664
|
+
* the slow-call path, once per shape per window; it is public so a guard can
|
|
81665
|
+
* assert that a declared index is the one the planner actually picks — "we
|
|
81666
|
+
* added an index" and "the query uses it" are different claims.
|
|
81505
81667
|
*
|
|
81506
81668
|
* Never throws. A plan that cannot be taken (a statement the engine will no
|
|
81507
81669
|
* longer prepare, a closed handle mid-shutdown) is a missing ANSWER, not a
|
|
@@ -82650,7 +82812,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82650
82812
|
[Symbol.toStringTag]: { value: "Module" }
|
|
82651
82813
|
});
|
|
82652
82814
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
82653
|
-
var require_dist10 =
|
|
82815
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
82654
82816
|
var node_crypto = __require("crypto");
|
|
82655
82817
|
var node_fs_promises = __require("fs/promises");
|
|
82656
82818
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -82856,6 +83018,10 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82856
83018
|
"recorder",
|
|
82857
83019
|
"pipeline"
|
|
82858
83020
|
];
|
|
83021
|
+
function describeUnstamped(lane) {
|
|
83022
|
+
if (!lane.present) return "0";
|
|
83023
|
+
return lane.rows === null ? "an unknown number of" : lane.rows.toString();
|
|
83024
|
+
}
|
|
82859
83025
|
var StorageMigrationCoordinator = class {
|
|
82860
83026
|
deps;
|
|
82861
83027
|
active = null;
|
|
@@ -82899,12 +83065,21 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82899
83065
|
if (moves.length === 0) throw new Error("select at least one storage class");
|
|
82900
83066
|
if (moves.some((move) => move.storageClass === "eventMedia")) {
|
|
82901
83067
|
const unstamped = await this.deps.participants.analytics.countUnstamped();
|
|
82902
|
-
if (unstamped
|
|
82903
|
-
if (mode === "nonBlocking") throw new Error(
|
|
83068
|
+
if (unstamped === null) {
|
|
83069
|
+
if (mode === "nonBlocking") throw new Error("could not determine whether any event-media row still carries no locationId. A non-blocking cutover repoints the default and would silently orphan every such row, so an unmeasured collection is refused exactly like a non-empty one. Retry, or run the blocking mode, which stamps rows as it moves them.");
|
|
83070
|
+
findings.push({
|
|
83071
|
+
code: "unstampedEventMediaRows",
|
|
83072
|
+
storageClass: "eventMedia",
|
|
83073
|
+
message: "the count of event-media rows carrying no locationId could not be taken \u2014 treat it as unknown, not as zero. This blocking migration stamps them as it moves them, so it is safe regardless; a non-blocking one would be refused."
|
|
83074
|
+
});
|
|
83075
|
+
} else if (unstamped.anyPresent) {
|
|
83076
|
+
const scale = unstamped.total === null ? "an unknown number of" : unstamped.total.toString();
|
|
83077
|
+
const perLane = `${describeUnstamped(unstamped.media)} media, ${describeUnstamped(unstamped.retrainFrames)} retrain frames`;
|
|
83078
|
+
if (mode === "nonBlocking") throw new Error(`${scale} event-media row(s) still carry no locationId (${perLane}). A non-blocking cutover would silently orphan them. Run the seal first: pipelineAnalytics.relocateMedia({ mode: "seal", toLocationId: "${this.deps.locations.getDefaultLocation("eventMedia")?.id ?? "eventMedia"}" }).`);
|
|
82904
83079
|
findings.push({
|
|
82905
83080
|
code: "unstampedEventMediaRows",
|
|
82906
83081
|
storageClass: "eventMedia",
|
|
82907
|
-
message: `${
|
|
83082
|
+
message: `${scale} event-media row(s) carry no locationId (${perLane}). This blocking migration stamps them as it moves them, but a non-blocking one would be refused until a seal pass drives the count to zero.`
|
|
82908
83083
|
});
|
|
82909
83084
|
}
|
|
82910
83085
|
}
|
|
@@ -83153,6 +83328,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83153
83328
|
const remaining = residue.filter((entry) => entry.storageClass === storageClass);
|
|
83154
83329
|
if (remaining.length === 0) throw new Error(`"${storageClass}" has nothing left outside its default location \u2014 there is nothing to drain. To CHANGE where it writes, use storageMigration.start with a new destination.`);
|
|
83155
83330
|
for (const entry of remaining) {
|
|
83331
|
+
if (this.deps.locations.getLocationById(entry.toLocationId)?.enabled === false) throw new Error(`The "${storageClass}" drain destination "${entry.toLocationId}" is DISABLED \u2014 a disabled location takes no writes, and a drain is the largest write there is. Enable it on the Storage screen, or make an enabled location the "${storageClass}" default first.`);
|
|
83156
83332
|
moves.push({
|
|
83157
83333
|
storageClass,
|
|
83158
83334
|
fromLocationId: entry.fromLocationId,
|
|
@@ -83404,6 +83580,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83404
83580
|
filesMoved: status.filesMoved,
|
|
83405
83581
|
filesTotal: status.filesTotal,
|
|
83406
83582
|
bytesMoved: status.bytesMoved,
|
|
83583
|
+
...status.rowsReconciled === void 0 ? {} : { rowsReconciled: status.rowsReconciled },
|
|
83407
83584
|
startedAt: status.startedAt,
|
|
83408
83585
|
observedAt: this.deps.now()
|
|
83409
83586
|
};
|
|
@@ -83426,9 +83603,10 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83426
83603
|
*
|
|
83427
83604
|
* The gate is the RE-COUNT, not the seal job's terminal state: a seal that
|
|
83428
83605
|
* failed some rows still finishes, and "finished" is not "there are none
|
|
83429
|
-
* left". A
|
|
83606
|
+
* left". A remaining row here fails the job while nothing has been paused
|
|
83430
83607
|
* and nothing has been repointed, which is the cheapest possible place to
|
|
83431
|
-
* discover it.
|
|
83608
|
+
* discover it. So does a re-count that could not be TAKEN: the gate opens on
|
|
83609
|
+
* a measured absence, and only on that.
|
|
83432
83610
|
*
|
|
83433
83611
|
* The seal's own mover job id is deliberately NOT durable. It is idempotent
|
|
83434
83612
|
* and cheap, so a coordinator restart mid-seal simply re-runs the whole
|
|
@@ -83438,7 +83616,8 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83438
83616
|
async sealEventMedia(job) {
|
|
83439
83617
|
const move = job.moves.find((candidate) => candidate.storageClass === "eventMedia");
|
|
83440
83618
|
if (move === void 0) return;
|
|
83441
|
-
|
|
83619
|
+
const before = await this.deps.participants.analytics.countUnstamped();
|
|
83620
|
+
if (before === null || before.anyPresent) {
|
|
83442
83621
|
const started = await this.deps.participants.analytics.startDrain({
|
|
83443
83622
|
toLocationId: move.fromLocationId,
|
|
83444
83623
|
mode: "seal"
|
|
@@ -83447,7 +83626,8 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83447
83626
|
if (job.cancelRequested) return;
|
|
83448
83627
|
}
|
|
83449
83628
|
const after = await this.deps.participants.analytics.countUnstamped();
|
|
83450
|
-
if (after
|
|
83629
|
+
if (after === null) throw new Error("event-media seal ran, but whether any row still carries no locationId could not be measured afterwards. Unknown is not zero; nothing has been paused or repointed.");
|
|
83630
|
+
if (after.anyPresent) throw new Error(`event-media seal left ${after.total === null ? "an unknown number of" : after.total.toString()} row(s) without a locationId (${describeUnstamped(after.media)} media, ${describeUnstamped(after.retrainFrames)} retrain frames). A non-blocking cutover would silently orphan them; nothing has been paused or repointed.`);
|
|
83451
83631
|
}
|
|
83452
83632
|
async waitForSeal(job, moverJobId) {
|
|
83453
83633
|
const sleep = this.deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
@@ -83796,6 +83976,34 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83796
83976
|
backfilled: backfill.length,
|
|
83797
83977
|
total: this.locations.size
|
|
83798
83978
|
} });
|
|
83979
|
+
this.reportDisabledDefaults();
|
|
83980
|
+
}
|
|
83981
|
+
/**
|
|
83982
|
+
* Shout about a persisted row that is BOTH the type default and disabled.
|
|
83983
|
+
*
|
|
83984
|
+
* `upsertLocation` cannot produce this (`resolveEnabled` force-enables a
|
|
83985
|
+
* default) and neither can `setDefaultLocations` — but hydrate writes rows
|
|
83986
|
+
* straight into the map without either, so a hand-edited store, a partial
|
|
83987
|
+
* migration or a row written by another build can. It matters because every
|
|
83988
|
+
* bare-type ref resolves through {@link getDefaultLocation}: on such a row
|
|
83989
|
+
* `storage.write({ location: 'eventMedia' })` and the migration coordinator's
|
|
83990
|
+
* drain destination both point at a disk the operator turned off. That is the
|
|
83991
|
+
* one thing this model must never do silently.
|
|
83992
|
+
*
|
|
83993
|
+
* Reported, never repaired: flipping an operator's flag back on at boot would
|
|
83994
|
+
* be the system overruling the switch instead of obeying it. The coordinator
|
|
83995
|
+
* refuses the drain (`storage-migration-coordinator.drain`); the bare-ref read
|
|
83996
|
+
* path is deliberately left working, because narrowing a READ is a worse bug
|
|
83997
|
+
* than the one this line reports.
|
|
83998
|
+
*/
|
|
83999
|
+
reportDisabledDefaults() {
|
|
84000
|
+
for (const loc of this.locations.values()) {
|
|
84001
|
+
if (!loc.isDefault || loc.enabled !== false) continue;
|
|
84002
|
+
this.logger.error("storage-orchestrator: the type DEFAULT is disabled \u2014 every bare-type ref for this type resolves to a location the operator turned off. Enable it, or make another location the default.", { meta: {
|
|
84003
|
+
id: loc.id,
|
|
84004
|
+
type: loc.type
|
|
84005
|
+
} });
|
|
84006
|
+
}
|
|
83799
84007
|
}
|
|
83800
84008
|
/**
|
|
83801
84009
|
* Inject the declaration-driven cardinality source. Called once by the
|
|
@@ -83867,6 +84075,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83867
84075
|
loaded: this.locations.size,
|
|
83868
84076
|
isSystemUpgraded: upgraded
|
|
83869
84077
|
} });
|
|
84078
|
+
this.reportDisabledDefaults();
|
|
83870
84079
|
}
|
|
83871
84080
|
/**
|
|
83872
84081
|
* Boot backfill (SP1): stamp `nodeId` on every persisted node-local
|
|
@@ -85178,7 +85387,7 @@ var require_system_config_addon = __commonJS({
|
|
|
85178
85387
|
[Symbol.toStringTag]: { value: "Module" }
|
|
85179
85388
|
});
|
|
85180
85389
|
require_chunk_Cek0wNdY();
|
|
85181
|
-
var require_dist10 =
|
|
85390
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
85182
85391
|
var SECTION_TITLES = {
|
|
85183
85392
|
server: "Server",
|
|
85184
85393
|
auth: "Authentication"
|
|
@@ -103239,7 +103448,7 @@ var require_winston_logging = __commonJS({
|
|
|
103239
103448
|
[Symbol.toStringTag]: { value: "Module" }
|
|
103240
103449
|
});
|
|
103241
103450
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
103242
|
-
var require_dist10 =
|
|
103451
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
103243
103452
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
103244
103453
|
var node_path = __require("path");
|
|
103245
103454
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -105182,9 +105391,9 @@ var require_event_category_BaEgqJNv = __commonJS({
|
|
|
105182
105391
|
}
|
|
105183
105392
|
});
|
|
105184
105393
|
|
|
105185
|
-
// ../types/dist/sleep-
|
|
105186
|
-
var
|
|
105187
|
-
"../types/dist/sleep-
|
|
105394
|
+
// ../types/dist/sleep-C3AniWy-.js
|
|
105395
|
+
var require_sleep_C3AniWy = __commonJS({
|
|
105396
|
+
"../types/dist/sleep-C3AniWy-.js"(exports) {
|
|
105188
105397
|
"use strict";
|
|
105189
105398
|
var require_event_category = require_event_category_BaEgqJNv();
|
|
105190
105399
|
var zod = require_zod();
|
|
@@ -107906,6 +108115,7 @@ var require_sleep_CWWLTM6W = __commonJS({
|
|
|
107906
108115
|
getEventMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getEventMedia", "query", input),
|
|
107907
108116
|
getTrackMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getTrackMedia", "query", input),
|
|
107908
108117
|
listTrackMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listTrackMedia", "query", input),
|
|
108118
|
+
listEventMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listEventMedia", "query", input),
|
|
107909
108119
|
searchObjectEvents: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "searchObjectEvents", "query", input),
|
|
107910
108120
|
wipeObjectEmbeddings: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "wipeObjectEmbeddings", "mutation", input),
|
|
107911
108121
|
rebuildObjectEmbeddings: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "rebuildObjectEmbeddings", "mutation", input),
|
|
@@ -108850,7 +109060,7 @@ var require_addon = __commonJS({
|
|
|
108850
109060
|
"use strict";
|
|
108851
109061
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
108852
109062
|
var require_event_category = require_event_category_BaEgqJNv();
|
|
108853
|
-
var require_sleep =
|
|
109063
|
+
var require_sleep = require_sleep_C3AniWy();
|
|
108854
109064
|
var require_err_msg = require_err_msg_COpsHMw2();
|
|
108855
109065
|
var CAP_INPUT_DEFAULTS = Object.freeze({
|
|
108856
109066
|
"addons": { "getLogs": { "limit": 100 } },
|
|
@@ -115732,12 +115942,12 @@ var require_dist2 = __commonJS({
|
|
|
115732
115942
|
}
|
|
115733
115943
|
});
|
|
115734
115944
|
|
|
115735
|
-
// ../system/dist/manifest-system-deps-
|
|
115736
|
-
var
|
|
115737
|
-
"../system/dist/manifest-system-deps-
|
|
115945
|
+
// ../system/dist/manifest-system-deps-DYv4ZPo2.js
|
|
115946
|
+
var require_manifest_system_deps_DYv4ZPo2 = __commonJS({
|
|
115947
|
+
"../system/dist/manifest-system-deps-DYv4ZPo2.js"(exports) {
|
|
115738
115948
|
"use strict";
|
|
115739
115949
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
115740
|
-
|
|
115950
|
+
require_dist_Keu5TDO7();
|
|
115741
115951
|
var node_crypto = __require("crypto");
|
|
115742
115952
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
115743
115953
|
var _camstack_types_node = require_node();
|
|
@@ -119845,6 +120055,174 @@ var require_manifest_system_deps_8boi90D9 = __commonJS({
|
|
|
119845
120055
|
createClient: (nodeId) => new UdsLocalTransportClient(localEndpointPath(nodeId))
|
|
119846
120056
|
};
|
|
119847
120057
|
}
|
|
120058
|
+
var DEFAULT_RETENTION_SECONDS = 300;
|
|
120059
|
+
var DEFAULT_MAX_TRIPLES = 4e3;
|
|
120060
|
+
var PARENT_ROUTED_PROVIDER_ADDON_ID = "(unresolved: parent-routed)";
|
|
120061
|
+
var CapUsageRegistry = class {
|
|
120062
|
+
retentionSeconds;
|
|
120063
|
+
maxTriples;
|
|
120064
|
+
map = /* @__PURE__ */ new Map();
|
|
120065
|
+
triples = 0;
|
|
120066
|
+
droppedTriples = 0;
|
|
120067
|
+
constructor(opts) {
|
|
120068
|
+
this.retentionSeconds = Math.max(1, opts?.retentionSeconds ?? DEFAULT_RETENTION_SECONDS);
|
|
120069
|
+
this.maxTriples = Math.max(1, opts?.maxTriples ?? DEFAULT_MAX_TRIPLES);
|
|
120070
|
+
}
|
|
120071
|
+
/**
|
|
120072
|
+
* Record one observed cap call.
|
|
120073
|
+
*
|
|
120074
|
+
* On the hot path: this runs once per cross-process cap call on hub-main's
|
|
120075
|
+
* event loop, which is the cluster's only queue (D181). Three Map lookups, an
|
|
120076
|
+
* integer increment, and — only when the wall clock has moved on — a bounded
|
|
120077
|
+
* clear. No allocation once a triple exists, and it NEVER throws into the
|
|
120078
|
+
* call it observes.
|
|
120079
|
+
*/
|
|
120080
|
+
recordCall(rec) {
|
|
120081
|
+
try {
|
|
120082
|
+
if (rec.callerAddonId === "" || rec.providerAddonId === "" || rec.capName === "") return;
|
|
120083
|
+
if (!Number.isFinite(rec.atMs) || rec.atMs < 0) return;
|
|
120084
|
+
const window2 = this.windowFor(rec);
|
|
120085
|
+
if (window2 === null) return;
|
|
120086
|
+
const sec = Math.floor(rec.atMs / 1e3);
|
|
120087
|
+
if (window2.lastCallAtMs !== 0) {
|
|
120088
|
+
const lastSec = Math.floor(window2.lastCallAtMs / 1e3);
|
|
120089
|
+
const advanced = Math.min(sec - lastSec, this.retentionSeconds);
|
|
120090
|
+
for (let k = 1; k <= advanced; k++) window2.buckets[(lastSec + k) % this.retentionSeconds] = 0;
|
|
120091
|
+
}
|
|
120092
|
+
const idx = sec % this.retentionSeconds;
|
|
120093
|
+
window2.buckets[idx] = (window2.buckets[idx] ?? 0) + 1;
|
|
120094
|
+
if (rec.atMs > window2.lastCallAtMs) window2.lastCallAtMs = rec.atMs;
|
|
120095
|
+
} catch {
|
|
120096
|
+
}
|
|
120097
|
+
}
|
|
120098
|
+
getGraph(opts) {
|
|
120099
|
+
const windowSeconds = Math.max(1, Math.min(opts.windowSeconds, this.retentionSeconds));
|
|
120100
|
+
const minMs = opts.nowMs - windowSeconds * 1e3;
|
|
120101
|
+
const nowSec = Math.floor(opts.nowMs / 1e3);
|
|
120102
|
+
const out = [];
|
|
120103
|
+
for (const [caller, byProvider] of this.map) for (const [provider, byCap] of byProvider) for (const [capName, window2] of byCap) {
|
|
120104
|
+
if (window2.lastCallAtMs < minMs) continue;
|
|
120105
|
+
const count = this.sumWindow(window2, nowSec, windowSeconds);
|
|
120106
|
+
if (count === 0) continue;
|
|
120107
|
+
const callsPerMin = count / windowSeconds * 60;
|
|
120108
|
+
out.push({
|
|
120109
|
+
callerAddonId: caller,
|
|
120110
|
+
providerAddonId: provider,
|
|
120111
|
+
capName,
|
|
120112
|
+
callsPerMin,
|
|
120113
|
+
lastCallAtMs: window2.lastCallAtMs
|
|
120114
|
+
});
|
|
120115
|
+
}
|
|
120116
|
+
return out;
|
|
120117
|
+
}
|
|
120118
|
+
/** See {@link CapUsageStats}. Cheap — three counters, no scan. */
|
|
120119
|
+
getStats() {
|
|
120120
|
+
return {
|
|
120121
|
+
triples: this.triples,
|
|
120122
|
+
maxTriples: this.maxTriples,
|
|
120123
|
+
droppedTriples: this.droppedTriples
|
|
120124
|
+
};
|
|
120125
|
+
}
|
|
120126
|
+
/** Test / diagnostic helper — drops all recorded calls. */
|
|
120127
|
+
clear() {
|
|
120128
|
+
this.map.clear();
|
|
120129
|
+
this.triples = 0;
|
|
120130
|
+
this.droppedTriples = 0;
|
|
120131
|
+
}
|
|
120132
|
+
/**
|
|
120133
|
+
* Sum the buckets covering `(nowSec - windowSeconds, nowSec]`.
|
|
120134
|
+
*
|
|
120135
|
+
* The scan starts at `lastSec`, never at `nowSec`. `recordCall` clears the
|
|
120136
|
+
* buckets the ring has advanced OVER, which by definition stops at the last
|
|
120137
|
+
* write: the buckets for the seconds since then still hold whatever the
|
|
120138
|
+
* previous pass left in them, and reading `nowSec` down would count it. A
|
|
120139
|
+
* triple that went quiet for a couple of minutes and then made one call would
|
|
120140
|
+
* report the burst it made five minutes earlier.
|
|
120141
|
+
*
|
|
120142
|
+
* No lower bound is needed beyond the window itself: every bucket at or below
|
|
120143
|
+
* `lastSec - retention` was cleared by the advance that reached `lastSec`, so
|
|
120144
|
+
* it reads zero anyway. The loop is bounded by `windowSeconds ≤ retention`.
|
|
120145
|
+
*/
|
|
120146
|
+
sumWindow(window2, nowSec, windowSeconds) {
|
|
120147
|
+
const lastSec = Math.floor(window2.lastCallAtMs / 1e3);
|
|
120148
|
+
const from = Math.min(nowSec, lastSec);
|
|
120149
|
+
const floorSec = nowSec - windowSeconds;
|
|
120150
|
+
let count = 0;
|
|
120151
|
+
for (let s = from; s > floorSec; s--) count += window2.buckets[s % this.retentionSeconds] ?? 0;
|
|
120152
|
+
return count;
|
|
120153
|
+
}
|
|
120154
|
+
/**
|
|
120155
|
+
* The ring for this triple, creating it if the ceiling allows. `null` means
|
|
120156
|
+
* the observation is dropped — counted, never silent (see
|
|
120157
|
+
* {@link CapUsageStats.droppedTriples}).
|
|
120158
|
+
*/
|
|
120159
|
+
windowFor(rec) {
|
|
120160
|
+
let byProvider = this.map.get(rec.callerAddonId);
|
|
120161
|
+
if (!byProvider) {
|
|
120162
|
+
byProvider = /* @__PURE__ */ new Map();
|
|
120163
|
+
this.map.set(rec.callerAddonId, byProvider);
|
|
120164
|
+
}
|
|
120165
|
+
let byCap = byProvider.get(rec.providerAddonId);
|
|
120166
|
+
if (!byCap) {
|
|
120167
|
+
byCap = /* @__PURE__ */ new Map();
|
|
120168
|
+
byProvider.set(rec.providerAddonId, byCap);
|
|
120169
|
+
}
|
|
120170
|
+
const existing = byCap.get(rec.capName);
|
|
120171
|
+
if (existing) return existing;
|
|
120172
|
+
if (this.triples >= this.maxTriples) {
|
|
120173
|
+
this.reclaimExpired(rec.atMs);
|
|
120174
|
+
if (this.triples >= this.maxTriples) {
|
|
120175
|
+
this.droppedTriples++;
|
|
120176
|
+
return null;
|
|
120177
|
+
}
|
|
120178
|
+
byProvider = this.map.get(rec.callerAddonId);
|
|
120179
|
+
if (!byProvider) {
|
|
120180
|
+
byProvider = /* @__PURE__ */ new Map();
|
|
120181
|
+
this.map.set(rec.callerAddonId, byProvider);
|
|
120182
|
+
}
|
|
120183
|
+
byCap = byProvider.get(rec.providerAddonId);
|
|
120184
|
+
if (!byCap) {
|
|
120185
|
+
byCap = /* @__PURE__ */ new Map();
|
|
120186
|
+
byProvider.set(rec.providerAddonId, byCap);
|
|
120187
|
+
}
|
|
120188
|
+
}
|
|
120189
|
+
const created = {
|
|
120190
|
+
buckets: new Uint32Array(this.retentionSeconds),
|
|
120191
|
+
lastCallAtMs: 0
|
|
120192
|
+
};
|
|
120193
|
+
byCap.set(rec.capName, created);
|
|
120194
|
+
this.triples++;
|
|
120195
|
+
return created;
|
|
120196
|
+
}
|
|
120197
|
+
/**
|
|
120198
|
+
* Drop every triple whose whole ring has aged out — its last call is older
|
|
120199
|
+
* than the retention window, so it can contribute to no query. O(triples),
|
|
120200
|
+
* and only reachable when the ceiling is full, so it is amortised away by the
|
|
120201
|
+
* admissions it enables.
|
|
120202
|
+
*/
|
|
120203
|
+
reclaimExpired(nowMs) {
|
|
120204
|
+
const cutoff = nowMs - this.retentionSeconds * 1e3;
|
|
120205
|
+
for (const [caller, byProvider] of this.map) {
|
|
120206
|
+
for (const [provider, byCap] of byProvider) {
|
|
120207
|
+
for (const [capName, window2] of byCap) {
|
|
120208
|
+
if (window2.lastCallAtMs >= cutoff) continue;
|
|
120209
|
+
byCap.delete(capName);
|
|
120210
|
+
this.triples--;
|
|
120211
|
+
}
|
|
120212
|
+
if (byCap.size === 0) byProvider.delete(provider);
|
|
120213
|
+
}
|
|
120214
|
+
if (byProvider.size === 0) this.map.delete(caller);
|
|
120215
|
+
}
|
|
120216
|
+
}
|
|
120217
|
+
};
|
|
120218
|
+
var singleton = null;
|
|
120219
|
+
function getCapUsageRegistry() {
|
|
120220
|
+
if (!singleton) singleton = new CapUsageRegistry();
|
|
120221
|
+
return singleton;
|
|
120222
|
+
}
|
|
120223
|
+
function __resetCapUsageRegistryForTests() {
|
|
120224
|
+
singleton = null;
|
|
120225
|
+
}
|
|
119848
120226
|
function createSharedBusState(retainRecent = false) {
|
|
119849
120227
|
return {
|
|
119850
120228
|
handlers: /* @__PURE__ */ new Map(),
|
|
@@ -120391,6 +120769,8 @@ var require_manifest_system_deps_8boi90D9 = __commonJS({
|
|
|
120391
120769
|
isAddonPinnedCall;
|
|
120392
120770
|
/** See {@link LocalChildRegistryOptions.capTimeoutMs}. */
|
|
120393
120771
|
capTimeoutMs;
|
|
120772
|
+
/** See {@link LocalChildRegistryOptions.capUsageObserver}. */
|
|
120773
|
+
capUsageObserver;
|
|
120394
120774
|
/** Tracks capNames already logged as UDS-routed; one INFO line per capName per process. */
|
|
120395
120775
|
egressRoutedCaps = /* @__PURE__ */ new Set();
|
|
120396
120776
|
/** Active event fan-out mode, read once from `CAMSTACK_UDS_EVENT_FANOUT`. */
|
|
@@ -120422,6 +120802,7 @@ var require_manifest_system_deps_8boi90D9 = __commonJS({
|
|
|
120422
120802
|
this.isAggregatedCollectionMethod = opts.isAggregatedCollectionMethod;
|
|
120423
120803
|
this.isAddonPinnedCall = opts.isAddonPinnedCall;
|
|
120424
120804
|
this.capTimeoutMs = opts.capTimeoutMs;
|
|
120805
|
+
this.capUsageObserver = opts.capUsageObserver;
|
|
120425
120806
|
} else {
|
|
120426
120807
|
this.server = serverOrOptions;
|
|
120427
120808
|
this.onUnownedCall = onUnownedCallArg;
|
|
@@ -120588,6 +120969,30 @@ var require_manifest_system_deps_8boi90D9 = __commonJS({
|
|
|
120588
120969
|
}
|
|
120589
120970
|
return candidates[0];
|
|
120590
120971
|
}
|
|
120972
|
+
/**
|
|
120973
|
+
* Publish one cap-usage observation, if a sink is wired.
|
|
120974
|
+
*
|
|
120975
|
+
* Cost discipline — this is on the hot path (D181: hub-main's event loop is
|
|
120976
|
+
* the cluster's only queue): no sink means one undefined check; with a sink
|
|
120977
|
+
* it is one object literal and one `Date.now()`, and the sink itself is O(1).
|
|
120978
|
+
* Wrapped so a broken observer can never fail the call it observes — the
|
|
120979
|
+
* registry swallows too, and BOTH matter: this catch also covers a sink that
|
|
120980
|
+
* is not the registry.
|
|
120981
|
+
*/
|
|
120982
|
+
recordCapUsage(callerChildId, providerChildId, capName, methodName) {
|
|
120983
|
+
const sink = this.capUsageObserver;
|
|
120984
|
+
if (sink === void 0 || callerChildId === null) return;
|
|
120985
|
+
try {
|
|
120986
|
+
sink({
|
|
120987
|
+
callerAddonId: callerChildId,
|
|
120988
|
+
providerAddonId: providerChildId ?? "(unresolved: parent-routed)",
|
|
120989
|
+
capName,
|
|
120990
|
+
methodName,
|
|
120991
|
+
atMs: Date.now()
|
|
120992
|
+
});
|
|
120993
|
+
} catch {
|
|
120994
|
+
}
|
|
120995
|
+
}
|
|
120591
120996
|
/** First child whose cap manifest contains a descriptor matching `predicate`. */
|
|
120592
120997
|
findChildId(predicate) {
|
|
120593
120998
|
for (const entry of this.children.values()) if (entry.caps.some(predicate)) return entry.childId;
|
|
@@ -120857,7 +121262,9 @@ var require_manifest_system_deps_8boi90D9 = __commonJS({
|
|
|
120857
121262
|
const pinTargetsThisNode = pinnedNodeId !== void 0 && this.ownNodeId !== void 0 && pinnedNodeId === this.ownNodeId;
|
|
120858
121263
|
const aggregated = pinnedNodeId === void 0 && this.isAggregatedCollectionMethod?.(out.capName, out.method) === true;
|
|
120859
121264
|
const addonPinned = pinnedNodeId === void 0 && this.isAddonPinnedCall?.(out.capName, out.method, out.args) === true;
|
|
120860
|
-
|
|
121265
|
+
const target = !(out.native === true) && !aggregated && !addonPinned && (pinnedNodeId === void 0 || pinTargetsThisNode) ? this.resolveChildId(out.capName, out.deviceId) : null;
|
|
121266
|
+
this.recordCapUsage(childId, target, out.capName, out.method);
|
|
121267
|
+
if (target !== null) {
|
|
120861
121268
|
if (!this.egressRoutedCaps.has(out.capName)) {
|
|
120862
121269
|
this.egressRoutedCaps.add(out.capName);
|
|
120863
121270
|
this.logger?.info("routed child egress over UDS", { capName: out.capName });
|
|
@@ -121714,77 +122121,6 @@ var require_manifest_system_deps_8boi90D9 = __commonJS({
|
|
|
121714
122121
|
}
|
|
121715
122122
|
return links;
|
|
121716
122123
|
}
|
|
121717
|
-
var CapUsageRegistry = class {
|
|
121718
|
-
retentionSeconds;
|
|
121719
|
-
map = /* @__PURE__ */ new Map();
|
|
121720
|
-
constructor(opts) {
|
|
121721
|
-
this.retentionSeconds = Math.max(1, opts?.retentionSeconds ?? 300);
|
|
121722
|
-
}
|
|
121723
|
-
recordCall(rec) {
|
|
121724
|
-
try {
|
|
121725
|
-
if (rec.callerAddonId === "" || rec.providerAddonId === "" || rec.capName === "") return;
|
|
121726
|
-
if (!Number.isFinite(rec.atMs) || rec.atMs < 0) return;
|
|
121727
|
-
let byProvider = this.map.get(rec.callerAddonId);
|
|
121728
|
-
if (!byProvider) {
|
|
121729
|
-
byProvider = /* @__PURE__ */ new Map();
|
|
121730
|
-
this.map.set(rec.callerAddonId, byProvider);
|
|
121731
|
-
}
|
|
121732
|
-
let byCap = byProvider.get(rec.providerAddonId);
|
|
121733
|
-
if (!byCap) {
|
|
121734
|
-
byCap = /* @__PURE__ */ new Map();
|
|
121735
|
-
byProvider.set(rec.providerAddonId, byCap);
|
|
121736
|
-
}
|
|
121737
|
-
let window2 = byCap.get(rec.capName);
|
|
121738
|
-
if (!window2) {
|
|
121739
|
-
window2 = {
|
|
121740
|
-
buckets: Array.from({ length: this.retentionSeconds }, () => 0),
|
|
121741
|
-
lastCallAtMs: 0
|
|
121742
|
-
};
|
|
121743
|
-
byCap.set(rec.capName, window2);
|
|
121744
|
-
}
|
|
121745
|
-
const bucketIdx = Math.floor(rec.atMs / 1e3) % this.retentionSeconds;
|
|
121746
|
-
window2.buckets[bucketIdx] = (window2.buckets[bucketIdx] ?? 0) + 1;
|
|
121747
|
-
if (rec.atMs > window2.lastCallAtMs) window2.lastCallAtMs = rec.atMs;
|
|
121748
|
-
} catch {
|
|
121749
|
-
}
|
|
121750
|
-
}
|
|
121751
|
-
getGraph(opts) {
|
|
121752
|
-
const windowSeconds = Math.max(1, Math.min(opts.windowSeconds, this.retentionSeconds));
|
|
121753
|
-
const minMs = opts.nowMs - windowSeconds * 1e3;
|
|
121754
|
-
const out = [];
|
|
121755
|
-
for (const [caller, byProvider] of this.map) for (const [provider, byCap] of byProvider) for (const [capName, window2] of byCap) {
|
|
121756
|
-
if (window2.lastCallAtMs < minMs) continue;
|
|
121757
|
-
const nowBucket = Math.floor(opts.nowMs / 1e3);
|
|
121758
|
-
let count = 0;
|
|
121759
|
-
for (let i = 0; i < windowSeconds; i++) {
|
|
121760
|
-
const b = (nowBucket - i + this.retentionSeconds * 1e6) % this.retentionSeconds;
|
|
121761
|
-
count += window2.buckets[b] ?? 0;
|
|
121762
|
-
}
|
|
121763
|
-
if (count === 0) continue;
|
|
121764
|
-
const callsPerMin = count / windowSeconds * 60;
|
|
121765
|
-
out.push({
|
|
121766
|
-
callerAddonId: caller,
|
|
121767
|
-
providerAddonId: provider,
|
|
121768
|
-
capName,
|
|
121769
|
-
callsPerMin,
|
|
121770
|
-
lastCallAtMs: window2.lastCallAtMs
|
|
121771
|
-
});
|
|
121772
|
-
}
|
|
121773
|
-
return out;
|
|
121774
|
-
}
|
|
121775
|
-
/** Test / diagnostic helper — drops all recorded calls. */
|
|
121776
|
-
clear() {
|
|
121777
|
-
this.map.clear();
|
|
121778
|
-
}
|
|
121779
|
-
};
|
|
121780
|
-
var singleton = null;
|
|
121781
|
-
function getCapUsageRegistry() {
|
|
121782
|
-
if (!singleton) singleton = new CapUsageRegistry();
|
|
121783
|
-
return singleton;
|
|
121784
|
-
}
|
|
121785
|
-
function __resetCapUsageRegistryForTests() {
|
|
121786
|
-
singleton = null;
|
|
121787
|
-
}
|
|
121788
122124
|
function safeExistsSync(path) {
|
|
121789
122125
|
try {
|
|
121790
122126
|
return node_fs.existsSync(path);
|
|
@@ -123220,6 +123556,12 @@ var require_manifest_system_deps_8boi90D9 = __commonJS({
|
|
|
123220
123556
|
return NATIVE_PROVIDER_SERVICE_INFIX;
|
|
123221
123557
|
}
|
|
123222
123558
|
});
|
|
123559
|
+
Object.defineProperty(exports, "PARENT_ROUTED_PROVIDER_ADDON_ID", {
|
|
123560
|
+
enumerable: true,
|
|
123561
|
+
get: function() {
|
|
123562
|
+
return PARENT_ROUTED_PROVIDER_ADDON_ID;
|
|
123563
|
+
}
|
|
123564
|
+
});
|
|
123223
123565
|
Object.defineProperty(exports, "RSS_BUDGET_REANNOUNCE_MIN_MS", {
|
|
123224
123566
|
enumerable: true,
|
|
123225
123567
|
get: function() {
|
|
@@ -127634,7 +127976,7 @@ var require_dist3 = __commonJS({
|
|
|
127634
127976
|
"use strict";
|
|
127635
127977
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
127636
127978
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
127637
|
-
var require_dist10 =
|
|
127979
|
+
var require_dist10 = require_dist_Keu5TDO7();
|
|
127638
127980
|
var require_builtins_alerts_alerts_addon = require_alerts_addon();
|
|
127639
127981
|
require_alerts();
|
|
127640
127982
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
@@ -127660,7 +128002,7 @@ var require_dist3 = __commonJS({
|
|
|
127660
128002
|
var require_builtins_winston_logging_index = require_winston_logging();
|
|
127661
128003
|
var require_file_data_plane = require_file_data_plane_DO8KbxCe();
|
|
127662
128004
|
var require_tls$1 = require_tls_BxQlomxd();
|
|
127663
|
-
var require_manifest_system_deps =
|
|
128005
|
+
var require_manifest_system_deps = require_manifest_system_deps_DYv4ZPo2();
|
|
127664
128006
|
var require_resource_monitor = require_resource_monitor_CdnzxBLP();
|
|
127665
128007
|
var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
|
|
127666
128008
|
var zod = require_zod();
|
|
@@ -207872,6 +208214,19 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
207872
208214
|
* per-task abort timeout. On success the resolved `stagedPath` is returned; on
|
|
207873
208215
|
* failure the task is marked `failed` and `null` is returned (so the apply
|
|
207874
208216
|
* phase skips it). Other tasks are unaffected.
|
|
208217
|
+
*
|
|
208218
|
+
* **`toVersion` is REWRITTEN here, to the version that was actually
|
|
208219
|
+
* fetched.** A task's `toVersion` starts as the REQUEST, and the request is
|
|
208220
|
+
* routinely a dist-tag: `addons.updatePackage` with no version resolves to
|
|
208221
|
+
* the literal `'latest'`. The staging area already resolves it — it reads
|
|
208222
|
+
* the version out of the unpacked tarball and returns `resolvedVersion` —
|
|
208223
|
+
* and dropping that answer sent the string `'latest'` onward as if it were a
|
|
208224
|
+
* version. It became the manifest's recorded version, the version the Addons
|
|
208225
|
+
* page displayed, and the `toVersion` on the `addon.updated` event, which an
|
|
208226
|
+
* operator read on his phone as "@camstack/addon-terminal 0.0.0 → latest"
|
|
208227
|
+
* (2026-08-30). Everything downstream of the apply reads a version; none of
|
|
208228
|
+
* it can resolve a tag, and none of it could tell that it had been handed
|
|
208229
|
+
* one.
|
|
207875
208230
|
*/
|
|
207876
208231
|
async fetchAddonTask(job, task) {
|
|
207877
208232
|
try {
|
|
@@ -207881,20 +208236,28 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
207881
208236
|
});
|
|
207882
208237
|
const ac = new AbortController();
|
|
207883
208238
|
const timer = setTimeout(() => ac.abort(), this.deps.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS);
|
|
207884
|
-
let
|
|
208239
|
+
let staged;
|
|
207885
208240
|
try {
|
|
207886
|
-
|
|
208241
|
+
staged = await this.deps.staging.fetchAndStage({
|
|
207887
208242
|
jobId: job.jobId,
|
|
207888
208243
|
name: task.packageName,
|
|
207889
208244
|
version: task.toVersion,
|
|
207890
208245
|
signal: ac.signal
|
|
207891
|
-
})
|
|
208246
|
+
});
|
|
207892
208247
|
} finally {
|
|
207893
208248
|
clearTimeout(timer);
|
|
207894
208249
|
}
|
|
207895
|
-
|
|
208250
|
+
const stagedPath = staged.stagedPath;
|
|
208251
|
+
const resolved = {
|
|
208252
|
+
...task,
|
|
208253
|
+
toVersion: staged.resolvedVersion
|
|
208254
|
+
};
|
|
208255
|
+
this.advance(job.jobId, task, "staged", {
|
|
208256
|
+
stagedPath,
|
|
208257
|
+
toVersion: resolved.toVersion
|
|
208258
|
+
});
|
|
207896
208259
|
return {
|
|
207897
|
-
task,
|
|
208260
|
+
task: resolved,
|
|
207898
208261
|
stagedPath
|
|
207899
208262
|
};
|
|
207900
208263
|
} catch (err) {
|
|
@@ -208146,6 +208509,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
208146
208509
|
exports.NativeMetricsProvider = require_builtins_native_metrics_native_metrics_addon.NativeMetricsProvider;
|
|
208147
208510
|
exports.NetworkQualityTracker = NetworkQualityTracker;
|
|
208148
208511
|
exports.NotificationService = NotificationService;
|
|
208512
|
+
exports.PARENT_ROUTED_PROVIDER_ADDON_ID = require_manifest_system_deps.PARENT_ROUTED_PROVIDER_ADDON_ID;
|
|
208149
208513
|
Object.defineProperty(exports, "PYTHON_VERSION", {
|
|
208150
208514
|
enumerable: true,
|
|
208151
208515
|
get: function() {
|
|
@@ -208462,7 +208826,7 @@ var require_dist4 = __commonJS({
|
|
|
208462
208826
|
"use strict";
|
|
208463
208827
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
208464
208828
|
var require_event_category = require_event_category_BaEgqJNv();
|
|
208465
|
-
var require_sleep =
|
|
208829
|
+
var require_sleep = require_sleep_C3AniWy();
|
|
208466
208830
|
var require_canonical_hash = require_canonical_hash_DNV8S5ET();
|
|
208467
208831
|
var require_enums2 = require_enums();
|
|
208468
208832
|
var require_err_msg = require_err_msg_COpsHMw2();
|
|
@@ -209794,6 +210158,21 @@ var require_dist4 = __commonJS({
|
|
|
209794
210158
|
bytesMoved: zod.z.number().int(),
|
|
209795
210159
|
/** Total files discovered up front; null while (or when) unknown. */
|
|
209796
210160
|
filesTotal: zod.z.number().int().nullable(),
|
|
210161
|
+
/**
|
|
210162
|
+
* Rows this run CORRECTED while moving them — a durable mutation the move
|
|
210163
|
+
* made that nobody asked for, so it is reported where the operator reads the
|
|
210164
|
+
* job rather than only in a log line.
|
|
210165
|
+
*
|
|
210166
|
+
* A footage segment records its byte count in its own NAME, and the durable
|
|
210167
|
+
* hour row derives its aggregates from those names. A file that does not
|
|
210168
|
+
* match its name therefore makes the ledger's sums — and with them quota and
|
|
210169
|
+
* pressure eviction — wrong by the difference, and only a rename can fix it.
|
|
210170
|
+
* On 2026-08-30 one such row also stalled a 110 749-file drain permanently.
|
|
210171
|
+
*
|
|
210172
|
+
* Absent on lanes where the question has no meaning: a media blob's size is
|
|
210173
|
+
* in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
|
|
210174
|
+
*/
|
|
210175
|
+
rowsReconciled: zod.z.number().int().nonnegative().optional(),
|
|
209797
210176
|
startedAt: zod.z.number(),
|
|
209798
210177
|
finishedAt: zod.z.number().nullable(),
|
|
209799
210178
|
error: zod.z.string().nullable()
|
|
@@ -209836,11 +210215,18 @@ var require_dist4 = __commonJS({
|
|
|
209836
210215
|
/** Omitted = `move`, the pre-existing behaviour. */
|
|
209837
210216
|
mode: MediaRelocateModeSchema.optional()
|
|
209838
210217
|
});
|
|
209839
|
-
var
|
|
209840
|
-
|
|
209841
|
-
|
|
209842
|
-
total: zod.z.number().int().nonnegative()
|
|
210218
|
+
var UnstampedRowsSchema = zod.z.object({
|
|
210219
|
+
present: zod.z.boolean(),
|
|
210220
|
+
rows: zod.z.number().int().nonnegative().nullable()
|
|
209843
210221
|
});
|
|
210222
|
+
var UnstampedEventMediaCountSchema = zod.z.object({
|
|
210223
|
+
media: UnstampedRowsSchema,
|
|
210224
|
+
retrainFrames: UnstampedRowsSchema,
|
|
210225
|
+
/** True when EITHER collection holds one. The refusal reads this. */
|
|
210226
|
+
anyPresent: zod.z.boolean(),
|
|
210227
|
+
/** Sum across both, or `null` when either lane could not be counted. */
|
|
210228
|
+
total: zod.z.number().int().nonnegative().nullable()
|
|
210229
|
+
}).nullable();
|
|
209844
210230
|
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: zod.z.string().min(1) });
|
|
209845
210231
|
var StorageMigrationClassSchema = zod.z.enum([
|
|
209846
210232
|
"recordings",
|
|
@@ -209887,6 +210273,10 @@ var require_dist4 = __commonJS({
|
|
|
209887
210273
|
/** The archive census — the **M** of "N of M" (D295). `null` = unknowable. */
|
|
209888
210274
|
filesTotal: zod.z.number().int().nonnegative().nullable(),
|
|
209889
210275
|
bytesMoved: zod.z.number().int().nonnegative(),
|
|
210276
|
+
/** Rows the mover corrected while moving them — see `RelocateJob`. Absent on
|
|
210277
|
+
* a lane that cannot reconcile. A migration that silently rewrote durable
|
|
210278
|
+
* rows would be the same failure as one that silently skipped them. */
|
|
210279
|
+
rowsReconciled: zod.z.number().int().nonnegative().optional(),
|
|
209890
210280
|
/** The MOVER's start, not the migration's: a drain restarted after an addon
|
|
209891
210281
|
* crash gets a new mover, and a rate computed from the migration's start
|
|
209892
210282
|
* would silently average in the time nothing was running. */
|
|
@@ -216211,6 +216601,15 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
216211
216601
|
* calls are sync. Bindings change rarely (only on wrapper toggle or
|
|
216212
216602
|
* device add/remove) — clients invalidate via the
|
|
216213
216603
|
* `capability.binding-changed` event.
|
|
216604
|
+
*
|
|
216605
|
+
* "A single round-trip" describes the CLIENT's side and used not to
|
|
216606
|
+
* describe the server's: until 2026-08-30 the resolver read the persisted
|
|
216607
|
+
* wrapper activations once per device, so answering this cost one
|
|
216608
|
+
* settings-door RPC per device — 1 020 on the live 1 019-device hub, and
|
|
216609
|
+
* it did not return in 240 s against `SystemMirror.init`'s 15 s budget.
|
|
216610
|
+
* The server side is now two reads for the whole fleet. Anything PERIODIC
|
|
216611
|
+
* still belongs on `getBindings` / `getBindingsBatch` (D12); this remains
|
|
216612
|
+
* a warm seed.
|
|
216214
216613
|
*/
|
|
216215
216614
|
getAllBindings: require_sleep.method(zod.z.object({}), zod.z.array(DeviceBindingsForDeviceSchema)),
|
|
216216
216615
|
/**
|
|
@@ -216609,6 +217008,80 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
216609
217008
|
getInfo: require_sleep.method(zod.z.void(), EmbeddingInfoSchema, { auth: "admin" })
|
|
216610
217009
|
}
|
|
216611
217010
|
};
|
|
217011
|
+
var FailureReasonCountSchema = zod.z.object({
|
|
217012
|
+
/**
|
|
217013
|
+
* Why the attempt did not land, in the contributor's own vocabulary —
|
|
217014
|
+
* `worker-lease-gone`, `queue-overflow`, `timeout`, `empty-read`. The same
|
|
217015
|
+
* strings that already appear in this repo's logs and, where one exists, the
|
|
217016
|
+
* same string the per-track `previewMissReason` records (D276): a second
|
|
217017
|
+
* vocabulary for the same loss would make the row and the counter
|
|
217018
|
+
* un-joinable.
|
|
217019
|
+
*/
|
|
217020
|
+
reason: zod.z.string(),
|
|
217021
|
+
count: zod.z.number().int().nonnegative()
|
|
217022
|
+
});
|
|
217023
|
+
var FailureContributionSchema = zod.z.object({
|
|
217024
|
+
/**
|
|
217025
|
+
* The failing path — `enrichment-crop`, `inference`, `plate-ocr`,
|
|
217026
|
+
* `person-over-vehicle`. Free text, for the reason `load-contribution` keeps
|
|
217027
|
+
* `unit` free: the families are owned by different addons and a shared enum
|
|
217028
|
+
* is a central list that rots invisibly.
|
|
217029
|
+
*/
|
|
217030
|
+
family: zod.z.string(),
|
|
217031
|
+
/**
|
|
217032
|
+
* The NUMERIC device id — the same value every log line carries as
|
|
217033
|
+
* `tags.deviceId`. Never nullable and never absent: a contributor that
|
|
217034
|
+
* cannot name the camera must not emit the entry, because a fleet total
|
|
217035
|
+
* cannot answer the only question anybody asks of this surface.
|
|
217036
|
+
*/
|
|
217037
|
+
deviceId: zod.z.number().int().positive(),
|
|
217038
|
+
/**
|
|
217039
|
+
* A second dimension inside the family: the model / step id for an inference
|
|
217040
|
+
* timeout, so "which camera AND which model" is one read. Absent when the
|
|
217041
|
+
* family has a single variant.
|
|
217042
|
+
*/
|
|
217043
|
+
variant: zod.z.string().optional(),
|
|
217044
|
+
/**
|
|
217045
|
+
* Epoch ms this counter started — the INCARNATION MARKER. A consumer
|
|
217046
|
+
* differencing two reads must drop the interval when it changes, because the
|
|
217047
|
+
* counter restarted from zero in a respawned runner. Same discipline as
|
|
217048
|
+
* `LoadContribution.startedAtMs`.
|
|
217049
|
+
*/
|
|
217050
|
+
sinceMs: zod.z.number(),
|
|
217051
|
+
/** Epoch ms it was read. `atMs - sinceMs` is the interval this covers. */
|
|
217052
|
+
atMs: zod.z.number(),
|
|
217053
|
+
/**
|
|
217054
|
+
* THE DENOMINATOR — every attempt on this path for this camera in the
|
|
217055
|
+
* window. A failure count published without it is the mistake this schema
|
|
217056
|
+
* exists to make impossible.
|
|
217057
|
+
*/
|
|
217058
|
+
attempts: zod.z.number().int().nonnegative(),
|
|
217059
|
+
/** Attempts that landed. `attempts - succeeded` is the loss. */
|
|
217060
|
+
succeeded: zod.z.number().int().nonnegative(),
|
|
217061
|
+
/** The loss, partitioned. Sums to `attempts - succeeded`. */
|
|
217062
|
+
reasons: zod.z.array(FailureReasonCountSchema).readonly()
|
|
217063
|
+
});
|
|
217064
|
+
var failureContributionCapability = {
|
|
217065
|
+
name: "failure-contribution",
|
|
217066
|
+
scope: "system",
|
|
217067
|
+
mode: "collection",
|
|
217068
|
+
internal: true,
|
|
217069
|
+
methods: {
|
|
217070
|
+
/**
|
|
217071
|
+
* This addon's per-camera failure counters, read live from bounded in-RAM
|
|
217072
|
+
* state it already keeps. Inert: no persistence, no sampling, no timer.
|
|
217073
|
+
*
|
|
217074
|
+
* READING NEVER RESETS. The counters are CUMULATIVE since `sinceMs`, and a
|
|
217075
|
+
* consumer that wants a rate differences two reads. A draining read would
|
|
217076
|
+
* make two operators with the page open each destroy half of the other's
|
|
217077
|
+
* numbers, and `load-contribution` already settled the same question the
|
|
217078
|
+
* same way for `cpuSeconds`.
|
|
217079
|
+
*/
|
|
217080
|
+
list: require_sleep.method(zod.z.void(), zod.z.array(FailureContributionSchema).readonly())
|
|
217081
|
+
},
|
|
217082
|
+
/** In-process only — enumerated through `addons.listCapabilityProviders`. */
|
|
217083
|
+
mount: { kind: "skip" }
|
|
217084
|
+
};
|
|
216612
217085
|
var DirEntrySchema = zod.z.object({
|
|
216613
217086
|
name: zod.z.string(),
|
|
216614
217087
|
path: zod.z.string()
|
|
@@ -217153,145 +217626,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
217153
217626
|
})
|
|
217154
217627
|
}
|
|
217155
217628
|
};
|
|
217156
|
-
var LogChannelApplyResultSchema = zod.z.object({
|
|
217157
|
-
/** How many declared channels are armed in this process after the call. */
|
|
217158
|
-
armed: zod.z.number().int().min(0),
|
|
217159
|
-
/**
|
|
217160
|
-
* Names the document armed that this process does not declare. Reported
|
|
217161
|
-
* rather than swallowed: a name here is either a typo or an addon that has
|
|
217162
|
-
* not booted, and both deserve a line instead of silence.
|
|
217163
|
-
*/
|
|
217164
|
-
unknown: zod.z.array(zod.z.string()).readonly()
|
|
217165
|
-
});
|
|
217166
|
-
var logChannelsCapability = {
|
|
217167
|
-
name: "log-channels",
|
|
217168
|
-
scope: "system",
|
|
217169
|
-
mode: "collection",
|
|
217170
|
-
internal: true,
|
|
217171
|
-
methods: {
|
|
217172
|
-
/** The channels this addon declares. Inert: no value, no state. */
|
|
217173
|
-
list: require_sleep.method(zod.z.void(), zod.z.array(LogChannelDescriptorSchema).readonly()),
|
|
217174
|
-
/**
|
|
217175
|
-
* Refresh this process's mirror from the document's FULL set of armed
|
|
217176
|
-
* windows.
|
|
217177
|
-
*
|
|
217178
|
-
* Full and not incremental on purpose: the document is the authority, so a
|
|
217179
|
-
* channel it does not name is disarmed here. An incremental apply would
|
|
217180
|
-
* let a disarm get lost in transit and leave a channel running that
|
|
217181
|
-
* nobody can see is running.
|
|
217182
|
-
*/
|
|
217183
|
-
apply: require_sleep.method(zod.z.object({ windows: zod.z.array(LogChannelWindowSchema).readonly() }), LogChannelApplyResultSchema, { kind: "mutation" })
|
|
217184
|
-
},
|
|
217185
|
-
/** In-process only — enumerated through `addons.listCapabilityProviders`. */
|
|
217186
|
-
mount: { kind: "skip" }
|
|
217187
|
-
};
|
|
217188
|
-
var LogLevelSchema = zod.z.enum([
|
|
217189
|
-
"debug",
|
|
217190
|
-
"info",
|
|
217191
|
-
"warn",
|
|
217192
|
-
"error"
|
|
217193
|
-
]);
|
|
217194
|
-
var LogEntrySchema = zod.z.object({
|
|
217195
|
-
timestamp: zod.z.date(),
|
|
217196
|
-
level: LogLevelSchema,
|
|
217197
|
-
scope: zod.z.array(zod.z.string()),
|
|
217198
|
-
message: zod.z.string(),
|
|
217199
|
-
meta: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
|
|
217200
|
-
tags: zod.z.record(zod.z.string(), zod.z.string()).optional()
|
|
217201
|
-
});
|
|
217202
|
-
var logDestinationCapability = {
|
|
217203
|
-
name: "log-destination",
|
|
217204
|
-
scope: "system",
|
|
217205
|
-
mode: "collection",
|
|
217206
|
-
internal: true,
|
|
217207
|
-
methods: {
|
|
217208
|
-
write: require_sleep.method(LogEntrySchema, zod.z.void(), { kind: "mutation" }),
|
|
217209
|
-
query: require_sleep.method(zod.z.object({
|
|
217210
|
-
scope: zod.z.array(zod.z.string()).optional(),
|
|
217211
|
-
level: LogLevelSchema.optional(),
|
|
217212
|
-
since: zod.z.date().optional(),
|
|
217213
|
-
until: zod.z.date().optional(),
|
|
217214
|
-
limit: zod.z.number().optional(),
|
|
217215
|
-
tags: zod.z.record(zod.z.string(), zod.z.string()).optional()
|
|
217216
|
-
}), zod.z.array(LogEntrySchema).readonly())
|
|
217217
|
-
},
|
|
217218
|
-
/** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
|
|
217219
|
-
mount: { kind: "skip" }
|
|
217220
|
-
};
|
|
217221
|
-
var FailureReasonCountSchema = zod.z.object({
|
|
217222
|
-
/**
|
|
217223
|
-
* Why the attempt did not land, in the contributor's own vocabulary —
|
|
217224
|
-
* `worker-lease-gone`, `queue-overflow`, `timeout`, `empty-read`. The same
|
|
217225
|
-
* strings that already appear in this repo's logs and, where one exists, the
|
|
217226
|
-
* same string the per-track `previewMissReason` records (D276): a second
|
|
217227
|
-
* vocabulary for the same loss would make the row and the counter
|
|
217228
|
-
* un-joinable.
|
|
217229
|
-
*/
|
|
217230
|
-
reason: zod.z.string(),
|
|
217231
|
-
count: zod.z.number().int().nonnegative()
|
|
217232
|
-
});
|
|
217233
|
-
var FailureContributionSchema = zod.z.object({
|
|
217234
|
-
/**
|
|
217235
|
-
* The failing path — `enrichment-crop`, `inference`, `plate-ocr`,
|
|
217236
|
-
* `person-over-vehicle`. Free text, for the reason `load-contribution` keeps
|
|
217237
|
-
* `unit` free: the families are owned by different addons and a shared enum
|
|
217238
|
-
* is a central list that rots invisibly.
|
|
217239
|
-
*/
|
|
217240
|
-
family: zod.z.string(),
|
|
217241
|
-
/**
|
|
217242
|
-
* The NUMERIC device id — the same value every log line carries as
|
|
217243
|
-
* `tags.deviceId`. Never nullable and never absent: a contributor that
|
|
217244
|
-
* cannot name the camera must not emit the entry, because a fleet total
|
|
217245
|
-
* cannot answer the only question anybody asks of this surface.
|
|
217246
|
-
*/
|
|
217247
|
-
deviceId: zod.z.number().int().positive(),
|
|
217248
|
-
/**
|
|
217249
|
-
* A second dimension inside the family: the model / step id for an inference
|
|
217250
|
-
* timeout, so "which camera AND which model" is one read. Absent when the
|
|
217251
|
-
* family has a single variant.
|
|
217252
|
-
*/
|
|
217253
|
-
variant: zod.z.string().optional(),
|
|
217254
|
-
/**
|
|
217255
|
-
* Epoch ms this counter started — the INCARNATION MARKER. A consumer
|
|
217256
|
-
* differencing two reads must drop the interval when it changes, because the
|
|
217257
|
-
* counter restarted from zero in a respawned runner. Same discipline as
|
|
217258
|
-
* `LoadContribution.startedAtMs`.
|
|
217259
|
-
*/
|
|
217260
|
-
sinceMs: zod.z.number(),
|
|
217261
|
-
/** Epoch ms it was read. `atMs - sinceMs` is the interval this covers. */
|
|
217262
|
-
atMs: zod.z.number(),
|
|
217263
|
-
/**
|
|
217264
|
-
* THE DENOMINATOR — every attempt on this path for this camera in the
|
|
217265
|
-
* window. A failure count published without it is the mistake this schema
|
|
217266
|
-
* exists to make impossible.
|
|
217267
|
-
*/
|
|
217268
|
-
attempts: zod.z.number().int().nonnegative(),
|
|
217269
|
-
/** Attempts that landed. `attempts - succeeded` is the loss. */
|
|
217270
|
-
succeeded: zod.z.number().int().nonnegative(),
|
|
217271
|
-
/** The loss, partitioned. Sums to `attempts - succeeded`. */
|
|
217272
|
-
reasons: zod.z.array(FailureReasonCountSchema).readonly()
|
|
217273
|
-
});
|
|
217274
|
-
var failureContributionCapability = {
|
|
217275
|
-
name: "failure-contribution",
|
|
217276
|
-
scope: "system",
|
|
217277
|
-
mode: "collection",
|
|
217278
|
-
internal: true,
|
|
217279
|
-
methods: {
|
|
217280
|
-
/**
|
|
217281
|
-
* This addon's per-camera failure counters, read live from bounded in-RAM
|
|
217282
|
-
* state it already keeps. Inert: no persistence, no sampling, no timer.
|
|
217283
|
-
*
|
|
217284
|
-
* READING NEVER RESETS. The counters are CUMULATIVE since `sinceMs`, and a
|
|
217285
|
-
* consumer that wants a rate differences two reads. A draining read would
|
|
217286
|
-
* make two operators with the page open each destroy half of the other's
|
|
217287
|
-
* numbers, and `load-contribution` already settled the same question the
|
|
217288
|
-
* same way for `cpuSeconds`.
|
|
217289
|
-
*/
|
|
217290
|
-
list: require_sleep.method(zod.z.void(), zod.z.array(FailureContributionSchema).readonly())
|
|
217291
|
-
},
|
|
217292
|
-
/** In-process only — enumerated through `addons.listCapabilityProviders`. */
|
|
217293
|
-
mount: { kind: "skip" }
|
|
217294
|
-
};
|
|
217295
217629
|
var LOAD_CONTRIBUTION_ROLES = [
|
|
217296
217630
|
"decode",
|
|
217297
217631
|
"transcode",
|
|
@@ -217371,6 +217705,71 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
217371
217705
|
/** In-process only — enumerated through `addons.listCapabilityProviders`. */
|
|
217372
217706
|
mount: { kind: "skip" }
|
|
217373
217707
|
};
|
|
217708
|
+
var LogChannelApplyResultSchema = zod.z.object({
|
|
217709
|
+
/** How many declared channels are armed in this process after the call. */
|
|
217710
|
+
armed: zod.z.number().int().min(0),
|
|
217711
|
+
/**
|
|
217712
|
+
* Names the document armed that this process does not declare. Reported
|
|
217713
|
+
* rather than swallowed: a name here is either a typo or an addon that has
|
|
217714
|
+
* not booted, and both deserve a line instead of silence.
|
|
217715
|
+
*/
|
|
217716
|
+
unknown: zod.z.array(zod.z.string()).readonly()
|
|
217717
|
+
});
|
|
217718
|
+
var logChannelsCapability = {
|
|
217719
|
+
name: "log-channels",
|
|
217720
|
+
scope: "system",
|
|
217721
|
+
mode: "collection",
|
|
217722
|
+
internal: true,
|
|
217723
|
+
methods: {
|
|
217724
|
+
/** The channels this addon declares. Inert: no value, no state. */
|
|
217725
|
+
list: require_sleep.method(zod.z.void(), zod.z.array(LogChannelDescriptorSchema).readonly()),
|
|
217726
|
+
/**
|
|
217727
|
+
* Refresh this process's mirror from the document's FULL set of armed
|
|
217728
|
+
* windows.
|
|
217729
|
+
*
|
|
217730
|
+
* Full and not incremental on purpose: the document is the authority, so a
|
|
217731
|
+
* channel it does not name is disarmed here. An incremental apply would
|
|
217732
|
+
* let a disarm get lost in transit and leave a channel running that
|
|
217733
|
+
* nobody can see is running.
|
|
217734
|
+
*/
|
|
217735
|
+
apply: require_sleep.method(zod.z.object({ windows: zod.z.array(LogChannelWindowSchema).readonly() }), LogChannelApplyResultSchema, { kind: "mutation" })
|
|
217736
|
+
},
|
|
217737
|
+
/** In-process only — enumerated through `addons.listCapabilityProviders`. */
|
|
217738
|
+
mount: { kind: "skip" }
|
|
217739
|
+
};
|
|
217740
|
+
var LogLevelSchema = zod.z.enum([
|
|
217741
|
+
"debug",
|
|
217742
|
+
"info",
|
|
217743
|
+
"warn",
|
|
217744
|
+
"error"
|
|
217745
|
+
]);
|
|
217746
|
+
var LogEntrySchema = zod.z.object({
|
|
217747
|
+
timestamp: zod.z.date(),
|
|
217748
|
+
level: LogLevelSchema,
|
|
217749
|
+
scope: zod.z.array(zod.z.string()),
|
|
217750
|
+
message: zod.z.string(),
|
|
217751
|
+
meta: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
|
|
217752
|
+
tags: zod.z.record(zod.z.string(), zod.z.string()).optional()
|
|
217753
|
+
});
|
|
217754
|
+
var logDestinationCapability = {
|
|
217755
|
+
name: "log-destination",
|
|
217756
|
+
scope: "system",
|
|
217757
|
+
mode: "collection",
|
|
217758
|
+
internal: true,
|
|
217759
|
+
methods: {
|
|
217760
|
+
write: require_sleep.method(LogEntrySchema, zod.z.void(), { kind: "mutation" }),
|
|
217761
|
+
query: require_sleep.method(zod.z.object({
|
|
217762
|
+
scope: zod.z.array(zod.z.string()).optional(),
|
|
217763
|
+
level: LogLevelSchema.optional(),
|
|
217764
|
+
since: zod.z.date().optional(),
|
|
217765
|
+
until: zod.z.date().optional(),
|
|
217766
|
+
limit: zod.z.number().optional(),
|
|
217767
|
+
tags: zod.z.record(zod.z.string(), zod.z.string()).optional()
|
|
217768
|
+
}), zod.z.array(LogEntrySchema).readonly())
|
|
217769
|
+
},
|
|
217770
|
+
/** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
|
|
217771
|
+
mount: { kind: "skip" }
|
|
217772
|
+
};
|
|
217374
217773
|
var LoginStageEnum = zod.z.enum(["primary", "second-factor"]);
|
|
217375
217774
|
var RedirectLoginMethodSchema = zod.z.object({
|
|
217376
217775
|
kind: zod.z.literal("redirect"),
|
|
@@ -222258,13 +222657,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
222258
222657
|
"keyFrameSmall",
|
|
222259
222658
|
"thumbnailSmall"
|
|
222260
222659
|
]);
|
|
222261
|
-
var
|
|
222660
|
+
var MediaFileRefSchema = zod.z.object({
|
|
222262
222661
|
key: zod.z.string(),
|
|
222263
222662
|
kind: MediaFileKindEnum,
|
|
222264
|
-
base64: zod.z.string(),
|
|
222265
222663
|
sizeBytes: zod.z.number(),
|
|
222266
222664
|
timestamp: zod.z.number()
|
|
222267
222665
|
});
|
|
222666
|
+
var MediaFileSchema = MediaFileRefSchema.extend({
|
|
222667
|
+
/** `/addon/<addonId>/event-media/<encoded stored key>`. Always present. */
|
|
222668
|
+
url: zod.z.string(),
|
|
222669
|
+
/** @deprecated Transitional — see the schema docblock. Use {@link url}. */
|
|
222670
|
+
base64: zod.z.string()
|
|
222671
|
+
});
|
|
222268
222672
|
var MediaFileInfoSchema = MediaFileSchema.omit({ base64: true });
|
|
222269
222673
|
var RetrainMacroClassSchema = zod.z.enum([
|
|
222270
222674
|
"person",
|
|
@@ -222996,6 +223400,17 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
222996
223400
|
* happens to stamp it. This count is what the migration planner's
|
|
222997
223401
|
* non-blocking gate reads; `relocateMedia({ mode: 'seal' })` is what drives
|
|
222998
223402
|
* it to zero.
|
|
223403
|
+
*
|
|
223404
|
+
* TWO indexed statements per collection, not a walk. It used to page the
|
|
223405
|
+
* whole collection at 200 rows per RPC ordered by an unindexed column, so
|
|
223406
|
+
* on the live hub — 1 254 576 rows — it hit the 60 s RPC deadline every
|
|
223407
|
+
* time it was called, and the migration it gates could never start. The
|
|
223408
|
+
* cheap question (`present`: is there at least one) is asked first and
|
|
223409
|
+
* separately from the expensive one (`rows`), because only the first has
|
|
223410
|
+
* to be answerable for the gate to do its job.
|
|
223411
|
+
*
|
|
223412
|
+
* **`null` is "not measurable", never zero** — at either level. An
|
|
223413
|
+
* unreadable collection must not read as a sealed one.
|
|
222999
223414
|
*/
|
|
223000
223415
|
countUnstampedEventMedia: require_sleep.method(zod.z.object({}), UnstampedEventMediaCountSchema, { auth: "admin" }),
|
|
223001
223416
|
/**
|
|
@@ -223319,6 +223734,26 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
223319
223734
|
deviceId: zod.z.number()
|
|
223320
223735
|
}), zod.z.array(MediaFileInfoSchema).readonly()),
|
|
223321
223736
|
/**
|
|
223737
|
+
* What media an EVENT has, without any of it — the twin `getEventMedia`
|
|
223738
|
+
* never had.
|
|
223739
|
+
*
|
|
223740
|
+
* `listTrackMedia` above got this treatment because a track's media is
|
|
223741
|
+
* 5-8 MB. An event's is worse per row, not better: an old-style event owns
|
|
223742
|
+
* a `crop` AND a native-resolution `fullFrameBoxed`, and the track DETAIL
|
|
223743
|
+
* modal — the one surface that legitimately shows the big kinds — unions
|
|
223744
|
+
* both listings to build its filmstrip. It then renders every tile from
|
|
223745
|
+
* the `event-media` plane by key and throws the bytes away. Measured on
|
|
223746
|
+
* the live hub: one event's `fullFrameBoxed` is 2 824 077 B, base64'd to
|
|
223747
|
+
* ~3.8 MB, allocated whole in hub-main's heap, for a list of keys.
|
|
223748
|
+
*
|
|
223749
|
+
* Same `deviceId` authorization subject as `getEventMedia`, and the same
|
|
223750
|
+
* rows — this is a projection of that method, never a different question.
|
|
223751
|
+
*/
|
|
223752
|
+
listEventMedia: require_sleep.method(zod.z.object({
|
|
223753
|
+
eventId: zod.z.string(),
|
|
223754
|
+
deviceId: zod.z.number()
|
|
223755
|
+
}), zod.z.array(MediaFileInfoSchema).readonly()),
|
|
223756
|
+
/**
|
|
223322
223757
|
* Search object events by text query using CLIP cosine similarity.
|
|
223323
223758
|
* Encodes `text` via the `embedding-encoder` cap, queries the
|
|
223324
223759
|
* `ObjectEmbeddingStore` with optional prefilters, ranks all matching
|
|
@@ -228781,7 +229216,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
228781
229216
|
var MediaFileLiteSchema$1 = zod.z.object({
|
|
228782
229217
|
key: zod.z.string(),
|
|
228783
229218
|
kind: zod.z.string(),
|
|
228784
|
-
|
|
229219
|
+
url: zod.z.string(),
|
|
228785
229220
|
sizeBytes: zod.z.number(),
|
|
228786
229221
|
timestamp: zod.z.number()
|
|
228787
229222
|
});
|
|
@@ -231676,7 +232111,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
231676
232111
|
var MediaFileLiteSchema = zod.z.object({
|
|
231677
232112
|
key: zod.z.string(),
|
|
231678
232113
|
kind: zod.z.string(),
|
|
231679
|
-
|
|
232114
|
+
url: zod.z.string(),
|
|
231680
232115
|
sizeBytes: zod.z.number(),
|
|
231681
232116
|
timestamp: zod.z.number()
|
|
231682
232117
|
});
|
|
@@ -241924,6 +242359,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
241924
242359
|
addonId: null,
|
|
241925
242360
|
access: "view"
|
|
241926
242361
|
},
|
|
242362
|
+
"pipelineAnalytics.listEventMedia": {
|
|
242363
|
+
capName: "pipeline-analytics",
|
|
242364
|
+
capScope: "device",
|
|
242365
|
+
addonId: null,
|
|
242366
|
+
access: "view"
|
|
242367
|
+
},
|
|
241927
242368
|
"pipelineAnalytics.listGroups": {
|
|
241928
242369
|
capName: "pipeline-analytics",
|
|
241929
242370
|
capScope: "device",
|
|
@@ -245805,6 +246246,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
245805
246246
|
form: "array",
|
|
245806
246247
|
optional: false
|
|
245807
246248
|
}],
|
|
246249
|
+
"pipelineAnalytics.listEventMedia": [{
|
|
246250
|
+
name: "deviceId",
|
|
246251
|
+
form: "single",
|
|
246252
|
+
optional: false
|
|
246253
|
+
}],
|
|
245808
246254
|
"pipelineAnalytics.listGroups": [{
|
|
245809
246255
|
name: "deviceIds",
|
|
245810
246256
|
form: "array",
|
|
@@ -250845,6 +251291,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
250845
251291
|
exports.MaskShapeKindSchema = MaskShapeKindSchema;
|
|
250846
251292
|
exports.MaskShapeSchema = MaskShapeSchema;
|
|
250847
251293
|
exports.MediaFileInfoSchema = MediaFileInfoSchema;
|
|
251294
|
+
exports.MediaFileRefSchema = MediaFileRefSchema;
|
|
250848
251295
|
exports.MediaFileSchema = MediaFileSchema;
|
|
250849
251296
|
exports.MediaPlayerRepeatSchema = MediaPlayerRepeatSchema;
|
|
250850
251297
|
exports.MediaPlayerStateSchema = MediaPlayerStateSchema;
|
|
@@ -251323,6 +251770,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
251323
251770
|
exports.UnifiedBrokerInfoSchema = BrokerInfoSchema$1;
|
|
251324
251771
|
exports.UnitConversionError = UnitConversionError;
|
|
251325
251772
|
exports.UnstampedEventMediaCountSchema = UnstampedEventMediaCountSchema;
|
|
251773
|
+
exports.UnstampedRowsSchema = UnstampedRowsSchema;
|
|
251326
251774
|
exports.UpdateIntegrationInputSchema = UpdateIntegrationInputSchema;
|
|
251327
251775
|
exports.UpdateStatusSchema = UpdateStatusSchema;
|
|
251328
251776
|
exports.UpdateUserInputSchema = UpdateUserInputSchema;
|
|
@@ -385633,6 +386081,14 @@ var require_main2 = __commonJS({
|
|
|
385633
386081
|
// cap). A pin to ANOTHER node still bypasses the sibling → onUnownedCall.
|
|
385634
386082
|
ownNodeId: agentNodeId,
|
|
385635
386083
|
onUnownedCall
|
|
386084
|
+
// NO `capUsageObserver` here, deliberately. `getCapUsageRegistry()` is a
|
|
386085
|
+
// per-process singleton and the reader (`nodes.getCapUsageGraph`) runs on
|
|
386086
|
+
// hub-main; recording in the agent would produce a map nobody ever reads,
|
|
386087
|
+
// which is exactly the shape that made this graph return `[]` for
|
|
386088
|
+
// months. An agent's addon traffic is a KNOWN blind spot of the graph —
|
|
386089
|
+
// see `LocalChildRegistryOptions.capUsageObserver`. Closing it needs the
|
|
386090
|
+
// caller identity to survive `$hub-cap-fwd`'s envelope, which today it
|
|
386091
|
+
// does not.
|
|
385636
386092
|
});
|
|
385637
386093
|
await agentUdsRegistry.start();
|
|
385638
386094
|
agentUdsRegistry.onChildRegistered((child) => {
|
|
@@ -402542,7 +402998,7 @@ var require_addon_package_service = __commonJS({
|
|
|
402542
402998
|
this.requireInstaller();
|
|
402543
402999
|
const addonInstaller = this.installer;
|
|
402544
403000
|
const previousVersion = this.getInstalledPackageVersion(name);
|
|
402545
|
-
const isFirstInstall =
|
|
403001
|
+
const isFirstInstall = previousVersion === null;
|
|
402546
403002
|
let result;
|
|
402547
403003
|
if (isFirstInstall) {
|
|
402548
403004
|
const r = await addonInstaller.installFromNpm(name, version);
|
|
@@ -402568,12 +403024,13 @@ var require_addon_package_service = __commonJS({
|
|
|
402568
403024
|
const targetDir = path.join(addonsDir, dirName);
|
|
402569
403025
|
const packageSpec = version ? `${name}@${version}` : name;
|
|
402570
403026
|
await installPackageFromNpm(packageSpec, targetDir);
|
|
402571
|
-
|
|
403027
|
+
const installedVersion = this.getInstalledPackageVersion(name);
|
|
403028
|
+
updatedVersion = installedVersion ?? "";
|
|
402572
403029
|
this.cachedUpdates = null;
|
|
402573
403030
|
this.logger.info("Core package updated -- restart required", {
|
|
402574
|
-
meta: { name, updatedVersion }
|
|
403031
|
+
meta: { name, updatedVersion: installedVersion }
|
|
402575
403032
|
});
|
|
402576
|
-
this.sendUpdateNotification(name,
|
|
403033
|
+
this.sendUpdateNotification(name, installedVersion);
|
|
402577
403034
|
return { success: true, version: updatedVersion, requiresRestart: true };
|
|
402578
403035
|
} catch (error) {
|
|
402579
403036
|
const msg = (0, types_1.errMsg)(error);
|
|
@@ -403260,36 +403717,39 @@ var require_addon_package_service = __commonJS({
|
|
|
403260
403717
|
// Private: general helpers
|
|
403261
403718
|
// =========================================================================
|
|
403262
403719
|
/**
|
|
403263
|
-
*
|
|
403264
|
-
*
|
|
403265
|
-
*
|
|
403720
|
+
* The version of `packageName` INSTALLED RIGHT NOW, or `null` when the hub
|
|
403721
|
+
* has no readable copy.
|
|
403722
|
+
*
|
|
403723
|
+
* Delegates to the installer, which is the one place that knows the on-disk
|
|
403724
|
+
* layout (`<addonsDir>/@camstack/<pkg>/`, with the pre-scope flat form as a
|
|
403725
|
+
* fallback). This method used to restate that path and got it wrong —
|
|
403726
|
+
* `packageName.replace(/^@camstack\//, '')` looked ONLY in the flat form, so
|
|
403727
|
+
* every scoped install missed. It then fell back to
|
|
403728
|
+
* `require.resolve('<pkg>/package.json')`, which cannot succeed either: an
|
|
403729
|
+
* addon is installed under the data directory, not in the hub's own
|
|
403730
|
+
* node_modules. Both misses landed on the literal `'0.0.0'`, and that string
|
|
403731
|
+
* was published on the `addon.updated` event as the version the operator had
|
|
403732
|
+
* been running ("@camstack/addon-terminal 0.0.0 → latest", 2026-08-30).
|
|
403733
|
+
*
|
|
403734
|
+
* `null`, not `'0.0.0'`: a version nobody installed is indistinguishable
|
|
403735
|
+
* from one that was, and the caller must be forced to decide what to say
|
|
403736
|
+
* about "unknown" rather than inherit a number that looks like an answer.
|
|
403266
403737
|
*/
|
|
403267
403738
|
getInstalledPackageVersion(packageName) {
|
|
403268
403739
|
try {
|
|
403269
|
-
const
|
|
403270
|
-
const
|
|
403271
|
-
|
|
403272
|
-
|
|
403273
|
-
const pkgJson = readJsonObject(pkgJsonPath);
|
|
403274
|
-
const v = asString(pkgJson?.["version"]);
|
|
403275
|
-
if (v)
|
|
403276
|
-
return v;
|
|
403277
|
-
}
|
|
403278
|
-
} catch (err) {
|
|
403279
|
-
this.logger.debug("Version lookup via fs failed, trying require.resolve", {
|
|
403280
|
-
meta: { packageName, error: (0, types_1.errMsg)(err) }
|
|
403281
|
-
});
|
|
403282
|
-
}
|
|
403283
|
-
try {
|
|
403284
|
-
const pkgJsonPath = __require.resolve(`${packageName}/package.json`);
|
|
403285
|
-
const pkgJson = readJsonObject(pkgJsonPath);
|
|
403286
|
-
return asString(pkgJson?.["version"], "0.0.0");
|
|
403740
|
+
const installed = this.installer?.getInstalledPackage(packageName) ?? null;
|
|
403741
|
+
const version = installed === null ? "" : asString(installed.version);
|
|
403742
|
+
if (version)
|
|
403743
|
+
return version;
|
|
403287
403744
|
} catch (err) {
|
|
403288
|
-
this.logger.debug("
|
|
403745
|
+
this.logger.debug("Installed-version lookup failed", {
|
|
403289
403746
|
meta: { packageName, error: (0, types_1.errMsg)(err) }
|
|
403290
403747
|
});
|
|
403291
|
-
return "0.0.0";
|
|
403292
403748
|
}
|
|
403749
|
+
this.logger.debug("No readable installed copy \u2014 previous version is unknown", {
|
|
403750
|
+
meta: { packageName }
|
|
403751
|
+
});
|
|
403752
|
+
return null;
|
|
403293
403753
|
}
|
|
403294
403754
|
/** Only allow @camstack/* scoped packages */
|
|
403295
403755
|
isAllowedPackage(name) {
|
|
@@ -403341,11 +403801,18 @@ var require_addon_package_service = __commonJS({
|
|
|
403341
403801
|
throw new Error("AddonInstaller is not available -- @camstack/system may not be installed");
|
|
403342
403802
|
}
|
|
403343
403803
|
}
|
|
403344
|
-
/**
|
|
403804
|
+
/**
|
|
403805
|
+
* Send notification and toast for a successful package update.
|
|
403806
|
+
*
|
|
403807
|
+
* `version === null` means the hub could not read back what it just
|
|
403808
|
+
* installed. The sentence then says the package was updated and stops —
|
|
403809
|
+
* never "updated to v0.0.0", which reads as a real version.
|
|
403810
|
+
*/
|
|
403345
403811
|
sendUpdateNotification(name, version) {
|
|
403812
|
+
const sentence = version === null ? `${name} updated` : `${name} updated to v${version}`;
|
|
403346
403813
|
this.notificationService.notify({
|
|
403347
403814
|
title: "Package Updated",
|
|
403348
|
-
body:
|
|
403815
|
+
body: sentence,
|
|
403349
403816
|
format: "text",
|
|
403350
403817
|
priority: 3,
|
|
403351
403818
|
category: "system",
|
|
@@ -403356,7 +403823,7 @@ var require_addon_package_service = __commonJS({
|
|
|
403356
403823
|
});
|
|
403357
403824
|
this.toastService.broadcast({
|
|
403358
403825
|
title: "Package Updated",
|
|
403359
|
-
message:
|
|
403826
|
+
message: sentence,
|
|
403360
403827
|
severity: "info",
|
|
403361
403828
|
duration: 5e3
|
|
403362
403829
|
});
|
|
@@ -410443,8 +410910,30 @@ var require_cap_providers = __commonJS({
|
|
|
410443
410910
|
}
|
|
410444
410911
|
return result;
|
|
410445
410912
|
},
|
|
410913
|
+
/**
|
|
410914
|
+
* Observed caller → provider → cap edges over the last `windowSeconds`.
|
|
410915
|
+
*
|
|
410916
|
+
* **Read the blind spots before drawing a conclusion.** This reads
|
|
410917
|
+
* hub-main's `CapUsageRegistry`, written by the hub's `LocalChildRegistry`
|
|
410918
|
+
* on every `cap-call-out` a hub-local forked addon sends. It therefore does
|
|
410919
|
+
* NOT contain: hub-main → hub-main in-process calls (one shared `ctx.api`
|
|
410920
|
+
* client, no per-caller identity), a runner's own co-located calls, or any
|
|
410921
|
+
* traffic originating on an agent. An edge missing here means "not seen on
|
|
410922
|
+
* this plane", never "did not happen" — the full list is on
|
|
410923
|
+
* `LocalChildRegistryOptions.capUsageObserver`.
|
|
410924
|
+
*/
|
|
410446
410925
|
getCapUsageGraph: async (input) => {
|
|
410447
410926
|
const reg = (0, system_1.getCapUsageRegistry)();
|
|
410927
|
+
const stats = reg.getStats();
|
|
410928
|
+
if (stats.droppedTriples > 0) {
|
|
410929
|
+
logger?.warn("cap-usage graph is truncated \u2014 the triple ceiling was reached", {
|
|
410930
|
+
meta: {
|
|
410931
|
+
triples: stats.triples,
|
|
410932
|
+
maxTriples: stats.maxTriples,
|
|
410933
|
+
droppedTriples: stats.droppedTriples
|
|
410934
|
+
}
|
|
410935
|
+
});
|
|
410936
|
+
}
|
|
410448
410937
|
return reg.getGraph({ windowSeconds: input.windowSeconds, nowMs: Date.now() });
|
|
410449
410938
|
},
|
|
410450
410939
|
setProcessLogLevel: async (input) => {
|
|
@@ -416873,6 +417362,7 @@ var require_addon_registry_service = __commonJS({
|
|
|
416873
417362
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
416874
417363
|
exports.AddonRegistryService = void 0;
|
|
416875
417364
|
exports.shouldEvictMissingOnDisk = shouldEvictMissingOnDisk;
|
|
417365
|
+
exports.addonUpdatePayload = addonUpdatePayload;
|
|
416876
417366
|
exports.shouldEmitProviderRegisteredReady = shouldEmitProviderRegisteredReady;
|
|
416877
417367
|
var node_crypto_1 = __require("crypto");
|
|
416878
417368
|
var fs = __importStar(__require("fs"));
|
|
@@ -416895,6 +417385,12 @@ var require_addon_registry_service = __commonJS({
|
|
|
416895
417385
|
function shouldEvictMissingOnDisk(entry, id, onDiskIds) {
|
|
416896
417386
|
return entry.source === "installed" && entry.packageName !== "@camstack/system" && !onDiskIds.has(id);
|
|
416897
417387
|
}
|
|
417388
|
+
function addonUpdatePayload(fromVersion, toVersion) {
|
|
417389
|
+
return {
|
|
417390
|
+
...fromVersion !== null ? { fromVersion } : {},
|
|
417391
|
+
toVersion
|
|
417392
|
+
};
|
|
417393
|
+
}
|
|
416898
417394
|
function isSettingsStore(backend) {
|
|
416899
417395
|
const required = [
|
|
416900
417396
|
"getSystem",
|
|
@@ -417970,14 +418466,17 @@ var require_addon_registry_service = __commonJS({
|
|
|
417970
418466
|
this.healthMonitor.forget(packageName);
|
|
417971
418467
|
this.addonLoader.clearLoadFailures(packageName);
|
|
417972
418468
|
}
|
|
417973
|
-
/**
|
|
418469
|
+
/**
|
|
418470
|
+
* Emit `addon.updated` for every addon belonging to a package.
|
|
418471
|
+
*
|
|
418472
|
+
* The payload shape is {@link addonUpdatePayload} — an unknown predecessor
|
|
418473
|
+
* is an ABSENT key, never a placeholder.
|
|
418474
|
+
*/
|
|
417974
418475
|
emitUpdateEvent(packageName, fromVersion, toVersion) {
|
|
418476
|
+
const payload = addonUpdatePayload(fromVersion, toVersion);
|
|
417975
418477
|
for (const [id, entry] of this.addonEntries) {
|
|
417976
418478
|
if (entry.packageName === packageName) {
|
|
417977
|
-
this.emitAddonLifecycleEvent("addon.updated", id,
|
|
417978
|
-
fromVersion,
|
|
417979
|
-
toVersion
|
|
417980
|
-
});
|
|
418479
|
+
this.emitAddonLifecycleEvent("addon.updated", id, payload);
|
|
417981
418480
|
}
|
|
417982
418481
|
}
|
|
417983
418482
|
}
|
|
@@ -421469,6 +421968,14 @@ var require_moleculer_service = __commonJS({
|
|
|
421469
421968
|
// below, so a method that survives 16 minutes across nodes survives it
|
|
421470
421969
|
// across a socket too — and one that hangs is cut off on both.
|
|
421471
421970
|
capTimeoutMs: (capName, method) => this.capabilityService.getRegistry()?.getDefinition(capName)?.methods?.[method]?.timeoutMs,
|
|
421971
|
+
// Feed the cap-usage graph from the one place that sees a forked
|
|
421972
|
+
// addon's outbound `ctx.api` traffic AND runs in the process that reads
|
|
421973
|
+
// it back (`nodes.getCapUsageGraph` → `cap-providers.ts`). The
|
|
421974
|
+
// runner-side observer in `addon-context-factory.ts` writes to the
|
|
421975
|
+
// RUNNER's own module singleton, which hub-main never sees — which is
|
|
421976
|
+
// why the graph answered `[]` on a hub running 64 addons. Blind spots
|
|
421977
|
+
// that remain are listed on `LocalChildRegistryOptions.capUsageObserver`.
|
|
421978
|
+
capUsageObserver: (obs) => (0, system_1.getCapUsageRegistry)().recordCall(obs),
|
|
421472
421979
|
logger: {
|
|
421473
421980
|
info: (msg, meta) => logger.info(msg, meta !== null && meta !== void 0 ? { meta } : void 0)
|
|
421474
421981
|
},
|