camstack 1.2.48 → 1.2.50
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-BVU5JADq.js
|
|
23637
|
+
var require_dist_BVU5JADq = __commonJS({
|
|
23638
|
+
"../system/dist/dist-BVU5JADq.js"(exports) {
|
|
23639
23639
|
"use strict";
|
|
23640
23640
|
var zod = require_zod();
|
|
23641
23641
|
var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
|
|
@@ -25904,6 +25904,46 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
25904
25904
|
function logLevelAtMost(level, threshold) {
|
|
25905
25905
|
return LOG_LEVEL_RANK[level] <= LOG_LEVEL_RANK[threshold];
|
|
25906
25906
|
}
|
|
25907
|
+
var LogChannelLevelSchema = zod.z.enum([
|
|
25908
|
+
"info",
|
|
25909
|
+
"warn",
|
|
25910
|
+
"error"
|
|
25911
|
+
]);
|
|
25912
|
+
var LogChannelDescriptorSchema = zod.z.object({
|
|
25913
|
+
/**
|
|
25914
|
+
* Dotted `area.thing`, unique across the workspace. `area` is conventionally
|
|
25915
|
+
* the addon's short name so an operator reading a channel list can tell who
|
|
25916
|
+
* owns it without a second lookup.
|
|
25917
|
+
*/
|
|
25918
|
+
name: zod.z.string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
|
|
25919
|
+
/** One sentence: what the operator will SEE after arming it. */
|
|
25920
|
+
description: zod.z.string().min(1),
|
|
25921
|
+
/** The level its lines are emitted at. Never below `info`. */
|
|
25922
|
+
defaultLevel: LogChannelLevelSchema,
|
|
25923
|
+
/**
|
|
25924
|
+
* Whether this channel can be narrowed to a camera.
|
|
25925
|
+
*
|
|
25926
|
+
* `true` is a PROMISE with two halves, and both must hold: the gate is
|
|
25927
|
+
* consulted with the numeric device id, AND every line the channel admits
|
|
25928
|
+
* carries `tags: { deviceId }` with that same numeric id. The second half is
|
|
25929
|
+
* what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
|
|
25930
|
+
* keeps `deviceId` out of the stream labels for cardinality, so the tag in
|
|
25931
|
+
* the body is the only way to filter.
|
|
25932
|
+
*
|
|
25933
|
+
* A channel whose lines carry the device only in `meta` (or not at all) is
|
|
25934
|
+
* declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
|
|
25935
|
+
* the operator narrows to one camera, sees nothing, and concludes the code
|
|
25936
|
+
* path was never taken.
|
|
25937
|
+
*/
|
|
25938
|
+
perDevice: zod.z.boolean()
|
|
25939
|
+
});
|
|
25940
|
+
var LogChannelWindowSchema = zod.z.object({
|
|
25941
|
+
channel: zod.z.string().min(1),
|
|
25942
|
+
/** Epoch ms the window closes at. */
|
|
25943
|
+
armedUntilMs: zod.z.number(),
|
|
25944
|
+
/** `null` = every camera. A non-empty list narrows to those numeric ids. */
|
|
25945
|
+
deviceIds: zod.z.array(zod.z.number().int()).readonly().nullable()
|
|
25946
|
+
});
|
|
25907
25947
|
var OpsLogDomainSchema = zod.z.enum(["recording", "events"]);
|
|
25908
25948
|
var OpsLogOpSchema = zod.z.enum([
|
|
25909
25949
|
"prune",
|
|
@@ -29461,6 +29501,21 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
29461
29501
|
whereBetween: zod.z.record(zod.z.string(), zod.z.tuple([zod.z.unknown(), zod.z.unknown()])).optional(),
|
|
29462
29502
|
whereNot: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
29463
29503
|
});
|
|
29504
|
+
var AggregateFieldSchema = zod.z.object({
|
|
29505
|
+
/** Result key. */
|
|
29506
|
+
as: zod.z.string().min(1),
|
|
29507
|
+
/** Column to aggregate. Must be a real column of a declared collection. */
|
|
29508
|
+
field: zod.z.string().min(1),
|
|
29509
|
+
op: zod.z.enum([
|
|
29510
|
+
"sum",
|
|
29511
|
+
"min",
|
|
29512
|
+
"max"
|
|
29513
|
+
])
|
|
29514
|
+
});
|
|
29515
|
+
var AggregateResultSchema = zod.z.object({
|
|
29516
|
+
count: zod.z.number().int(),
|
|
29517
|
+
values: zod.z.record(zod.z.string(), zod.z.number().nullable())
|
|
29518
|
+
});
|
|
29464
29519
|
var SettingsRecordSchema = zod.z.object({
|
|
29465
29520
|
id: zod.z.string(),
|
|
29466
29521
|
data: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
@@ -29588,6 +29643,32 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
29588
29643
|
collection: zod.z.string(),
|
|
29589
29644
|
filter: QueryFilterSchema.optional()
|
|
29590
29645
|
}), zod.z.number()),
|
|
29646
|
+
/**
|
|
29647
|
+
* `COUNT(*)` and one `SUM` / `MIN` / `MAX` per requested field, in ONE
|
|
29648
|
+
* statement, over the rows `filter` selects.
|
|
29649
|
+
*
|
|
29650
|
+
* Exists because "how much is there" was being answered by materialising
|
|
29651
|
+
* "what is there". The recorder's storage-pressure sweep asked its in-RAM
|
|
29652
|
+
* footage index for bytes/count/oldest/newest across a set of storage
|
|
29653
|
+
* locations twice a minute, and the only way to answer that from a map is
|
|
29654
|
+
* to visit every row — 7.1 M of them on the live hub, ~15 M row visits a
|
|
29655
|
+
* minute on the main thread, which is also why the whole archive had to
|
|
29656
|
+
* stay resident to be visited. The question is a sum; nothing needs to be
|
|
29657
|
+
* materialised to answer it.
|
|
29658
|
+
*
|
|
29659
|
+
* **The engine REFUSES a field it cannot serve**, exactly as
|
|
29660
|
+
* `query.columns` does and unlike a PREDICATE, which is skipped when
|
|
29661
|
+
* unresolvable. A dropped predicate over-matches and the caller sees extra
|
|
29662
|
+
* rows; a dropped aggregate returns a NUMBER that is wrong and looks
|
|
29663
|
+
* exactly like a real one. That asymmetry is what this repo has already
|
|
29664
|
+
* paid for once in `count`.
|
|
29665
|
+
*/
|
|
29666
|
+
aggregate: method(zod.z.object({
|
|
29667
|
+
namespace: zod.z.string().optional(),
|
|
29668
|
+
collection: zod.z.string(),
|
|
29669
|
+
fields: zod.z.array(AggregateFieldSchema).readonly(),
|
|
29670
|
+
filter: QueryFilterSchema.optional()
|
|
29671
|
+
}), AggregateResultSchema),
|
|
29591
29672
|
/** Grouped counts per ((field-origin)/bucketSize) bucket, filtered. */
|
|
29592
29673
|
histogram: method(zod.z.object({
|
|
29593
29674
|
namespace: zod.z.string().optional(),
|
|
@@ -29746,6 +29827,15 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
29746
29827
|
collection: zod.z.string(),
|
|
29747
29828
|
filter: QueryFilterSchema.optional()
|
|
29748
29829
|
}), zod.z.number(), { auth: "admin" }),
|
|
29830
|
+
/** `COUNT(*)` plus one SUM/MIN/MAX per field, in one statement. Mirror of
|
|
29831
|
+
* `settings-store.aggregate` — see it for why an unresolvable field is
|
|
29832
|
+
* refused rather than dropped. */
|
|
29833
|
+
aggregate: method(zod.z.object({
|
|
29834
|
+
namespace: zod.z.string().optional(),
|
|
29835
|
+
collection: zod.z.string(),
|
|
29836
|
+
fields: zod.z.array(AggregateFieldSchema).readonly(),
|
|
29837
|
+
filter: QueryFilterSchema.optional()
|
|
29838
|
+
}), AggregateResultSchema, { auth: "admin" }),
|
|
29749
29839
|
/** Grouped counts per ((field-origin)/bucketSize) bucket, filtered. */
|
|
29750
29840
|
histogram: method(zod.z.object({
|
|
29751
29841
|
namespace: zod.z.string().optional(),
|
|
@@ -30857,6 +30947,39 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
30857
30947
|
/** List children of a parent device (by parent numeric id). */
|
|
30858
30948
|
getChildren: method(zod.z.object({ parentDeviceId: zod.z.number() }), zod.z.array(DeviceInfoSchema)),
|
|
30859
30949
|
/**
|
|
30950
|
+
* `getChildren` for a NAMED SET of parents, in one call.
|
|
30951
|
+
*
|
|
30952
|
+
* The accessory reconcile in `device-cap-proxy.ts` asks this question once
|
|
30953
|
+
* per registered device — every `BaseDevice` inherits a
|
|
30954
|
+
* `getAccessoryChildren()` that returns `[]`, so even a leaf accessory
|
|
30955
|
+
* pays a round-trip to learn it has nothing to prune. Measured on the live
|
|
30956
|
+
* hub 2026-08-27 over a 120-second boot window, fleet of 1 017 devices:
|
|
30957
|
+
* `DeviceRowStore.list < DeviceRowStore.listByParent < getChildren` at
|
|
30958
|
+
* **1 024 calls** returning **919 rows in total** — 1 024 RPCs and 1 024
|
|
30959
|
+
* indexed scans to move less than one row each. `listByParentMany`
|
|
30960
|
+
* collapses the scans; this collapses the RPCs.
|
|
30961
|
+
*
|
|
30962
|
+
* Keyed by parent id as a STRING — a JSON object cannot key by number
|
|
30963
|
+
* (same reason as `getDeviceStatusAggregateBatch`). The per-parent value
|
|
30964
|
+
* is exactly what `getChildren` returns for that parent.
|
|
30965
|
+
*
|
|
30966
|
+
* A parent with no children — or one the fleet does not know — is ABSENT
|
|
30967
|
+
* from the record, never an invented empty row: the same contract as
|
|
30968
|
+
* `DeviceRowStore.getMany`/`listByParentMany`. An EMPTY `parentDeviceIds`
|
|
30969
|
+
* reads nothing at all rather than degrading to "every device".
|
|
30970
|
+
*
|
|
30971
|
+
* `parentDeviceIds` is capped at {@link DEVICE_CHILDREN_BATCH_MAX} — see
|
|
30972
|
+
* that constant for why. A caller with more parents than that sends more
|
|
30973
|
+
* than one call; it never sends one pathological one.
|
|
30974
|
+
*
|
|
30975
|
+
* Version skew: this is a NEW method, not a new field on `getChildren`, so
|
|
30976
|
+
* a hub that predates it answers NOT_FOUND rather than silently stripping
|
|
30977
|
+
* an unknown input key and answering a DIFFERENT question. The kernel-side
|
|
30978
|
+
* loader degrades to per-parent `getChildren` on that error — see
|
|
30979
|
+
* `children-batch-loader.ts`.
|
|
30980
|
+
*/
|
|
30981
|
+
getChildrenBatch: method(zod.z.object({ parentDeviceIds: zod.z.array(zod.z.number()).max(256) }), zod.z.record(zod.z.string(), zod.z.array(DeviceInfoSchema))),
|
|
30982
|
+
/**
|
|
30860
30983
|
* Resolve the devices LINKED to a camera — the single policy authority
|
|
30861
30984
|
* both consumers call (viewer devices panel + pipeline-analytics event
|
|
30862
30985
|
* kinds/ingest). Device-tree children are ALWAYS included; mode 'auto'
|
|
@@ -31901,6 +32024,38 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
31901
32024
|
})
|
|
31902
32025
|
}
|
|
31903
32026
|
};
|
|
32027
|
+
var LogChannelApplyResultSchema = zod.z.object({
|
|
32028
|
+
/** How many declared channels are armed in this process after the call. */
|
|
32029
|
+
armed: zod.z.number().int().min(0),
|
|
32030
|
+
/**
|
|
32031
|
+
* Names the document armed that this process does not declare. Reported
|
|
32032
|
+
* rather than swallowed: a name here is either a typo or an addon that has
|
|
32033
|
+
* not booted, and both deserve a line instead of silence.
|
|
32034
|
+
*/
|
|
32035
|
+
unknown: zod.z.array(zod.z.string()).readonly()
|
|
32036
|
+
});
|
|
32037
|
+
var logChannelsCapability = {
|
|
32038
|
+
name: "log-channels",
|
|
32039
|
+
scope: "system",
|
|
32040
|
+
mode: "collection",
|
|
32041
|
+
internal: true,
|
|
32042
|
+
methods: {
|
|
32043
|
+
/** The channels this addon declares. Inert: no value, no state. */
|
|
32044
|
+
list: method(zod.z.void(), zod.z.array(LogChannelDescriptorSchema).readonly()),
|
|
32045
|
+
/**
|
|
32046
|
+
* Refresh this process's mirror from the document's FULL set of armed
|
|
32047
|
+
* windows.
|
|
32048
|
+
*
|
|
32049
|
+
* Full and not incremental on purpose: the document is the authority, so a
|
|
32050
|
+
* channel it does not name is disarmed here. An incremental apply would
|
|
32051
|
+
* let a disarm get lost in transit and leave a channel running that
|
|
32052
|
+
* nobody can see is running.
|
|
32053
|
+
*/
|
|
32054
|
+
apply: method(zod.z.object({ windows: zod.z.array(LogChannelWindowSchema).readonly() }), LogChannelApplyResultSchema, { kind: "mutation" })
|
|
32055
|
+
},
|
|
32056
|
+
/** In-process only — enumerated through `addons.listCapabilityProviders`. */
|
|
32057
|
+
mount: { kind: "skip" }
|
|
32058
|
+
};
|
|
31904
32059
|
var LogLevelSchema = zod.z.enum([
|
|
31905
32060
|
"debug",
|
|
31906
32061
|
"info",
|
|
@@ -47226,6 +47381,14 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
47226
47381
|
scope: LoggingScopeKindSchema,
|
|
47227
47382
|
/** The node this layer speaks for; `null` on the cluster layer. */
|
|
47228
47383
|
nodeId: zod.z.string().nullable(),
|
|
47384
|
+
/**
|
|
47385
|
+
* The declared channel this layer speaks for; `null` on every layer but
|
|
47386
|
+
* `component`. Never folded into `nodeId`: a component level is CLUSTER-WIDE
|
|
47387
|
+
* by design — the convention this repo settled on is one orchestrator-wide
|
|
47388
|
+
* setting, never per node (D52) — so a component layer that carried a node
|
|
47389
|
+
* would invite a per-node copy of a value that has no per-node meaning.
|
|
47390
|
+
*/
|
|
47391
|
+
component: zod.z.string().nullable(),
|
|
47229
47392
|
/** Explicitly set here, or `null` when this layer inherits. */
|
|
47230
47393
|
level: LogLevelSchema$1.nullable()
|
|
47231
47394
|
});
|
|
@@ -47251,6 +47414,38 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
47251
47414
|
/** Cadence of the diagnostic's aggregated report line. Clamped by the server. */
|
|
47252
47415
|
reportEveryMs: zod.z.number().int().positive().optional()
|
|
47253
47416
|
});
|
|
47417
|
+
var LogChannelWindowStateSchema = zod.z.object({
|
|
47418
|
+
channel: zod.z.string(),
|
|
47419
|
+
armed: zod.z.boolean(),
|
|
47420
|
+
/** Epoch ms the window closes at. 0 when disarmed. */
|
|
47421
|
+
armedUntilMs: zod.z.number(),
|
|
47422
|
+
/** Ms left before it expires on its own. 0 when disarmed. */
|
|
47423
|
+
remainingMs: zod.z.number(),
|
|
47424
|
+
/**
|
|
47425
|
+
* The cameras it is narrowed to, or `null` for every camera.
|
|
47426
|
+
*
|
|
47427
|
+
* A channel declared `perDevice: false` can only ever report `null` here:
|
|
47428
|
+
* its lines do not carry `tags: { deviceId }`, so narrowing them would
|
|
47429
|
+
* produce a filter that silently matches nothing. The server REFUSES such a
|
|
47430
|
+
* patch rather than quietly widening it — ignoring the request would teach
|
|
47431
|
+
* the operator that per-camera filtering works on that channel when it does
|
|
47432
|
+
* not.
|
|
47433
|
+
*/
|
|
47434
|
+
deviceIds: zod.z.array(zod.z.number().int()).readonly().nullable()
|
|
47435
|
+
});
|
|
47436
|
+
var LogChannelWindowPatchSchema = zod.z.object({
|
|
47437
|
+
channel: zod.z.string().min(1),
|
|
47438
|
+
armMs: zod.z.number().int().min(0),
|
|
47439
|
+
/**
|
|
47440
|
+
* Narrow to these numeric device ids. Absent or `null` = every camera.
|
|
47441
|
+
*
|
|
47442
|
+
* Numeric because the repo's own rule makes it possible: every log line
|
|
47443
|
+
* about a device carries `tags: { deviceId }` with the numeric id. That rule
|
|
47444
|
+
* was paid for with a 22% thumbnail gap and a 3-hour media blackout both
|
|
47445
|
+
* diagnosed by hand, and this is the first thing that collects on it.
|
|
47446
|
+
*/
|
|
47447
|
+
deviceIds: zod.z.array(zod.z.number().int()).readonly().nullable().optional()
|
|
47448
|
+
});
|
|
47254
47449
|
var LoggingSettingsPatchSchema = zod.z.object({
|
|
47255
47450
|
/**
|
|
47256
47451
|
* Absent leaves the level untouched. `null` CLEARS the explicit value at the
|
|
@@ -47261,19 +47456,50 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
47261
47456
|
* Only the diagnostics NAMED here change. An armed window that is not listed
|
|
47262
47457
|
* keeps running — a patch is never a full replacement.
|
|
47263
47458
|
*/
|
|
47264
|
-
diagnostics: zod.z.array(DiagnosticWindowPatchSchema).readonly().optional()
|
|
47459
|
+
diagnostics: zod.z.array(DiagnosticWindowPatchSchema).readonly().optional(),
|
|
47460
|
+
/**
|
|
47461
|
+
* Only the channels NAMED here change. An armed channel that is not listed
|
|
47462
|
+
* keeps running — same rule as `diagnostics`, because a patch that silently
|
|
47463
|
+
* disarmed the channels it did not mention would make the Levels page and
|
|
47464
|
+
* the Diagnostics page fight over the same value.
|
|
47465
|
+
*/
|
|
47466
|
+
channels: zod.z.array(LogChannelWindowPatchSchema).readonly().optional()
|
|
47467
|
+
});
|
|
47468
|
+
var GetLoggingSettingsInputSchema = zod.z.object({
|
|
47469
|
+
scopeNodeId: zod.z.string().optional(),
|
|
47470
|
+
/**
|
|
47471
|
+
* The declared CHANNEL this document is addressed at, when the caller wants
|
|
47472
|
+
* the `component` layer. Absent = the node/cluster hierarchy only.
|
|
47473
|
+
*
|
|
47474
|
+
* Naming it separately rather than overloading `scopeNodeId` keeps the two
|
|
47475
|
+
* axes from collapsing: a component level is cluster-wide, a node level is
|
|
47476
|
+
* not, and one selector for both would make "which of these two did I just
|
|
47477
|
+
* set" unanswerable — the exact ambiguity `explicit` exists to remove.
|
|
47478
|
+
*/
|
|
47479
|
+
scopeComponent: zod.z.string().optional()
|
|
47265
47480
|
});
|
|
47266
|
-
var GetLoggingSettingsInputSchema = zod.z.object({ scopeNodeId: zod.z.string().optional() });
|
|
47267
47481
|
var SetLoggingSettingsInputSchema = zod.z.object({
|
|
47268
47482
|
scopeNodeId: zod.z.string().optional(),
|
|
47483
|
+
scopeComponent: zod.z.string().optional(),
|
|
47269
47484
|
patch: LoggingSettingsPatchSchema
|
|
47270
47485
|
});
|
|
47271
47486
|
var LoggingSettingsStateSchema = zod.z.object({
|
|
47272
47487
|
/** The layer this document was read at. `null` = the cluster layer. */
|
|
47273
47488
|
scopeNodeId: zod.z.string().nullable(),
|
|
47489
|
+
/** The channel this document was read at. `null` = no component layer. */
|
|
47490
|
+
scopeComponent: zod.z.string().nullable(),
|
|
47274
47491
|
effective: LoggingEffectiveSchema,
|
|
47275
47492
|
explicit: LoggingExplicitSchema,
|
|
47276
47493
|
activeWindows: zod.z.array(DiagnosticWindowSchema).readonly(),
|
|
47494
|
+
/**
|
|
47495
|
+
* Every channel the cluster's addons DECLARE, gathered from the
|
|
47496
|
+
* `log-channels` providers. Not stored anywhere: assembled per read, so a
|
|
47497
|
+
* channel added by a redeployed addon appears without anybody editing a
|
|
47498
|
+
* list, and a channel whose addon is gone stops being offered.
|
|
47499
|
+
*/
|
|
47500
|
+
channels: zod.z.array(LogChannelDescriptorSchema).readonly(),
|
|
47501
|
+
/** The channels ARMED right now, each with its deadline. */
|
|
47502
|
+
activeChannels: zod.z.array(LogChannelWindowStateSchema).readonly(),
|
|
47277
47503
|
persisted: zod.z.boolean()
|
|
47278
47504
|
});
|
|
47279
47505
|
var systemCapability = {
|
|
@@ -48752,6 +48978,7 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
48752
48978
|
llmRuntimeCapability,
|
|
48753
48979
|
localNetworkCapability,
|
|
48754
48980
|
lockControlCapability,
|
|
48981
|
+
logChannelsCapability,
|
|
48755
48982
|
logDestinationCapability,
|
|
48756
48983
|
loginMethodCapability,
|
|
48757
48984
|
mediaPlayerCapability,
|
|
@@ -49718,6 +49945,12 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
49718
49945
|
addonId: null,
|
|
49719
49946
|
access: "view"
|
|
49720
49947
|
},
|
|
49948
|
+
"dataStoreProvider.aggregate": {
|
|
49949
|
+
capName: "data-store-provider",
|
|
49950
|
+
capScope: "system",
|
|
49951
|
+
addonId: null,
|
|
49952
|
+
access: "view"
|
|
49953
|
+
},
|
|
49721
49954
|
"dataStoreProvider.count": {
|
|
49722
49955
|
capName: "data-store-provider",
|
|
49723
49956
|
capScope: "system",
|
|
@@ -50132,6 +50365,12 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
50132
50365
|
addonId: null,
|
|
50133
50366
|
access: "view"
|
|
50134
50367
|
},
|
|
50368
|
+
"deviceManager.getChildrenBatch": {
|
|
50369
|
+
capName: "device-manager",
|
|
50370
|
+
capScope: "system",
|
|
50371
|
+
addonId: null,
|
|
50372
|
+
access: "view"
|
|
50373
|
+
},
|
|
50135
50374
|
"deviceManager.getConfigSchema": {
|
|
50136
50375
|
capName: "device-manager",
|
|
50137
50376
|
capScope: "system",
|
|
@@ -51182,6 +51421,18 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
51182
51421
|
addonId: null,
|
|
51183
51422
|
access: "create"
|
|
51184
51423
|
},
|
|
51424
|
+
"logChannels.apply": {
|
|
51425
|
+
capName: "log-channels",
|
|
51426
|
+
capScope: "system",
|
|
51427
|
+
addonId: null,
|
|
51428
|
+
access: "create"
|
|
51429
|
+
},
|
|
51430
|
+
"logChannels.list": {
|
|
51431
|
+
capName: "log-channels",
|
|
51432
|
+
capScope: "system",
|
|
51433
|
+
addonId: null,
|
|
51434
|
+
access: "view"
|
|
51435
|
+
},
|
|
51185
51436
|
"logDestination.query": {
|
|
51186
51437
|
capName: "log-destination",
|
|
51187
51438
|
capScope: "system",
|
|
@@ -53336,6 +53587,12 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
53336
53587
|
addonId: null,
|
|
53337
53588
|
access: "create"
|
|
53338
53589
|
},
|
|
53590
|
+
"settingsStore.aggregate": {
|
|
53591
|
+
capName: "settings-store",
|
|
53592
|
+
capScope: "system",
|
|
53593
|
+
addonId: null,
|
|
53594
|
+
access: "view"
|
|
53595
|
+
},
|
|
53339
53596
|
"settingsStore.count": {
|
|
53340
53597
|
capName: "settings-store",
|
|
53341
53598
|
capScope: "system",
|
|
@@ -54915,6 +55172,11 @@ var require_dist_D3lqzV40 = __commonJS({
|
|
|
54915
55172
|
form: "single",
|
|
54916
55173
|
optional: false
|
|
54917
55174
|
}],
|
|
55175
|
+
"deviceManager.getChildrenBatch": [{
|
|
55176
|
+
name: "parentDeviceIds",
|
|
55177
|
+
form: "array",
|
|
55178
|
+
optional: false
|
|
55179
|
+
}],
|
|
54918
55180
|
"deviceManager.getConfigSchema": [{
|
|
54919
55181
|
name: "deviceId",
|
|
54920
55182
|
form: "single",
|
|
@@ -57730,7 +57992,7 @@ var require_alerts_addon = __commonJS({
|
|
|
57730
57992
|
[Symbol.toStringTag]: { value: "Module" }
|
|
57731
57993
|
});
|
|
57732
57994
|
require_chunk_Cek0wNdY();
|
|
57733
|
-
var require_dist10 =
|
|
57995
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
57734
57996
|
function selectExpired(alerts, cutoffMs) {
|
|
57735
57997
|
return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
|
|
57736
57998
|
}
|
|
@@ -58549,7 +58811,7 @@ var require_console_logging = __commonJS({
|
|
|
58549
58811
|
[Symbol.toStringTag]: { value: "Module" }
|
|
58550
58812
|
});
|
|
58551
58813
|
require_chunk_Cek0wNdY();
|
|
58552
|
-
var require_dist10 =
|
|
58814
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
58553
58815
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
58554
58816
|
var LEVEL_RANK = {
|
|
58555
58817
|
debug: 0,
|
|
@@ -58643,7 +58905,7 @@ var require_core_blocks_addon = __commonJS({
|
|
|
58643
58905
|
"use strict";
|
|
58644
58906
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
58645
58907
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
58646
|
-
var require_dist10 =
|
|
58908
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
58647
58909
|
var node_crypto = __require("crypto");
|
|
58648
58910
|
var node_fs_promises = __require("fs/promises");
|
|
58649
58911
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -59540,11 +59802,11 @@ var require_core_blocks = __commonJS({
|
|
|
59540
59802
|
}
|
|
59541
59803
|
});
|
|
59542
59804
|
|
|
59543
|
-
// ../system/dist/retired-settings-keys-
|
|
59544
|
-
var
|
|
59545
|
-
"../system/dist/retired-settings-keys-
|
|
59805
|
+
// ../system/dist/retired-settings-keys-_PLI9w0k.js
|
|
59806
|
+
var require_retired_settings_keys_PLI9w0k = __commonJS({
|
|
59807
|
+
"../system/dist/retired-settings-keys-_PLI9w0k.js"(exports) {
|
|
59546
59808
|
"use strict";
|
|
59547
|
-
var require_dist10 =
|
|
59809
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
59548
59810
|
function settingsStoreIsAuthoritativeHere(env) {
|
|
59549
59811
|
const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
|
|
59550
59812
|
return raw === "" || raw === "hub";
|
|
@@ -61587,8 +61849,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
61587
61849
|
[Symbol.toStringTag]: { value: "Module" }
|
|
61588
61850
|
});
|
|
61589
61851
|
require_chunk_Cek0wNdY();
|
|
61590
|
-
var require_dist10 =
|
|
61591
|
-
var require_retired_settings_keys =
|
|
61852
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
61853
|
+
var require_retired_settings_keys = require_retired_settings_keys_PLI9w0k();
|
|
61592
61854
|
var node_crypto = __require("crypto");
|
|
61593
61855
|
var _camstack_types_node = require_node();
|
|
61594
61856
|
var JOB_HISTORY = 20;
|
|
@@ -62750,30 +63012,16 @@ var require_device_manager_addon = __commonJS({
|
|
|
62750
63012
|
...sourceInfoGetDevice !== void 0 ? { sourceInfo: sourceInfoGetDevice } : {}
|
|
62751
63013
|
};
|
|
62752
63014
|
}
|
|
62753
|
-
async function
|
|
62754
|
-
const { parentDeviceId } = input;
|
|
62755
|
-
let ownerAddonId = null;
|
|
62756
|
-
if (pctx.registry) {
|
|
62757
|
-
if (pctx.registry.getById(parentDeviceId)) ownerAddonId = pctx.registry.getAddonId(parentDeviceId);
|
|
62758
|
-
}
|
|
62759
|
-
if (!ownerAddonId) {
|
|
62760
|
-
const persisted = await pctx.metaStore.resolvePersistedById(parentDeviceId);
|
|
62761
|
-
if (!persisted) return [];
|
|
62762
|
-
ownerAddonId = persisted.addonId;
|
|
62763
|
-
}
|
|
63015
|
+
async function projectChildren(pctx, parentDeviceId, ownerAddonId, childRows, liveChildren) {
|
|
62764
63016
|
const results = [];
|
|
62765
63017
|
const seen = /* @__PURE__ */ new Set();
|
|
62766
|
-
const childRows = await pctx.metaStore.rows.listByParent(parentDeviceId);
|
|
62767
63018
|
const rowById = /* @__PURE__ */ new Map();
|
|
62768
63019
|
for (const row of childRows) rowById.set(row.meta.id, row);
|
|
62769
|
-
|
|
62770
|
-
const
|
|
62771
|
-
|
|
62772
|
-
|
|
62773
|
-
|
|
62774
|
-
results.push(toDeviceInfo(ownerAddonId, device, row?.metadata ?? null, row?.meta ?? null));
|
|
62775
|
-
seen.add(key);
|
|
62776
|
-
}
|
|
63020
|
+
for (const device of liveChildren) {
|
|
63021
|
+
const key = String(device.id);
|
|
63022
|
+
const row = rowById.get(device.id);
|
|
63023
|
+
results.push(toDeviceInfo(ownerAddonId, device, row?.metadata ?? null, row?.meta ?? null));
|
|
63024
|
+
seen.add(key);
|
|
62777
63025
|
}
|
|
62778
63026
|
for (const row of childRows) {
|
|
62779
63027
|
const m = row.meta;
|
|
@@ -62809,6 +63057,58 @@ var require_device_manager_addon = __commonJS({
|
|
|
62809
63057
|
}
|
|
62810
63058
|
return results;
|
|
62811
63059
|
}
|
|
63060
|
+
function liveOwnerOf(pctx, parentDeviceId) {
|
|
63061
|
+
if (!pctx.registry) return null;
|
|
63062
|
+
if (!pctx.registry.getById(parentDeviceId)) return null;
|
|
63063
|
+
return pctx.registry.getAddonId(parentDeviceId);
|
|
63064
|
+
}
|
|
63065
|
+
async function getChildren(pctx, input) {
|
|
63066
|
+
const { parentDeviceId } = input;
|
|
63067
|
+
let ownerAddonId = liveOwnerOf(pctx, parentDeviceId);
|
|
63068
|
+
if (!ownerAddonId) {
|
|
63069
|
+
const persisted = await pctx.metaStore.resolvePersistedById(parentDeviceId);
|
|
63070
|
+
if (!persisted) return [];
|
|
63071
|
+
ownerAddonId = persisted.addonId;
|
|
63072
|
+
}
|
|
63073
|
+
const childRows = await pctx.metaStore.rows.listByParent(parentDeviceId);
|
|
63074
|
+
const liveChildren = pctx.registry?.getChildren(parentDeviceId) ?? [];
|
|
63075
|
+
return [...await projectChildren(pctx, parentDeviceId, ownerAddonId, childRows, liveChildren)];
|
|
63076
|
+
}
|
|
63077
|
+
async function getChildrenBatch(pctx, input) {
|
|
63078
|
+
const parentIds = [...new Set(input.parentDeviceIds)];
|
|
63079
|
+
const out = {};
|
|
63080
|
+
if (parentIds.length === 0) return out;
|
|
63081
|
+
const owners = /* @__PURE__ */ new Map();
|
|
63082
|
+
const unownedIds = [];
|
|
63083
|
+
for (const parentId of parentIds) {
|
|
63084
|
+
const live = liveOwnerOf(pctx, parentId);
|
|
63085
|
+
if (live !== null) owners.set(parentId, live);
|
|
63086
|
+
else unownedIds.push(parentId);
|
|
63087
|
+
}
|
|
63088
|
+
if (unownedIds.length > 0) {
|
|
63089
|
+
const rows = await pctx.metaStore.rows.getMany(unownedIds);
|
|
63090
|
+
for (const [parentId, row] of rows) owners.set(parentId, row.meta.addonId);
|
|
63091
|
+
}
|
|
63092
|
+
const requested = new Set(parentIds);
|
|
63093
|
+
const rowsByParent = await pctx.metaStore.rows.listByParentMany([...owners.keys()]);
|
|
63094
|
+
const liveByParent = /* @__PURE__ */ new Map();
|
|
63095
|
+
for (const device of pctx.registry?.getAll() ?? []) {
|
|
63096
|
+
const parentId = device.parentDeviceId;
|
|
63097
|
+
if (parentId === null || parentId === void 0) continue;
|
|
63098
|
+
if (!requested.has(parentId)) continue;
|
|
63099
|
+
const bucket = liveByParent.get(parentId);
|
|
63100
|
+
if (bucket === void 0) liveByParent.set(parentId, [device]);
|
|
63101
|
+
else bucket.push(device);
|
|
63102
|
+
}
|
|
63103
|
+
for (const [parentId, ownerAddonId] of owners) {
|
|
63104
|
+
const childRows = rowsByParent.get(parentId) ?? [];
|
|
63105
|
+
const liveChildren = liveByParent.get(parentId) ?? [];
|
|
63106
|
+
if (childRows.length === 0 && liveChildren.length === 0) continue;
|
|
63107
|
+
const projected = await projectChildren(pctx, parentId, ownerAddonId, childRows, liveChildren);
|
|
63108
|
+
if (projected.length > 0) out[String(parentId)] = [...projected];
|
|
63109
|
+
}
|
|
63110
|
+
return out;
|
|
63111
|
+
}
|
|
62812
63112
|
async function getStreamSources(pctx, input) {
|
|
62813
63113
|
const { deviceId } = input;
|
|
62814
63114
|
if (pctx.registry) {
|
|
@@ -64255,16 +64555,17 @@ var require_device_manager_addon = __commonJS({
|
|
|
64255
64555
|
async function collectDescendants(metaStore, rootId) {
|
|
64256
64556
|
const out = [];
|
|
64257
64557
|
const visited = /* @__PURE__ */ new Set([rootId]);
|
|
64258
|
-
|
|
64259
|
-
while (
|
|
64260
|
-
const
|
|
64261
|
-
|
|
64262
|
-
for (const row of
|
|
64558
|
+
let frontier = [rootId];
|
|
64559
|
+
while (frontier.length > 0) {
|
|
64560
|
+
const byParent = await metaStore.rows.listByParentMany(frontier);
|
|
64561
|
+
const next = [];
|
|
64562
|
+
for (const parentId of frontier) for (const row of byParent.get(parentId) ?? []) {
|
|
64263
64563
|
if (visited.has(row.meta.id)) continue;
|
|
64264
64564
|
visited.add(row.meta.id);
|
|
64265
64565
|
out.push(row.meta);
|
|
64266
|
-
|
|
64566
|
+
next.push(row.meta.id);
|
|
64267
64567
|
}
|
|
64568
|
+
frontier = next;
|
|
64268
64569
|
}
|
|
64269
64570
|
return out;
|
|
64270
64571
|
}
|
|
@@ -65323,6 +65624,46 @@ var require_device_manager_addon = __commonJS({
|
|
|
65323
65624
|
limit: DEVICE_ROWS_FLEET_LIMIT
|
|
65324
65625
|
});
|
|
65325
65626
|
}
|
|
65627
|
+
/**
|
|
65628
|
+
* Direct children of MANY devices, in ONE query.
|
|
65629
|
+
*
|
|
65630
|
+
* `listByParent` called once per parent is the same shape `get` was before
|
|
65631
|
+
* {@link getMany}: an indexed lookup this store makes cheap, repeated until
|
|
65632
|
+
* it isn't. Measured on the live hub 2026-08-27, 120-second boot window:
|
|
65633
|
+
* `DeviceRowStore.list < DeviceRowStore.listByParent < getChildren` at
|
|
65634
|
+
* **1 024 calls** — one per device — for 919 rows in total. The BFS in
|
|
65635
|
+
* `device-meta-actions.ts#collectDescendants` walked a container tree the
|
|
65636
|
+
* same way, one query per node.
|
|
65637
|
+
*
|
|
65638
|
+
* `whereIn` on the indexed `parentDeviceId` column collapses a level of that
|
|
65639
|
+
* walk into a single statement. Contract, verbatim from {@link getMany}: an
|
|
65640
|
+
* EMPTY `parentDeviceIds` reads nothing at all rather than degrading to
|
|
65641
|
+
* "everything", and a parent with no children — or one the fleet does not
|
|
65642
|
+
* know — is simply ABSENT from the map, never an invented empty row.
|
|
65643
|
+
*/
|
|
65644
|
+
async listByParentMany(parentDeviceIds) {
|
|
65645
|
+
const out = /* @__PURE__ */ new Map();
|
|
65646
|
+
const unique = [...new Set(parentDeviceIds)];
|
|
65647
|
+
if (unique.length === 0) return out;
|
|
65648
|
+
const rows = await this.list({
|
|
65649
|
+
whereIn: { parentDeviceId: unique },
|
|
65650
|
+
orderBy: {
|
|
65651
|
+
field: "deviceId",
|
|
65652
|
+
direction: "asc"
|
|
65653
|
+
},
|
|
65654
|
+
limit: DEVICE_ROWS_FLEET_LIMIT
|
|
65655
|
+
});
|
|
65656
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
65657
|
+
for (const row of rows) {
|
|
65658
|
+
const parent = row.meta.parentDeviceId;
|
|
65659
|
+
if (parent === null || parent === void 0) continue;
|
|
65660
|
+
const bucket = byParent.get(parent);
|
|
65661
|
+
if (bucket === void 0) byParent.set(parent, [row]);
|
|
65662
|
+
else bucket.push(row);
|
|
65663
|
+
}
|
|
65664
|
+
for (const [parent, bucket] of byParent) out.set(parent, bucket);
|
|
65665
|
+
return out;
|
|
65666
|
+
}
|
|
65326
65667
|
/** How many devices the fleet holds. Used to tell "empty store" from "gone device". */
|
|
65327
65668
|
async count() {
|
|
65328
65669
|
await this.declare();
|
|
@@ -66056,6 +66397,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
66056
66397
|
listAll: (input) => listAll(pctx, input),
|
|
66057
66398
|
getDevice: (input) => getDevice(pctx, input),
|
|
66058
66399
|
getChildren: (input) => getChildren(pctx, input),
|
|
66400
|
+
getChildrenBatch: (input) => getChildrenBatch(pctx, input),
|
|
66059
66401
|
getLinkedDevices: (input) => getLinkedDevices(pctx, input),
|
|
66060
66402
|
getLinkedDevicesBatch: (input) => getLinkedDevicesBatch(pctx, input),
|
|
66061
66403
|
getDeviceSettingsContribution: (input) => buildLinkedDevicesContribution(pctx, input.deviceId),
|
|
@@ -66257,7 +66599,7 @@ var require_hub_forwarder = __commonJS({
|
|
|
66257
66599
|
[Symbol.toStringTag]: { value: "Module" }
|
|
66258
66600
|
});
|
|
66259
66601
|
require_chunk_Cek0wNdY();
|
|
66260
|
-
var require_dist10 =
|
|
66602
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
66261
66603
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
66262
66604
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
66263
66605
|
var HubForwarderDestination = class {
|
|
@@ -66394,7 +66736,7 @@ var require_liveness_monitor_addon = __commonJS({
|
|
|
66394
66736
|
"use strict";
|
|
66395
66737
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
66396
66738
|
require_chunk_Cek0wNdY();
|
|
66397
|
-
var require_dist10 =
|
|
66739
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
66398
66740
|
var NO_DEVICES = "liveness:no-devices";
|
|
66399
66741
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
66400
66742
|
var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
|
|
@@ -66584,7 +66926,7 @@ var require_local_auth_addon = __commonJS({
|
|
|
66584
66926
|
[Symbol.toStringTag]: { value: "Module" }
|
|
66585
66927
|
});
|
|
66586
66928
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
66587
|
-
var require_dist10 =
|
|
66929
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
66588
66930
|
var node_crypto = __require("crypto");
|
|
66589
66931
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
66590
66932
|
var crypto$1 = __require("crypto");
|
|
@@ -74268,7 +74610,7 @@ var require_loki_logging = __commonJS({
|
|
|
74268
74610
|
[Symbol.toStringTag]: { value: "Module" }
|
|
74269
74611
|
});
|
|
74270
74612
|
require_chunk_Cek0wNdY();
|
|
74271
|
-
var require_dist10 =
|
|
74613
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
74272
74614
|
function sanitizeLabelName(raw) {
|
|
74273
74615
|
const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
|
|
74274
74616
|
return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
|
|
@@ -74833,7 +75175,7 @@ var require_native_metrics_addon = __commonJS({
|
|
|
74833
75175
|
[Symbol.toStringTag]: { value: "Module" }
|
|
74834
75176
|
});
|
|
74835
75177
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
74836
|
-
var require_dist10 =
|
|
75178
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
74837
75179
|
var node_child_process = __require("child_process");
|
|
74838
75180
|
var node_util = __require("util");
|
|
74839
75181
|
var node_os = __require("os");
|
|
@@ -75775,7 +76117,7 @@ var require_filesystem_storage_addon = __commonJS({
|
|
|
75775
76117
|
[Symbol.toStringTag]: { value: "Module" }
|
|
75776
76118
|
});
|
|
75777
76119
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
75778
|
-
var require_dist10 =
|
|
76120
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
75779
76121
|
var node_crypto = __require("crypto");
|
|
75780
76122
|
var node_fs_promises = __require("fs/promises");
|
|
75781
76123
|
var node_path = __require("path");
|
|
@@ -76891,8 +77233,8 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
76891
77233
|
[Symbol.toStringTag]: { value: "Module" }
|
|
76892
77234
|
});
|
|
76893
77235
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
76894
|
-
var require_dist10 =
|
|
76895
|
-
var require_retired_settings_keys =
|
|
77236
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
77237
|
+
var require_retired_settings_keys = require_retired_settings_keys_PLI9w0k();
|
|
76896
77238
|
var node_crypto = __require("crypto");
|
|
76897
77239
|
var node_fs = __require("fs");
|
|
76898
77240
|
var node_module = __require("module");
|
|
@@ -77733,6 +78075,46 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
77733
78075
|
params
|
|
77734
78076
|
}, () => this.getDb().prepare(sql).get(...params))?.cnt ?? 0;
|
|
77735
78077
|
}
|
|
78078
|
+
/**
|
|
78079
|
+
* `COUNT(*)` and one SUM/MIN/MAX per requested field, in ONE statement.
|
|
78080
|
+
*
|
|
78081
|
+
* Result slots are aliased POSITIONALLY (`a0`, `a1`, …) and mapped back by
|
|
78082
|
+
* index: `as` is caller text and must never reach SQL as an identifier, and
|
|
78083
|
+
* the column name is validated through {@link fieldExprFor} exactly like a
|
|
78084
|
+
* projection — an unresolvable field THROWS rather than being dropped, because
|
|
78085
|
+
* a missing aggregate comes back as a number that looks real.
|
|
78086
|
+
*
|
|
78087
|
+
* `select` mode on the filter, so this agrees with `query` and `count` about
|
|
78088
|
+
* which rows it is talking about.
|
|
78089
|
+
*/
|
|
78090
|
+
async aggregate({ namespace, collection, fields, filter }) {
|
|
78091
|
+
const scoped = this.scopedName(namespace, collection);
|
|
78092
|
+
const decl = this.requireDeclared(scoped);
|
|
78093
|
+
const shape = this.shapeOf(decl);
|
|
78094
|
+
const selects = ['COUNT(*) AS "n"'];
|
|
78095
|
+
fields.forEach((f, i) => {
|
|
78096
|
+
const col = fieldExprFor(f.field, shape);
|
|
78097
|
+
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`);
|
|
78098
|
+
selects.push(`${f.op.toUpperCase()}(${col}) AS "a${i}"`);
|
|
78099
|
+
});
|
|
78100
|
+
const { whereSql, params } = compileFilter$1(filter, shape, "select", (v) => this.serializeColumnValue(v));
|
|
78101
|
+
const sql = `SELECT ${selects.join(", ")} FROM "${scoped}"${whereSql}`;
|
|
78102
|
+
const row = this.measured({
|
|
78103
|
+
op: "aggregate",
|
|
78104
|
+
collection: scoped,
|
|
78105
|
+
sql,
|
|
78106
|
+
params
|
|
78107
|
+
}, () => this.getDb().prepare(sql).get(...params));
|
|
78108
|
+
const values = {};
|
|
78109
|
+
fields.forEach((f, i) => {
|
|
78110
|
+
const raw = row?.[`a${i}`];
|
|
78111
|
+
values[f.as] = typeof raw === "number" && Number.isFinite(raw) ? raw : null;
|
|
78112
|
+
});
|
|
78113
|
+
return {
|
|
78114
|
+
count: row?.["n"] ?? 0,
|
|
78115
|
+
values
|
|
78116
|
+
};
|
|
78117
|
+
}
|
|
77736
78118
|
async histogram({ namespace, collection, field, bucketSize, origin, filter }) {
|
|
77737
78119
|
const scoped = this.scopedName(namespace, collection);
|
|
77738
78120
|
const decl = this.requireDeclared(scoped);
|
|
@@ -79086,7 +79468,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
79086
79468
|
[Symbol.toStringTag]: { value: "Module" }
|
|
79087
79469
|
});
|
|
79088
79470
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
79089
|
-
var require_dist10 =
|
|
79471
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
79090
79472
|
var node_crypto = __require("crypto");
|
|
79091
79473
|
var node_fs_promises = __require("fs/promises");
|
|
79092
79474
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -79655,6 +80037,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
79655
80037
|
deleteWhere: async (input) => (await engine()).deleteWhere(input),
|
|
79656
80038
|
updateWhere: async (input) => (await engine()).updateWhere(input),
|
|
79657
80039
|
count: async (input) => (await engine()).count(input),
|
|
80040
|
+
aggregate: async (input) => (await engine()).aggregate(input),
|
|
79658
80041
|
histogram: async (input) => (await engine()).histogram(input),
|
|
79659
80042
|
isEmpty: async (input) => (await engine()).isEmpty(input),
|
|
79660
80043
|
declareCollection: async (input) => (await engine()).declareCollection(input)
|
|
@@ -80965,7 +81348,7 @@ var require_system_config_addon = __commonJS({
|
|
|
80965
81348
|
[Symbol.toStringTag]: { value: "Module" }
|
|
80966
81349
|
});
|
|
80967
81350
|
require_chunk_Cek0wNdY();
|
|
80968
|
-
var require_dist10 =
|
|
81351
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
80969
81352
|
var SECTION_TITLES = {
|
|
80970
81353
|
server: "Server",
|
|
80971
81354
|
auth: "Authentication"
|
|
@@ -99026,7 +99409,7 @@ var require_winston_logging = __commonJS({
|
|
|
99026
99409
|
[Symbol.toStringTag]: { value: "Module" }
|
|
99027
99410
|
});
|
|
99028
99411
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
99029
|
-
var require_dist10 =
|
|
99412
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
99030
99413
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
99031
99414
|
var node_path = __require("path");
|
|
99032
99415
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -104814,6 +105197,7 @@ var require_addon = __commonJS({
|
|
|
104814
105197
|
"listProfiles",
|
|
104815
105198
|
"listRuntimeNodes"
|
|
104816
105199
|
],
|
|
105200
|
+
"log-channels": ["list"],
|
|
104817
105201
|
"log-destination": ["query"],
|
|
104818
105202
|
"login-method": ["getLoginMethods"],
|
|
104819
105203
|
"mqtt-broker": ["listBrokers"],
|
|
@@ -111401,11 +111785,12 @@ var require_dist2 = __commonJS({
|
|
|
111401
111785
|
}
|
|
111402
111786
|
});
|
|
111403
111787
|
|
|
111404
|
-
// ../system/dist/manifest-python-deps-
|
|
111405
|
-
var
|
|
111406
|
-
"../system/dist/manifest-python-deps-
|
|
111788
|
+
// ../system/dist/manifest-python-deps-CktMcXzS.js
|
|
111789
|
+
var require_manifest_python_deps_CktMcXzS = __commonJS({
|
|
111790
|
+
"../system/dist/manifest-python-deps-CktMcXzS.js"(exports) {
|
|
111407
111791
|
"use strict";
|
|
111408
111792
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
111793
|
+
require_dist_BVU5JADq();
|
|
111409
111794
|
var node_crypto = __require("crypto");
|
|
111410
111795
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
111411
111796
|
var _camstack_types_node = require_node();
|
|
@@ -111496,6 +111881,34 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
111496
111881
|
function shouldReclaim(s, triggerMb = HEAP_RECLAIM_TRIGGER_MB) {
|
|
111497
111882
|
return strandedMb(s) > triggerMb;
|
|
111498
111883
|
}
|
|
111884
|
+
function share(partMb, rssMb) {
|
|
111885
|
+
return rssMb > 0 ? Math.round(partMb / rssMb * 100) : 0;
|
|
111886
|
+
}
|
|
111887
|
+
function describeRss(s, budgetMb) {
|
|
111888
|
+
const nativeResidueMb = Math.max(0, strandedMb(s));
|
|
111889
|
+
const shape = s.heapUsedMb >= s.externalMb && s.heapUsedMb >= nativeResidueMb ? "v8-heap" : s.externalMb >= nativeResidueMb ? "external" : "native-residue";
|
|
111890
|
+
return {
|
|
111891
|
+
rssMb: s.rssMb,
|
|
111892
|
+
budgetMb,
|
|
111893
|
+
overMb: Math.max(0, s.rssMb - budgetMb),
|
|
111894
|
+
heapUsedMb: s.heapUsedMb,
|
|
111895
|
+
externalMb: s.externalMb,
|
|
111896
|
+
nativeResidueMb,
|
|
111897
|
+
arrayBuffersMb: s.arrayBuffersMb,
|
|
111898
|
+
shape
|
|
111899
|
+
};
|
|
111900
|
+
}
|
|
111901
|
+
var SHAPE_ADVICE = {
|
|
111902
|
+
"v8-heap": "live JS retainers \u2014 bounded by --max-old-space-size; the fix is the retained set",
|
|
111903
|
+
external: "Buffers / native external \u2014 NO V8 flag bounds this, and the old-space ceiling never will",
|
|
111904
|
+
"native-residue": "freed native memory the allocator has not returned \u2014 MALLOC_ARENA_MAX/VIPS_CONCURRENCY are already applied, so this lever is spent"
|
|
111905
|
+
};
|
|
111906
|
+
var RSS_BUDGET_RELEASE_RATIO = 0.9;
|
|
111907
|
+
var RSS_BUDGET_REANNOUNCE_MIN_MS = 9e5;
|
|
111908
|
+
function nextRssBudgetState(current, rssMb, budgetMb, releaseRatio = RSS_BUDGET_RELEASE_RATIO) {
|
|
111909
|
+
if (current === "over") return rssMb < budgetMb * releaseRatio ? "within" : "over";
|
|
111910
|
+
return rssMb > budgetMb ? "over" : "within";
|
|
111911
|
+
}
|
|
111499
111912
|
function isGcFunction(value) {
|
|
111500
111913
|
return typeof value === "function";
|
|
111501
111914
|
}
|
|
@@ -111513,12 +111926,15 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
111513
111926
|
return;
|
|
111514
111927
|
}
|
|
111515
111928
|
}
|
|
111516
|
-
function
|
|
111517
|
-
|
|
111929
|
+
function formatOverBudget(label, b, source) {
|
|
111930
|
+
return `[mem] ${label} OVER RSS BUDGET rss=${b.rssMb}MB budget=${b.budgetMb}MB over=+${b.overMb}MB shape=${b.shape} heapUsed=${b.heapUsedMb}MB(${share(b.heapUsedMb, b.rssMb)}%) external=${b.externalMb}MB(${share(b.externalMb, b.rssMb)}%) nativeResidue=${b.nativeResidueMb}MB(${share(b.nativeResidueMb, b.rssMb)}%) arrayBuffers=${b.arrayBuffersMb}MB budgetFrom="${source}" \u2014 ${SHAPE_ADVICE[b.shape]}`;
|
|
111931
|
+
}
|
|
111932
|
+
function format2(label, s, loop, budgetMb) {
|
|
111933
|
+
const line = `[mem] ${label} rss=${s.rssMb}MB heapUsed=${s.heapUsedMb}MB heapTotal=${s.heapTotalMb}MB heapLimit=${s.heapLimitMb}MB used=${Math.round(s.usedRatio * 100)}% external=${s.externalMb}MB arrayBuffers=${s.arrayBuffersMb}MB` + (budgetMb === void 0 ? "" : ` rssBudget=${budgetMb}MB`);
|
|
111518
111934
|
if (loop === void 0) return line;
|
|
111519
111935
|
return `${line} loopP50=${loop.p50Ms}ms loopP99=${loop.p99Ms}ms loopMax=${loop.maxMs}ms`;
|
|
111520
111936
|
}
|
|
111521
|
-
function startHeapWatch(label = "hub-main", sink = consoleSink, intervalMs = HEAP_WATCH_INTERVAL_MS, reclaimOptions, loopDelay = createLoopDelayMeter(), execArgv = process.execArgv, announceCeilingOrigin = true) {
|
|
111937
|
+
function startHeapWatch(label = "hub-main", sink = consoleSink, intervalMs = HEAP_WATCH_INTERVAL_MS, reclaimOptions, loopDelay = createLoopDelayMeter(), execArgv = process.execArgv, announceCeilingOrigin = true, rssBudget) {
|
|
111522
111938
|
const readMemory = reclaimOptions?.readMemory ?? (() => process.memoryUsage());
|
|
111523
111939
|
const now = reclaimOptions?.now ?? (() => Date.now());
|
|
111524
111940
|
const triggerMb = reclaimOptions?.triggerMb ?? 1024;
|
|
@@ -111560,6 +111976,29 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
111560
111976
|
};
|
|
111561
111977
|
let mode = "steady";
|
|
111562
111978
|
let lastLoggedAt = Number.NEGATIVE_INFINITY;
|
|
111979
|
+
const budgetMb = rssBudget?.budgetMb !== void 0 && rssBudget.budgetMb > 0 ? rssBudget.budgetMb : void 0;
|
|
111980
|
+
const budgetSource = rssBudget?.source ?? "unspecified";
|
|
111981
|
+
const releaseRatio = rssBudget?.releaseRatio ?? 0.9;
|
|
111982
|
+
const reannounceMinMs = rssBudget?.reannounceMinMs ?? 9e5;
|
|
111983
|
+
let budgetState = "within";
|
|
111984
|
+
let lastBudgetWarnAt = Number.NEGATIVE_INFINITY;
|
|
111985
|
+
let overBudgetAnnounced = false;
|
|
111986
|
+
const checkRssBudget = (sample, at) => {
|
|
111987
|
+
if (budgetMb === void 0) return;
|
|
111988
|
+
const previous = budgetState;
|
|
111989
|
+
budgetState = nextRssBudgetState(previous, sample.rssMb, budgetMb, releaseRatio);
|
|
111990
|
+
if (budgetState === previous) return;
|
|
111991
|
+
if (budgetState === "over") {
|
|
111992
|
+
if (at - lastBudgetWarnAt < reannounceMinMs) return;
|
|
111993
|
+
lastBudgetWarnAt = at;
|
|
111994
|
+
overBudgetAnnounced = true;
|
|
111995
|
+
sink.warn(formatOverBudget(label, describeRss(sample, budgetMb), budgetSource));
|
|
111996
|
+
return;
|
|
111997
|
+
}
|
|
111998
|
+
if (!overBudgetAnnounced) return;
|
|
111999
|
+
overBudgetAnnounced = false;
|
|
112000
|
+
sink.info(`[mem] ${label} back within its RSS budget \u2014 rss=${sample.rssMb}MB budget=${budgetMb}MB`);
|
|
112001
|
+
};
|
|
111563
112002
|
const probeIntervalMs = Math.min(fastIntervalMs, intervalMs);
|
|
111564
112003
|
const tick = () => {
|
|
111565
112004
|
try {
|
|
@@ -111570,12 +112009,13 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
111570
112009
|
const due = at - lastLoggedAt >= intervalMs;
|
|
111571
112010
|
if (mode === "escalated" || due) {
|
|
111572
112011
|
lastLoggedAt = at;
|
|
111573
|
-
const line = format2(label, sample, loopDelay?.read());
|
|
112012
|
+
const line = format2(label, sample, loopDelay?.read(), budgetMb);
|
|
111574
112013
|
if (sample.nearLimit) sink.warn(`${line} \u2014 APPROACHING HEAP LIMIT`);
|
|
111575
112014
|
else if (mode === "escalated") sink.warn(`${line} \u2014 heap elevated, sampling every ${probeIntervalMs}ms`);
|
|
111576
112015
|
else sink.info(line);
|
|
111577
112016
|
}
|
|
111578
112017
|
if (previous === "escalated" && mode === "steady") sink.info(`[mem] ${label} heap back to routine \u2014 logging every ${intervalMs}ms`);
|
|
112018
|
+
checkRssBudget(sample, at);
|
|
111579
112019
|
maybeReclaim(sample);
|
|
111580
112020
|
} catch {
|
|
111581
112021
|
}
|
|
@@ -111583,6 +112023,7 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
111583
112023
|
const timer = setInterval(tick, probeIntervalMs);
|
|
111584
112024
|
timer.unref?.();
|
|
111585
112025
|
tick();
|
|
112026
|
+
if (rssBudget !== void 0 && budgetMb === void 0) sink.info(`[mem] ${label} is NOT WATCHED against an RSS budget \u2014 no budget declared (${budgetSource}). rss growth outside the V8 heap raises nothing here: --max-old-space-size bounds old space only, and stranded subtracts external by construction. Declare execution.rssBudgetMb once this process has a measured working set.`);
|
|
111586
112027
|
if (announceCeilingOrigin && heapCeilingOrigin(execArgv) === "v8-default") sink.info(`[mem] ${label} heap ceiling is V8's DEFAULT (${read().heapLimitMb}MB) \u2014 no --max-old-space-size on argv. Nothing here CHOSE that number; it is derived from host RAM and moves with it.`);
|
|
111587
112028
|
let stopped = false;
|
|
111588
112029
|
return () => {
|
|
@@ -111593,16 +112034,40 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
111593
112034
|
};
|
|
111594
112035
|
}
|
|
111595
112036
|
var RUNNER_HEAP_WATCH_INTERVAL_MS = 3e5;
|
|
112037
|
+
function parseRssBudgetMb(raw) {
|
|
112038
|
+
if (raw === void 0) return void 0;
|
|
112039
|
+
const parsed = Number(raw);
|
|
112040
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
|
|
112041
|
+
return Math.floor(parsed);
|
|
112042
|
+
}
|
|
112043
|
+
var RUNNER_RSS_BUDGET_ENV = "CAMSTACK_RUNNER_RSS_BUDGET_MB";
|
|
112044
|
+
var HUB_RSS_BUDGET_ENV = "CAMSTACK_HUB_RSS_BUDGET_MB";
|
|
112045
|
+
var HUB_MAIN_RSS_BUDGET_MB = 4096;
|
|
112046
|
+
function hubMainRssBudget(env = process.env) {
|
|
112047
|
+
const override = env[HUB_RSS_BUDGET_ENV];
|
|
112048
|
+
if (override !== void 0) return {
|
|
112049
|
+
budgetMb: parseRssBudgetMb(override),
|
|
112050
|
+
source: HUB_RSS_BUDGET_ENV
|
|
112051
|
+
};
|
|
112052
|
+
return {
|
|
112053
|
+
budgetMb: HUB_MAIN_RSS_BUDGET_MB,
|
|
112054
|
+
source: "HUB_MAIN_RSS_BUDGET_MB (kernel/heap-watch)"
|
|
112055
|
+
};
|
|
112056
|
+
}
|
|
111596
112057
|
function startRunnerHeapWatch(options) {
|
|
111597
112058
|
if (options.heapProfile !== "heavy") return void 0;
|
|
111598
112059
|
const intervalMs = options.intervalMs ?? 3e5;
|
|
111599
|
-
|
|
112060
|
+
const rssBudget = {
|
|
112061
|
+
budgetMb: parseRssBudgetMb(options.rssBudgetMb),
|
|
112062
|
+
source: `manifest execution.rssBudgetMb (via ${RUNNER_RSS_BUDGET_ENV})`
|
|
112063
|
+
};
|
|
112064
|
+
if (options.reclaimSwitch === "off") return startHeapWatch(options.label, options.sink, intervalMs, void 0, void 0, void 0, false, rssBudget);
|
|
111600
112065
|
let reclaimOptions = options.reclaimOptions;
|
|
111601
112066
|
if (reclaimOptions === void 0) {
|
|
111602
112067
|
const reclaimer = createV8Reclaimer();
|
|
111603
112068
|
reclaimOptions = reclaimer === void 0 ? void 0 : { reclaim: reclaimer };
|
|
111604
112069
|
}
|
|
111605
|
-
return startHeapWatch(options.label, options.sink, intervalMs, reclaimOptions, void 0, void 0, true);
|
|
112070
|
+
return startHeapWatch(options.label, options.sink, intervalMs, reclaimOptions, void 0, void 0, true, rssBudget);
|
|
111606
112071
|
}
|
|
111607
112072
|
function trimSlashes(s) {
|
|
111608
112073
|
return s.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
@@ -112479,6 +112944,105 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
112479
112944
|
});
|
|
112480
112945
|
return true;
|
|
112481
112946
|
}
|
|
112947
|
+
function parseChildRows(value) {
|
|
112948
|
+
if (!Array.isArray(value)) return null;
|
|
112949
|
+
const rows = [];
|
|
112950
|
+
for (const entry of value) {
|
|
112951
|
+
if (typeof entry !== "object" || entry === null) return null;
|
|
112952
|
+
const id = Reflect.get(entry, "id");
|
|
112953
|
+
const stableId = Reflect.get(entry, "stableId");
|
|
112954
|
+
const name = Reflect.get(entry, "name");
|
|
112955
|
+
const linkDeviceId = Reflect.get(entry, "linkDeviceId");
|
|
112956
|
+
if (typeof id !== "number" || typeof stableId !== "string" || typeof name !== "string") return null;
|
|
112957
|
+
rows.push({
|
|
112958
|
+
id,
|
|
112959
|
+
stableId,
|
|
112960
|
+
name,
|
|
112961
|
+
...typeof linkDeviceId === "number" || linkDeviceId === null ? { linkDeviceId } : {}
|
|
112962
|
+
});
|
|
112963
|
+
}
|
|
112964
|
+
return rows;
|
|
112965
|
+
}
|
|
112966
|
+
function parseChildrenByParent(value) {
|
|
112967
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
112968
|
+
const out = /* @__PURE__ */ new Map();
|
|
112969
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
112970
|
+
const parentDeviceId = Number(key);
|
|
112971
|
+
if (!Number.isInteger(parentDeviceId)) return null;
|
|
112972
|
+
const rows = parseChildRows(entry);
|
|
112973
|
+
if (rows === null) return null;
|
|
112974
|
+
out.set(parentDeviceId, rows);
|
|
112975
|
+
}
|
|
112976
|
+
return out;
|
|
112977
|
+
}
|
|
112978
|
+
function looksLikeUnknownMethod(err) {
|
|
112979
|
+
const lowered = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
112980
|
+
return lowered.includes("not_found") || lowered.includes("no procedure") || lowered.includes("unknown method") || lowered.includes("no such method") || lowered.includes("not available");
|
|
112981
|
+
}
|
|
112982
|
+
function createChildrenBatchLoader(opts) {
|
|
112983
|
+
const maxBatch = Math.max(1, opts.maxBatch ?? 256);
|
|
112984
|
+
let pending = [];
|
|
112985
|
+
let flushScheduled = false;
|
|
112986
|
+
let flushing = false;
|
|
112987
|
+
let batchUnsupported = false;
|
|
112988
|
+
const answerOneByOne = async (waiters) => {
|
|
112989
|
+
await Promise.all(waiters.map(async (waiter) => {
|
|
112990
|
+
try {
|
|
112991
|
+
waiter.resolve(await opts.fetchOne(waiter.parentDeviceId));
|
|
112992
|
+
} catch (err) {
|
|
112993
|
+
waiter.reject(err);
|
|
112994
|
+
}
|
|
112995
|
+
}));
|
|
112996
|
+
};
|
|
112997
|
+
const runBatch = async (waiters) => {
|
|
112998
|
+
const parentDeviceIds = [...new Set(waiters.map((w) => w.parentDeviceId))];
|
|
112999
|
+
try {
|
|
113000
|
+
const byParent = await opts.fetchBatch(parentDeviceIds);
|
|
113001
|
+
for (const waiter of waiters) waiter.resolve(byParent.get(waiter.parentDeviceId) ?? []);
|
|
113002
|
+
} catch (err) {
|
|
113003
|
+
const unknown = looksLikeUnknownMethod(err);
|
|
113004
|
+
if (unknown) batchUnsupported = true;
|
|
113005
|
+
opts.logger.warn(unknown ? "device-manager.getChildrenBatch is not available on this hub \u2014 falling back to per-parent getChildren for the process lifetime" : "device-manager.getChildrenBatch failed \u2014 answering this batch per parent", { meta: {
|
|
113006
|
+
parents: parentDeviceIds.length,
|
|
113007
|
+
error: err instanceof Error ? err.message : String(err)
|
|
113008
|
+
} });
|
|
113009
|
+
await answerOneByOne(waiters);
|
|
113010
|
+
}
|
|
113011
|
+
};
|
|
113012
|
+
const flush = async () => {
|
|
113013
|
+
flushing = true;
|
|
113014
|
+
try {
|
|
113015
|
+
while (pending.length > 0) {
|
|
113016
|
+
const batch = pending.slice(0, maxBatch);
|
|
113017
|
+
pending = pending.slice(maxBatch);
|
|
113018
|
+
if (batchUnsupported) await answerOneByOne(batch);
|
|
113019
|
+
else await runBatch(batch);
|
|
113020
|
+
}
|
|
113021
|
+
} finally {
|
|
113022
|
+
flushing = false;
|
|
113023
|
+
}
|
|
113024
|
+
};
|
|
113025
|
+
const scheduleFlush = () => {
|
|
113026
|
+
if (flushScheduled || flushing) return;
|
|
113027
|
+
flushScheduled = true;
|
|
113028
|
+
setTimeout(() => {
|
|
113029
|
+
flushScheduled = false;
|
|
113030
|
+
flush().catch((err) => {
|
|
113031
|
+
opts.logger.warn("children batch flush threw", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
113032
|
+
});
|
|
113033
|
+
}, 0);
|
|
113034
|
+
};
|
|
113035
|
+
return { load(parentDeviceId) {
|
|
113036
|
+
return new Promise((resolve, reject) => {
|
|
113037
|
+
pending.push({
|
|
113038
|
+
parentDeviceId,
|
|
113039
|
+
resolve,
|
|
113040
|
+
reject
|
|
113041
|
+
});
|
|
113042
|
+
scheduleFlush();
|
|
113043
|
+
});
|
|
113044
|
+
} };
|
|
113045
|
+
}
|
|
112482
113046
|
var ACCESSORY_SPAWN_CONCURRENCY = 12;
|
|
112483
113047
|
async function runBounded(items, limit, worker2) {
|
|
112484
113048
|
const effectiveLimit = Math.max(1, Math.min(limit, items.length));
|
|
@@ -112686,6 +113250,19 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
112686
113250
|
api
|
|
112687
113251
|
});
|
|
112688
113252
|
let selfApi;
|
|
113253
|
+
const childrenLoader = createChildrenBatchLoader({
|
|
113254
|
+
logger: opts.logger,
|
|
113255
|
+
fetchBatch: async (parentDeviceIds) => {
|
|
113256
|
+
const parsed = parseChildrenByParent(await callDeviceManager(api, "getChildrenBatch", { parentDeviceIds }));
|
|
113257
|
+
if (parsed === null) throw new Error("getChildrenBatch returned a shape this kernel cannot read");
|
|
113258
|
+
return parsed;
|
|
113259
|
+
},
|
|
113260
|
+
fetchOne: async (parentDeviceId) => {
|
|
113261
|
+
const parsed = parseChildRows(await callDeviceManager(api, "getChildren", { parentDeviceId }));
|
|
113262
|
+
if (parsed === null) throw new Error("getChildren returned a shape this kernel cannot read");
|
|
113263
|
+
return parsed;
|
|
113264
|
+
}
|
|
113265
|
+
});
|
|
112689
113266
|
const deviceRebuildFactories = /* @__PURE__ */ new Map();
|
|
112690
113267
|
const buildContext = (stableId, id, parentDeviceId = null, initialRuntimeState = {}, persistedConfig = {}, deviceMeta = null) => {
|
|
112691
113268
|
let runtimeStateRef = null;
|
|
@@ -113097,7 +113674,7 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
113097
113674
|
return;
|
|
113098
113675
|
}
|
|
113099
113676
|
try {
|
|
113100
|
-
const currentChildren = await
|
|
113677
|
+
const currentChildren = await childrenLoader.load(device.id);
|
|
113101
113678
|
for (const child of currentChildren) {
|
|
113102
113679
|
if (child.linkDeviceId !== device.id) continue;
|
|
113103
113680
|
if (expected.has(child.stableId)) continue;
|
|
@@ -118046,6 +118623,18 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
118046
118623
|
return HUB_CAP_FWD_SERVICE;
|
|
118047
118624
|
}
|
|
118048
118625
|
});
|
|
118626
|
+
Object.defineProperty(exports, "HUB_MAIN_RSS_BUDGET_MB", {
|
|
118627
|
+
enumerable: true,
|
|
118628
|
+
get: function() {
|
|
118629
|
+
return HUB_MAIN_RSS_BUDGET_MB;
|
|
118630
|
+
}
|
|
118631
|
+
});
|
|
118632
|
+
Object.defineProperty(exports, "HUB_RSS_BUDGET_ENV", {
|
|
118633
|
+
enumerable: true,
|
|
118634
|
+
get: function() {
|
|
118635
|
+
return HUB_RSS_BUDGET_ENV;
|
|
118636
|
+
}
|
|
118637
|
+
});
|
|
118049
118638
|
Object.defineProperty(exports, "LocalChildClient", {
|
|
118050
118639
|
enumerable: true,
|
|
118051
118640
|
get: function() {
|
|
@@ -118064,12 +118653,30 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
118064
118653
|
return NATIVE_PROVIDER_SERVICE_INFIX;
|
|
118065
118654
|
}
|
|
118066
118655
|
});
|
|
118656
|
+
Object.defineProperty(exports, "RSS_BUDGET_REANNOUNCE_MIN_MS", {
|
|
118657
|
+
enumerable: true,
|
|
118658
|
+
get: function() {
|
|
118659
|
+
return RSS_BUDGET_REANNOUNCE_MIN_MS;
|
|
118660
|
+
}
|
|
118661
|
+
});
|
|
118662
|
+
Object.defineProperty(exports, "RSS_BUDGET_RELEASE_RATIO", {
|
|
118663
|
+
enumerable: true,
|
|
118664
|
+
get: function() {
|
|
118665
|
+
return RSS_BUDGET_RELEASE_RATIO;
|
|
118666
|
+
}
|
|
118667
|
+
});
|
|
118067
118668
|
Object.defineProperty(exports, "RUNNER_HEAP_WATCH_INTERVAL_MS", {
|
|
118068
118669
|
enumerable: true,
|
|
118069
118670
|
get: function() {
|
|
118070
118671
|
return RUNNER_HEAP_WATCH_INTERVAL_MS;
|
|
118071
118672
|
}
|
|
118072
118673
|
});
|
|
118674
|
+
Object.defineProperty(exports, "RUNNER_RSS_BUDGET_ENV", {
|
|
118675
|
+
enumerable: true,
|
|
118676
|
+
get: function() {
|
|
118677
|
+
return RUNNER_RSS_BUDGET_ENV;
|
|
118678
|
+
}
|
|
118679
|
+
});
|
|
118073
118680
|
Object.defineProperty(exports, "SocketChannel", {
|
|
118074
118681
|
enumerable: true,
|
|
118075
118682
|
get: function() {
|
|
@@ -118274,6 +118881,12 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
118274
118881
|
return createV8Reclaimer;
|
|
118275
118882
|
}
|
|
118276
118883
|
});
|
|
118884
|
+
Object.defineProperty(exports, "describeRss", {
|
|
118885
|
+
enumerable: true,
|
|
118886
|
+
get: function() {
|
|
118887
|
+
return describeRss;
|
|
118888
|
+
}
|
|
118889
|
+
});
|
|
118277
118890
|
Object.defineProperty(exports, "deserializeTypedArrays", {
|
|
118278
118891
|
enumerable: true,
|
|
118279
118892
|
get: function() {
|
|
@@ -118334,6 +118947,12 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
118334
118947
|
return getWorkerNativeCapSnapshot;
|
|
118335
118948
|
}
|
|
118336
118949
|
});
|
|
118950
|
+
Object.defineProperty(exports, "hubMainRssBudget", {
|
|
118951
|
+
enumerable: true,
|
|
118952
|
+
get: function() {
|
|
118953
|
+
return hubMainRssBudget;
|
|
118954
|
+
}
|
|
118955
|
+
});
|
|
118337
118956
|
Object.defineProperty(exports, "installManifestNativeDeps", {
|
|
118338
118957
|
enumerable: true,
|
|
118339
118958
|
get: function() {
|
|
@@ -118370,12 +118989,24 @@ var require_manifest_python_deps_B_mCU6gz = __commonJS({
|
|
|
118370
118989
|
return mountNativeCapService;
|
|
118371
118990
|
}
|
|
118372
118991
|
});
|
|
118992
|
+
Object.defineProperty(exports, "nextRssBudgetState", {
|
|
118993
|
+
enumerable: true,
|
|
118994
|
+
get: function() {
|
|
118995
|
+
return nextRssBudgetState;
|
|
118996
|
+
}
|
|
118997
|
+
});
|
|
118373
118998
|
Object.defineProperty(exports, "parseCapAction", {
|
|
118374
118999
|
enumerable: true,
|
|
118375
119000
|
get: function() {
|
|
118376
119001
|
return parseCapAction;
|
|
118377
119002
|
}
|
|
118378
119003
|
});
|
|
119004
|
+
Object.defineProperty(exports, "parseRssBudgetMb", {
|
|
119005
|
+
enumerable: true,
|
|
119006
|
+
get: function() {
|
|
119007
|
+
return parseRssBudgetMb;
|
|
119008
|
+
}
|
|
119009
|
+
});
|
|
118379
119010
|
Object.defineProperty(exports, "registerEventBusService", {
|
|
118380
119011
|
enumerable: true,
|
|
118381
119012
|
get: function() {
|
|
@@ -122274,7 +122905,7 @@ var require_dist3 = __commonJS({
|
|
|
122274
122905
|
"use strict";
|
|
122275
122906
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
122276
122907
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
122277
|
-
var require_dist10 =
|
|
122908
|
+
var require_dist10 = require_dist_BVU5JADq();
|
|
122278
122909
|
var require_builtins_alerts_alerts_addon = require_alerts_addon();
|
|
122279
122910
|
require_alerts();
|
|
122280
122911
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
@@ -122300,7 +122931,7 @@ var require_dist3 = __commonJS({
|
|
|
122300
122931
|
var require_builtins_winston_logging_index = require_winston_logging();
|
|
122301
122932
|
var require_file_data_plane = require_file_data_plane_DO8KbxCe();
|
|
122302
122933
|
var require_tls$1 = require_tls_u8QCJCFE();
|
|
122303
|
-
var require_manifest_python_deps =
|
|
122934
|
+
var require_manifest_python_deps = require_manifest_python_deps_CktMcXzS();
|
|
122304
122935
|
var require_resource_monitor = require_resource_monitor_CdnzxBLP();
|
|
122305
122936
|
var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
|
|
122306
122937
|
var zod = require_zod();
|
|
@@ -201766,7 +202397,8 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
201766
202397
|
}
|
|
201767
202398
|
var EMPTY_HEAP_DECLARATION = {
|
|
201768
202399
|
profile: void 0,
|
|
201769
|
-
maxOldSpaceMb: void 0
|
|
202400
|
+
maxOldSpaceMb: void 0,
|
|
202401
|
+
rssBudgetMb: void 0
|
|
201770
202402
|
};
|
|
201771
202403
|
var heapProfileCache = /* @__PURE__ */ new Map();
|
|
201772
202404
|
function readAddonHeapDeclaration(spec) {
|
|
@@ -201779,10 +202411,12 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
201779
202411
|
const parsed = JSON.parse(raw);
|
|
201780
202412
|
const profile = extractHeapProfile(parsed, spec.addonId);
|
|
201781
202413
|
const maxOldSpaceMb = extractMaxOldSpaceMb(parsed, spec.addonId);
|
|
201782
|
-
|
|
202414
|
+
const rssBudgetMb = extractRssBudgetMb(parsed, spec.addonId);
|
|
202415
|
+
if (profile !== void 0 || maxOldSpaceMb !== void 0 || rssBudgetMb !== void 0) {
|
|
201783
202416
|
declaration = {
|
|
201784
202417
|
profile,
|
|
201785
|
-
maxOldSpaceMb
|
|
202418
|
+
maxOldSpaceMb,
|
|
202419
|
+
rssBudgetMb
|
|
201786
202420
|
};
|
|
201787
202421
|
break;
|
|
201788
202422
|
}
|
|
@@ -201806,6 +202440,10 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
201806
202440
|
const value = readManifestAddons(parsed).find((a) => a.id === addonId)?.execution?.maxOldSpaceMb;
|
|
201807
202441
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : void 0;
|
|
201808
202442
|
}
|
|
202443
|
+
function extractRssBudgetMb(parsed, addonId) {
|
|
202444
|
+
const value = readManifestAddons(parsed).find((a) => a.id === addonId)?.execution?.rssBudgetMb;
|
|
202445
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
|
|
202446
|
+
}
|
|
201809
202447
|
function readManifestAddons(parsed) {
|
|
201810
202448
|
if (typeof parsed !== "object" || parsed === null) return [];
|
|
201811
202449
|
const camstack = parsed.camstack;
|
|
@@ -201824,6 +202462,10 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
201824
202462
|
if (declared.includes(0)) return 0;
|
|
201825
202463
|
return Math.max(...declared);
|
|
201826
202464
|
}
|
|
202465
|
+
function runnerRssBudgetMb(addons) {
|
|
202466
|
+
const declared = addons.map((a) => readAddonHeapDeclaration(a).rssBudgetMb).filter((mb) => mb !== void 0);
|
|
202467
|
+
return declared.length === 0 ? void 0 : Math.max(...declared);
|
|
202468
|
+
}
|
|
201827
202469
|
function runnerHeapFlags(addons) {
|
|
201828
202470
|
if (process.env["CAMSTACK_RUNNER_HEAP_TUNING"] === "off") return [];
|
|
201829
202471
|
const heavy = isHeavyRunner(addons);
|
|
@@ -201880,6 +202522,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
201880
202522
|
const nodeId = buildNodeId(runnerId);
|
|
201881
202523
|
const runnerPath = node_path.resolve(__dirname, "addon-runner.js");
|
|
201882
202524
|
const heavy = isHeavyRunner(addons);
|
|
202525
|
+
const rssBudgetMb = runnerRssBudgetMb(addons);
|
|
201883
202526
|
const childEnv = {
|
|
201884
202527
|
...process.env,
|
|
201885
202528
|
CAMSTACK_RUNNER_ID: runnerId,
|
|
@@ -201893,6 +202536,8 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
201893
202536
|
...applyRunnerNativeAllocator(process.env),
|
|
201894
202537
|
...env
|
|
201895
202538
|
};
|
|
202539
|
+
if (rssBudgetMb === void 0) delete childEnv[require_manifest_python_deps.RUNNER_RSS_BUDGET_ENV];
|
|
202540
|
+
else childEnv[require_manifest_python_deps.RUNNER_RSS_BUDGET_ENV] = String(rssBudgetMb);
|
|
201896
202541
|
const heapFlags = runnerHeapFlags(addons);
|
|
201897
202542
|
capturedBroker?.logger.info(`[${runnerId}] heap profile: ${heavy ? "heavy" : "light"} flags=[${heapFlags.join(" ")}] arenas=${childEnv["MALLOC_ARENA_MAX"] ?? "glibc-default"} vips=${childEnv["VIPS_CONCURRENCY"] ?? "sharp-default"}`);
|
|
201898
202543
|
const child = spawnFn(process.execPath, [...heapFlags, runnerPath], {
|
|
@@ -202683,6 +203328,8 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
202683
203328
|
exports.HEAP_WATCH_WARN_RATIO = require_manifest_python_deps.HEAP_WATCH_WARN_RATIO;
|
|
202684
203329
|
exports.HUB_CAP_FWD_ACTION = require_manifest_python_deps.HUB_CAP_FWD_ACTION;
|
|
202685
203330
|
exports.HUB_CAP_FWD_SERVICE = require_manifest_python_deps.HUB_CAP_FWD_SERVICE;
|
|
203331
|
+
exports.HUB_MAIN_RSS_BUDGET_MB = require_manifest_python_deps.HUB_MAIN_RSS_BUDGET_MB;
|
|
203332
|
+
exports.HUB_RSS_BUDGET_ENV = require_manifest_python_deps.HUB_RSS_BUDGET_ENV;
|
|
202686
203333
|
exports.HubForwarderAddon = require_builtins_hub_forwarder_index.HubForwarderAddon$1;
|
|
202687
203334
|
exports.HubForwarderDestination = require_builtins_hub_forwarder_index.HubForwarderDestination$1;
|
|
202688
203335
|
exports.HubLogForwarder = HubLogForwarder;
|
|
@@ -202722,7 +203369,10 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
202722
203369
|
exports.PythonEnvManager = PythonEnvManager;
|
|
202723
203370
|
exports.QUARANTINE_DIRNAME = QUARANTINE_DIRNAME;
|
|
202724
203371
|
exports.RESTART_MARKER_FILE = RESTART_MARKER_FILE;
|
|
203372
|
+
exports.RSS_BUDGET_REANNOUNCE_MIN_MS = require_manifest_python_deps.RSS_BUDGET_REANNOUNCE_MIN_MS;
|
|
203373
|
+
exports.RSS_BUDGET_RELEASE_RATIO = require_manifest_python_deps.RSS_BUDGET_RELEASE_RATIO;
|
|
202725
203374
|
exports.RUNNER_HEAP_WATCH_INTERVAL_MS = require_manifest_python_deps.RUNNER_HEAP_WATCH_INTERVAL_MS;
|
|
203375
|
+
exports.RUNNER_RSS_BUDGET_ENV = require_manifest_python_deps.RUNNER_RSS_BUDGET_ENV;
|
|
202726
203376
|
exports.RUNTIME_DEFAULTS = require_dist10.RUNTIME_DEFAULTS;
|
|
202727
203377
|
exports.ReadinessRegistry = require_dist10.ReadinessRegistry;
|
|
202728
203378
|
exports.ReadinessTimeoutError = require_dist10.ReadinessTimeoutError;
|
|
@@ -202819,6 +203469,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
202819
203469
|
exports.deleteModelFromDisk = require_file_data_plane.deleteModelFromDisk;
|
|
202820
203470
|
exports.deriveAgentListenPort = deriveAgentListenPort;
|
|
202821
203471
|
exports.describeProviderKindDrift = describeProviderKindDrift;
|
|
203472
|
+
exports.describeRss = require_manifest_python_deps.describeRss;
|
|
202822
203473
|
exports.detectWorkspacePackagesDir = detectWorkspacePackagesDir;
|
|
202823
203474
|
Object.defineProperty(exports, "downloadBinary", {
|
|
202824
203475
|
enumerable: true,
|
|
@@ -202895,6 +203546,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
202895
203546
|
exports.getWorkerDeviceRegistry = require_manifest_python_deps.getWorkerDeviceRegistry;
|
|
202896
203547
|
exports.hasDotNode = hasDotNode;
|
|
202897
203548
|
exports.hashClusterSecret = hashClusterSecret;
|
|
203549
|
+
exports.hubMainRssBudget = require_manifest_python_deps.hubMainRssBudget;
|
|
202898
203550
|
exports.installManifestNativeDeps = require_manifest_python_deps.installManifestNativeDeps;
|
|
202899
203551
|
exports.installManifestPythonDeps = require_manifest_python_deps.installManifestPythonDeps;
|
|
202900
203552
|
exports.installPackageFromNpm = installPackageFromNpm;
|
|
@@ -202923,8 +203575,10 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
202923
203575
|
exports.localEndpointPath = require_manifest_python_deps.localEndpointPath;
|
|
202924
203576
|
exports.localProviderLink = require_manifest_python_deps.localProviderLink;
|
|
202925
203577
|
exports.mountNativeCapService = require_manifest_python_deps.mountNativeCapService;
|
|
203578
|
+
exports.nextRssBudgetState = require_manifest_python_deps.nextRssBudgetState;
|
|
202926
203579
|
exports.parseCapAction = require_manifest_python_deps.parseCapAction;
|
|
202927
203580
|
exports.parseRangeHeader = require_file_data_plane.parseRangeHeader;
|
|
203581
|
+
exports.parseRssBudgetMb = require_manifest_python_deps.parseRssBudgetMb;
|
|
202928
203582
|
exports.parseTokenizedUrl = require_file_data_plane.parseTokenizedUrl;
|
|
202929
203583
|
exports.partitionIsolatedBuiltinIds = partitionIsolatedBuiltinIds;
|
|
202930
203584
|
exports.proxyToUpstream = proxyToUpstream;
|
|
@@ -203840,6 +204494,253 @@ var require_dist4 = __commonJS({
|
|
|
203840
204494
|
function logLevelAtMost(level, threshold) {
|
|
203841
204495
|
return LOG_LEVEL_RANK[level] <= LOG_LEVEL_RANK[threshold];
|
|
203842
204496
|
}
|
|
204497
|
+
var LogChannelLevelSchema = zod.z.enum([
|
|
204498
|
+
"info",
|
|
204499
|
+
"warn",
|
|
204500
|
+
"error"
|
|
204501
|
+
]);
|
|
204502
|
+
var LogChannelDescriptorSchema = zod.z.object({
|
|
204503
|
+
/**
|
|
204504
|
+
* Dotted `area.thing`, unique across the workspace. `area` is conventionally
|
|
204505
|
+
* the addon's short name so an operator reading a channel list can tell who
|
|
204506
|
+
* owns it without a second lookup.
|
|
204507
|
+
*/
|
|
204508
|
+
name: zod.z.string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
|
|
204509
|
+
/** One sentence: what the operator will SEE after arming it. */
|
|
204510
|
+
description: zod.z.string().min(1),
|
|
204511
|
+
/** The level its lines are emitted at. Never below `info`. */
|
|
204512
|
+
defaultLevel: LogChannelLevelSchema,
|
|
204513
|
+
/**
|
|
204514
|
+
* Whether this channel can be narrowed to a camera.
|
|
204515
|
+
*
|
|
204516
|
+
* `true` is a PROMISE with two halves, and both must hold: the gate is
|
|
204517
|
+
* consulted with the numeric device id, AND every line the channel admits
|
|
204518
|
+
* carries `tags: { deviceId }` with that same numeric id. The second half is
|
|
204519
|
+
* what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
|
|
204520
|
+
* keeps `deviceId` out of the stream labels for cardinality, so the tag in
|
|
204521
|
+
* the body is the only way to filter.
|
|
204522
|
+
*
|
|
204523
|
+
* A channel whose lines carry the device only in `meta` (or not at all) is
|
|
204524
|
+
* declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
|
|
204525
|
+
* the operator narrows to one camera, sees nothing, and concludes the code
|
|
204526
|
+
* path was never taken.
|
|
204527
|
+
*/
|
|
204528
|
+
perDevice: zod.z.boolean()
|
|
204529
|
+
});
|
|
204530
|
+
var LogChannelWindowSchema = zod.z.object({
|
|
204531
|
+
channel: zod.z.string().min(1),
|
|
204532
|
+
/** Epoch ms the window closes at. */
|
|
204533
|
+
armedUntilMs: zod.z.number(),
|
|
204534
|
+
/** `null` = every camera. A non-empty list narrows to those numeric ids. */
|
|
204535
|
+
deviceIds: zod.z.array(zod.z.number().int()).readonly().nullable()
|
|
204536
|
+
});
|
|
204537
|
+
var LogChannelGate = class {
|
|
204538
|
+
descriptor;
|
|
204539
|
+
/**
|
|
204540
|
+
* HOT PATH GUARD. A plain data FIELD, and it must stay one.
|
|
204541
|
+
*
|
|
204542
|
+
* `log-channel.spec.ts` asserts the property descriptor has no getter and
|
|
204543
|
+
* booby-traps the device set, so turning this into an accessor — or reading
|
|
204544
|
+
* anything before it — fails the spec instead of taxing every line the
|
|
204545
|
+
* process emits.
|
|
204546
|
+
*/
|
|
204547
|
+
on = false;
|
|
204548
|
+
/** `null` while armed for every camera. Never read while `on` is false. */
|
|
204549
|
+
devices = null;
|
|
204550
|
+
level;
|
|
204551
|
+
closesAtMs = 0;
|
|
204552
|
+
constructor(descriptor) {
|
|
204553
|
+
this.descriptor = descriptor;
|
|
204554
|
+
this.level = descriptor.defaultLevel;
|
|
204555
|
+
}
|
|
204556
|
+
/** Epoch ms this channel disarms itself at. 0 when disarmed. */
|
|
204557
|
+
get armedUntilMs() {
|
|
204558
|
+
return this.on ? this.closesAtMs : 0;
|
|
204559
|
+
}
|
|
204560
|
+
/**
|
|
204561
|
+
* Does this channel want a line about `deviceId`?
|
|
204562
|
+
*
|
|
204563
|
+
* Call it only behind `gate.on &&`. On its own it is still correct — the
|
|
204564
|
+
* guard is repeated inside — but the point of the prefix is that a disarmed
|
|
204565
|
+
* channel must not pay the call at all.
|
|
204566
|
+
*/
|
|
204567
|
+
wants(deviceId) {
|
|
204568
|
+
if (!this.on) return false;
|
|
204569
|
+
return this.devices === null || this.devices.has(deviceId);
|
|
204570
|
+
}
|
|
204571
|
+
/**
|
|
204572
|
+
* Emit one line on this channel, at the channel's declared level.
|
|
204573
|
+
*
|
|
204574
|
+
* The channel name is added as `tags.logChannel` so LogQL can select the
|
|
204575
|
+
* channel without matching on the message text, and whatever `tags` the
|
|
204576
|
+
* caller passed — `deviceId` above all — is preserved.
|
|
204577
|
+
*/
|
|
204578
|
+
log(logger, message, extras) {
|
|
204579
|
+
if (!this.on) return;
|
|
204580
|
+
const tags = {
|
|
204581
|
+
...extras.tags,
|
|
204582
|
+
logChannel: this.descriptor.name
|
|
204583
|
+
};
|
|
204584
|
+
const line = {
|
|
204585
|
+
...extras,
|
|
204586
|
+
tags
|
|
204587
|
+
};
|
|
204588
|
+
if (this.level === "error") logger.error(message, line);
|
|
204589
|
+
else if (this.level === "warn") logger.warn(message, line);
|
|
204590
|
+
else logger.info(message, line);
|
|
204591
|
+
}
|
|
204592
|
+
/**
|
|
204593
|
+
* Arm (or RE-arm, restarting) this channel. Off the hot path only.
|
|
204594
|
+
*
|
|
204595
|
+
* An empty `deviceIds` list is treated as "every camera" rather than "no
|
|
204596
|
+
* camera": a window that matches nothing is indistinguishable from a
|
|
204597
|
+
* disarmed one, and the operator who asked for it would wait for lines that
|
|
204598
|
+
* can never come.
|
|
204599
|
+
*/
|
|
204600
|
+
arm(window2) {
|
|
204601
|
+
const ids = window2.deviceIds;
|
|
204602
|
+
this.devices = ids === null || ids.length === 0 ? null : new Set(ids);
|
|
204603
|
+
this.closesAtMs = window2.armedUntilMs;
|
|
204604
|
+
this.on = true;
|
|
204605
|
+
}
|
|
204606
|
+
/** Disarm. Off the hot path only. */
|
|
204607
|
+
disarm() {
|
|
204608
|
+
this.on = false;
|
|
204609
|
+
this.devices = null;
|
|
204610
|
+
this.closesAtMs = 0;
|
|
204611
|
+
}
|
|
204612
|
+
};
|
|
204613
|
+
var LogChannelRegistry = class {
|
|
204614
|
+
gates = /* @__PURE__ */ new Map();
|
|
204615
|
+
/**
|
|
204616
|
+
* Declare a channel and get its gate.
|
|
204617
|
+
*
|
|
204618
|
+
* A duplicate name throws. Two declarations of one name is a programming
|
|
204619
|
+
* error, not a merge: the operator would arm one and the other would stay
|
|
204620
|
+
* dark, which is the dead-knob shape (D62) with an extra step.
|
|
204621
|
+
*/
|
|
204622
|
+
declare(descriptor) {
|
|
204623
|
+
const parsed = LogChannelDescriptorSchema.parse(descriptor);
|
|
204624
|
+
if (this.gates.get(parsed.name) !== void 0) throw new Error(`log channel "${parsed.name}" is already declared in this process \u2014 two declarations of one name is a programming error, not a merge`);
|
|
204625
|
+
const gate = new LogChannelGate(parsed);
|
|
204626
|
+
this.gates.set(parsed.name, gate);
|
|
204627
|
+
return gate;
|
|
204628
|
+
}
|
|
204629
|
+
/** The declarations, sorted by name so a list is stable to read and diff. */
|
|
204630
|
+
list() {
|
|
204631
|
+
return [...this.gates.values()].map((gate) => gate.descriptor).sort((a, b) => a.name.localeCompare(b.name));
|
|
204632
|
+
}
|
|
204633
|
+
/** The gate for a declared channel, or `undefined`. */
|
|
204634
|
+
gate(name) {
|
|
204635
|
+
return this.gates.get(name);
|
|
204636
|
+
}
|
|
204637
|
+
/**
|
|
204638
|
+
* Apply the FULL set of armed windows. Off the hot path.
|
|
204639
|
+
*
|
|
204640
|
+
* Full, not incremental, and that is the whole design: the document is the
|
|
204641
|
+
* authority, so a channel the document does not name is disarmed here. An
|
|
204642
|
+
* incremental apply would let a disarm get lost in transit and leave a
|
|
204643
|
+
* channel running that nobody can see is running.
|
|
204644
|
+
*
|
|
204645
|
+
* A window already past its deadline is ignored rather than armed — a
|
|
204646
|
+
* restore that re-armed an expired window would make a forgotten diagnostic
|
|
204647
|
+
* immortal across restarts.
|
|
204648
|
+
*
|
|
204649
|
+
* Returns the names it could not place, so the caller can log them: a
|
|
204650
|
+
* channel named in the document that this process does not declare is
|
|
204651
|
+
* either a typo or an addon that has not booted yet, and both deserve a
|
|
204652
|
+
* line rather than silence.
|
|
204653
|
+
*/
|
|
204654
|
+
apply(windows, nowMs) {
|
|
204655
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
204656
|
+
const unknown = [];
|
|
204657
|
+
for (const window2 of windows) {
|
|
204658
|
+
if (window2.armedUntilMs <= nowMs) continue;
|
|
204659
|
+
if (!this.gates.has(window2.channel)) {
|
|
204660
|
+
unknown.push(window2.channel);
|
|
204661
|
+
continue;
|
|
204662
|
+
}
|
|
204663
|
+
wanted.set(window2.channel, window2);
|
|
204664
|
+
}
|
|
204665
|
+
for (const [name, gate] of this.gates) {
|
|
204666
|
+
const window2 = wanted.get(name);
|
|
204667
|
+
if (window2 === void 0) gate.disarm();
|
|
204668
|
+
else gate.arm(window2);
|
|
204669
|
+
}
|
|
204670
|
+
return unknown;
|
|
204671
|
+
}
|
|
204672
|
+
/**
|
|
204673
|
+
* Disarm whatever has run out. Called on a timer, NEVER from a log path — a
|
|
204674
|
+
* diagnostic that adds a `Date.now()` to the path it is measuring measures
|
|
204675
|
+
* itself.
|
|
204676
|
+
*
|
|
204677
|
+
* Returns the names it closed, so the caller can write the one line that
|
|
204678
|
+
* says a window ended and stops "it went quiet" from reading as "the branch
|
|
204679
|
+
* was not taken".
|
|
204680
|
+
*/
|
|
204681
|
+
tick(nowMs) {
|
|
204682
|
+
const closed = [];
|
|
204683
|
+
for (const [name, gate] of this.gates) if (gate.on && gate.armedUntilMs <= nowMs) {
|
|
204684
|
+
gate.disarm();
|
|
204685
|
+
closed.push(name);
|
|
204686
|
+
}
|
|
204687
|
+
return closed;
|
|
204688
|
+
}
|
|
204689
|
+
/** The channels armed right now, as the document would describe them. */
|
|
204690
|
+
armed() {
|
|
204691
|
+
const out = [];
|
|
204692
|
+
for (const [name, gate] of this.gates) if (gate.on) out.push({
|
|
204693
|
+
channel: name,
|
|
204694
|
+
armedUntilMs: gate.armedUntilMs,
|
|
204695
|
+
deviceIds: null
|
|
204696
|
+
});
|
|
204697
|
+
return out;
|
|
204698
|
+
}
|
|
204699
|
+
};
|
|
204700
|
+
var instance = null;
|
|
204701
|
+
function getLogChannelRegistry() {
|
|
204702
|
+
instance ??= new LogChannelRegistry();
|
|
204703
|
+
return instance;
|
|
204704
|
+
}
|
|
204705
|
+
function declareLogChannel(descriptor) {
|
|
204706
|
+
return getLogChannelRegistry().declare(descriptor);
|
|
204707
|
+
}
|
|
204708
|
+
function __resetLogChannelRegistryForTests() {
|
|
204709
|
+
instance = null;
|
|
204710
|
+
}
|
|
204711
|
+
var LOG_CHANNEL_TICK_MS = 5e3;
|
|
204712
|
+
function createLogChannelsProvider(logger, options = {}) {
|
|
204713
|
+
const registry = getLogChannelRegistry();
|
|
204714
|
+
const now = options.now ?? Date.now;
|
|
204715
|
+
const tickMs = options.tickMs ?? 5e3;
|
|
204716
|
+
const timer = setInterval(() => {
|
|
204717
|
+
const closed = registry.tick(now());
|
|
204718
|
+
for (const name of closed) logger.info("log channel window closed", {
|
|
204719
|
+
tags: { logChannel: name },
|
|
204720
|
+
meta: { channel: name }
|
|
204721
|
+
});
|
|
204722
|
+
}, tickMs);
|
|
204723
|
+
timer.unref?.();
|
|
204724
|
+
return {
|
|
204725
|
+
list: () => registry.list(),
|
|
204726
|
+
apply: (input) => {
|
|
204727
|
+
const unknown = registry.apply(input.windows, now());
|
|
204728
|
+
const armed = registry.armed();
|
|
204729
|
+
logger.info("log channels applied", { meta: {
|
|
204730
|
+
armed: armed.map((window2) => window2.channel),
|
|
204731
|
+
unknown,
|
|
204732
|
+
declared: registry.list().length
|
|
204733
|
+
} });
|
|
204734
|
+
return {
|
|
204735
|
+
armed: armed.length,
|
|
204736
|
+
unknown
|
|
204737
|
+
};
|
|
204738
|
+
},
|
|
204739
|
+
stop: () => {
|
|
204740
|
+
clearInterval(timer);
|
|
204741
|
+
}
|
|
204742
|
+
};
|
|
204743
|
+
}
|
|
203843
204744
|
var OpsLogDomainSchema = zod.z.enum(["recording", "events"]);
|
|
203844
204745
|
var OpsLogOpSchema = zod.z.enum([
|
|
203845
204746
|
"prune",
|
|
@@ -208416,6 +209317,21 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
208416
209317
|
whereBetween: zod.z.record(zod.z.string(), zod.z.tuple([zod.z.unknown(), zod.z.unknown()])).optional(),
|
|
208417
209318
|
whereNot: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
208418
209319
|
});
|
|
209320
|
+
var AggregateFieldSchema = zod.z.object({
|
|
209321
|
+
/** Result key. */
|
|
209322
|
+
as: zod.z.string().min(1),
|
|
209323
|
+
/** Column to aggregate. Must be a real column of a declared collection. */
|
|
209324
|
+
field: zod.z.string().min(1),
|
|
209325
|
+
op: zod.z.enum([
|
|
209326
|
+
"sum",
|
|
209327
|
+
"min",
|
|
209328
|
+
"max"
|
|
209329
|
+
])
|
|
209330
|
+
});
|
|
209331
|
+
var AggregateResultSchema = zod.z.object({
|
|
209332
|
+
count: zod.z.number().int(),
|
|
209333
|
+
values: zod.z.record(zod.z.string(), zod.z.number().nullable())
|
|
209334
|
+
});
|
|
208419
209335
|
var SettingsRecordSchema = zod.z.object({
|
|
208420
209336
|
id: zod.z.string(),
|
|
208421
209337
|
data: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
@@ -208543,6 +209459,32 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
208543
209459
|
collection: zod.z.string(),
|
|
208544
209460
|
filter: QueryFilterSchema.optional()
|
|
208545
209461
|
}), zod.z.number()),
|
|
209462
|
+
/**
|
|
209463
|
+
* `COUNT(*)` and one `SUM` / `MIN` / `MAX` per requested field, in ONE
|
|
209464
|
+
* statement, over the rows `filter` selects.
|
|
209465
|
+
*
|
|
209466
|
+
* Exists because "how much is there" was being answered by materialising
|
|
209467
|
+
* "what is there". The recorder's storage-pressure sweep asked its in-RAM
|
|
209468
|
+
* footage index for bytes/count/oldest/newest across a set of storage
|
|
209469
|
+
* locations twice a minute, and the only way to answer that from a map is
|
|
209470
|
+
* to visit every row — 7.1 M of them on the live hub, ~15 M row visits a
|
|
209471
|
+
* minute on the main thread, which is also why the whole archive had to
|
|
209472
|
+
* stay resident to be visited. The question is a sum; nothing needs to be
|
|
209473
|
+
* materialised to answer it.
|
|
209474
|
+
*
|
|
209475
|
+
* **The engine REFUSES a field it cannot serve**, exactly as
|
|
209476
|
+
* `query.columns` does and unlike a PREDICATE, which is skipped when
|
|
209477
|
+
* unresolvable. A dropped predicate over-matches and the caller sees extra
|
|
209478
|
+
* rows; a dropped aggregate returns a NUMBER that is wrong and looks
|
|
209479
|
+
* exactly like a real one. That asymmetry is what this repo has already
|
|
209480
|
+
* paid for once in `count`.
|
|
209481
|
+
*/
|
|
209482
|
+
aggregate: require_sleep.method(zod.z.object({
|
|
209483
|
+
namespace: zod.z.string().optional(),
|
|
209484
|
+
collection: zod.z.string(),
|
|
209485
|
+
fields: zod.z.array(AggregateFieldSchema).readonly(),
|
|
209486
|
+
filter: QueryFilterSchema.optional()
|
|
209487
|
+
}), AggregateResultSchema),
|
|
208546
209488
|
/** Grouped counts per ((field-origin)/bucketSize) bucket, filtered. */
|
|
208547
209489
|
histogram: require_sleep.method(zod.z.object({
|
|
208548
209490
|
namespace: zod.z.string().optional(),
|
|
@@ -208701,6 +209643,15 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
208701
209643
|
collection: zod.z.string(),
|
|
208702
209644
|
filter: QueryFilterSchema.optional()
|
|
208703
209645
|
}), zod.z.number(), { auth: "admin" }),
|
|
209646
|
+
/** `COUNT(*)` plus one SUM/MIN/MAX per field, in one statement. Mirror of
|
|
209647
|
+
* `settings-store.aggregate` — see it for why an unresolvable field is
|
|
209648
|
+
* refused rather than dropped. */
|
|
209649
|
+
aggregate: require_sleep.method(zod.z.object({
|
|
209650
|
+
namespace: zod.z.string().optional(),
|
|
209651
|
+
collection: zod.z.string(),
|
|
209652
|
+
fields: zod.z.array(AggregateFieldSchema).readonly(),
|
|
209653
|
+
filter: QueryFilterSchema.optional()
|
|
209654
|
+
}), AggregateResultSchema, { auth: "admin" }),
|
|
208704
209655
|
/** Grouped counts per ((field-origin)/bucketSize) bucket, filtered. */
|
|
208705
209656
|
histogram: require_sleep.method(zod.z.object({
|
|
208706
209657
|
namespace: zod.z.string().optional(),
|
|
@@ -209415,6 +210366,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
209415
210366
|
})
|
|
209416
210367
|
}
|
|
209417
210368
|
};
|
|
210369
|
+
var DEVICE_CHILDREN_BATCH_MAX = 256;
|
|
209418
210370
|
var ChildLayoutEntrySchema = zod.z.object({
|
|
209419
210371
|
childKey: zod.z.string(),
|
|
209420
210372
|
section: zod.z.string(),
|
|
@@ -209896,6 +210848,39 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
209896
210848
|
/** List children of a parent device (by parent numeric id). */
|
|
209897
210849
|
getChildren: require_sleep.method(zod.z.object({ parentDeviceId: zod.z.number() }), zod.z.array(DeviceInfoSchema)),
|
|
209898
210850
|
/**
|
|
210851
|
+
* `getChildren` for a NAMED SET of parents, in one call.
|
|
210852
|
+
*
|
|
210853
|
+
* The accessory reconcile in `device-cap-proxy.ts` asks this question once
|
|
210854
|
+
* per registered device — every `BaseDevice` inherits a
|
|
210855
|
+
* `getAccessoryChildren()` that returns `[]`, so even a leaf accessory
|
|
210856
|
+
* pays a round-trip to learn it has nothing to prune. Measured on the live
|
|
210857
|
+
* hub 2026-08-27 over a 120-second boot window, fleet of 1 017 devices:
|
|
210858
|
+
* `DeviceRowStore.list < DeviceRowStore.listByParent < getChildren` at
|
|
210859
|
+
* **1 024 calls** returning **919 rows in total** — 1 024 RPCs and 1 024
|
|
210860
|
+
* indexed scans to move less than one row each. `listByParentMany`
|
|
210861
|
+
* collapses the scans; this collapses the RPCs.
|
|
210862
|
+
*
|
|
210863
|
+
* Keyed by parent id as a STRING — a JSON object cannot key by number
|
|
210864
|
+
* (same reason as `getDeviceStatusAggregateBatch`). The per-parent value
|
|
210865
|
+
* is exactly what `getChildren` returns for that parent.
|
|
210866
|
+
*
|
|
210867
|
+
* A parent with no children — or one the fleet does not know — is ABSENT
|
|
210868
|
+
* from the record, never an invented empty row: the same contract as
|
|
210869
|
+
* `DeviceRowStore.getMany`/`listByParentMany`. An EMPTY `parentDeviceIds`
|
|
210870
|
+
* reads nothing at all rather than degrading to "every device".
|
|
210871
|
+
*
|
|
210872
|
+
* `parentDeviceIds` is capped at {@link DEVICE_CHILDREN_BATCH_MAX} — see
|
|
210873
|
+
* that constant for why. A caller with more parents than that sends more
|
|
210874
|
+
* than one call; it never sends one pathological one.
|
|
210875
|
+
*
|
|
210876
|
+
* Version skew: this is a NEW method, not a new field on `getChildren`, so
|
|
210877
|
+
* a hub that predates it answers NOT_FOUND rather than silently stripping
|
|
210878
|
+
* an unknown input key and answering a DIFFERENT question. The kernel-side
|
|
210879
|
+
* loader degrades to per-parent `getChildren` on that error — see
|
|
210880
|
+
* `children-batch-loader.ts`.
|
|
210881
|
+
*/
|
|
210882
|
+
getChildrenBatch: require_sleep.method(zod.z.object({ parentDeviceIds: zod.z.array(zod.z.number()).max(256) }), zod.z.record(zod.z.string(), zod.z.array(DeviceInfoSchema))),
|
|
210883
|
+
/**
|
|
209899
210884
|
* Resolve the devices LINKED to a camera — the single policy authority
|
|
209900
210885
|
* both consumers call (viewer devices panel + pipeline-analytics event
|
|
209901
210886
|
* kinds/ingest). Device-tree children are ALWAYS included; mode 'auto'
|
|
@@ -210947,6 +211932,38 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
210947
211932
|
})
|
|
210948
211933
|
}
|
|
210949
211934
|
};
|
|
211935
|
+
var LogChannelApplyResultSchema = zod.z.object({
|
|
211936
|
+
/** How many declared channels are armed in this process after the call. */
|
|
211937
|
+
armed: zod.z.number().int().min(0),
|
|
211938
|
+
/**
|
|
211939
|
+
* Names the document armed that this process does not declare. Reported
|
|
211940
|
+
* rather than swallowed: a name here is either a typo or an addon that has
|
|
211941
|
+
* not booted, and both deserve a line instead of silence.
|
|
211942
|
+
*/
|
|
211943
|
+
unknown: zod.z.array(zod.z.string()).readonly()
|
|
211944
|
+
});
|
|
211945
|
+
var logChannelsCapability = {
|
|
211946
|
+
name: "log-channels",
|
|
211947
|
+
scope: "system",
|
|
211948
|
+
mode: "collection",
|
|
211949
|
+
internal: true,
|
|
211950
|
+
methods: {
|
|
211951
|
+
/** The channels this addon declares. Inert: no value, no state. */
|
|
211952
|
+
list: require_sleep.method(zod.z.void(), zod.z.array(LogChannelDescriptorSchema).readonly()),
|
|
211953
|
+
/**
|
|
211954
|
+
* Refresh this process's mirror from the document's FULL set of armed
|
|
211955
|
+
* windows.
|
|
211956
|
+
*
|
|
211957
|
+
* Full and not incremental on purpose: the document is the authority, so a
|
|
211958
|
+
* channel it does not name is disarmed here. An incremental apply would
|
|
211959
|
+
* let a disarm get lost in transit and leave a channel running that
|
|
211960
|
+
* nobody can see is running.
|
|
211961
|
+
*/
|
|
211962
|
+
apply: require_sleep.method(zod.z.object({ windows: zod.z.array(LogChannelWindowSchema).readonly() }), LogChannelApplyResultSchema, { kind: "mutation" })
|
|
211963
|
+
},
|
|
211964
|
+
/** In-process only — enumerated through `addons.listCapabilityProviders`. */
|
|
211965
|
+
mount: { kind: "skip" }
|
|
211966
|
+
};
|
|
210950
211967
|
var LogLevelSchema = zod.z.enum([
|
|
210951
211968
|
"debug",
|
|
210952
211969
|
"info",
|
|
@@ -227266,6 +228283,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
227266
228283
|
scope: LoggingScopeKindSchema,
|
|
227267
228284
|
/** The node this layer speaks for; `null` on the cluster layer. */
|
|
227268
228285
|
nodeId: zod.z.string().nullable(),
|
|
228286
|
+
/**
|
|
228287
|
+
* The declared channel this layer speaks for; `null` on every layer but
|
|
228288
|
+
* `component`. Never folded into `nodeId`: a component level is CLUSTER-WIDE
|
|
228289
|
+
* by design — the convention this repo settled on is one orchestrator-wide
|
|
228290
|
+
* setting, never per node (D52) — so a component layer that carried a node
|
|
228291
|
+
* would invite a per-node copy of a value that has no per-node meaning.
|
|
228292
|
+
*/
|
|
228293
|
+
component: zod.z.string().nullable(),
|
|
227269
228294
|
/** Explicitly set here, or `null` when this layer inherits. */
|
|
227270
228295
|
level: LogLevelSchema$1.nullable()
|
|
227271
228296
|
});
|
|
@@ -227291,6 +228316,38 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
227291
228316
|
/** Cadence of the diagnostic's aggregated report line. Clamped by the server. */
|
|
227292
228317
|
reportEveryMs: zod.z.number().int().positive().optional()
|
|
227293
228318
|
});
|
|
228319
|
+
var LogChannelWindowStateSchema = zod.z.object({
|
|
228320
|
+
channel: zod.z.string(),
|
|
228321
|
+
armed: zod.z.boolean(),
|
|
228322
|
+
/** Epoch ms the window closes at. 0 when disarmed. */
|
|
228323
|
+
armedUntilMs: zod.z.number(),
|
|
228324
|
+
/** Ms left before it expires on its own. 0 when disarmed. */
|
|
228325
|
+
remainingMs: zod.z.number(),
|
|
228326
|
+
/**
|
|
228327
|
+
* The cameras it is narrowed to, or `null` for every camera.
|
|
228328
|
+
*
|
|
228329
|
+
* A channel declared `perDevice: false` can only ever report `null` here:
|
|
228330
|
+
* its lines do not carry `tags: { deviceId }`, so narrowing them would
|
|
228331
|
+
* produce a filter that silently matches nothing. The server REFUSES such a
|
|
228332
|
+
* patch rather than quietly widening it — ignoring the request would teach
|
|
228333
|
+
* the operator that per-camera filtering works on that channel when it does
|
|
228334
|
+
* not.
|
|
228335
|
+
*/
|
|
228336
|
+
deviceIds: zod.z.array(zod.z.number().int()).readonly().nullable()
|
|
228337
|
+
});
|
|
228338
|
+
var LogChannelWindowPatchSchema = zod.z.object({
|
|
228339
|
+
channel: zod.z.string().min(1),
|
|
228340
|
+
armMs: zod.z.number().int().min(0),
|
|
228341
|
+
/**
|
|
228342
|
+
* Narrow to these numeric device ids. Absent or `null` = every camera.
|
|
228343
|
+
*
|
|
228344
|
+
* Numeric because the repo's own rule makes it possible: every log line
|
|
228345
|
+
* about a device carries `tags: { deviceId }` with the numeric id. That rule
|
|
228346
|
+
* was paid for with a 22% thumbnail gap and a 3-hour media blackout both
|
|
228347
|
+
* diagnosed by hand, and this is the first thing that collects on it.
|
|
228348
|
+
*/
|
|
228349
|
+
deviceIds: zod.z.array(zod.z.number().int()).readonly().nullable().optional()
|
|
228350
|
+
});
|
|
227294
228351
|
var LoggingSettingsPatchSchema = zod.z.object({
|
|
227295
228352
|
/**
|
|
227296
228353
|
* Absent leaves the level untouched. `null` CLEARS the explicit value at the
|
|
@@ -227301,19 +228358,50 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
227301
228358
|
* Only the diagnostics NAMED here change. An armed window that is not listed
|
|
227302
228359
|
* keeps running — a patch is never a full replacement.
|
|
227303
228360
|
*/
|
|
227304
|
-
diagnostics: zod.z.array(DiagnosticWindowPatchSchema).readonly().optional()
|
|
228361
|
+
diagnostics: zod.z.array(DiagnosticWindowPatchSchema).readonly().optional(),
|
|
228362
|
+
/**
|
|
228363
|
+
* Only the channels NAMED here change. An armed channel that is not listed
|
|
228364
|
+
* keeps running — same rule as `diagnostics`, because a patch that silently
|
|
228365
|
+
* disarmed the channels it did not mention would make the Levels page and
|
|
228366
|
+
* the Diagnostics page fight over the same value.
|
|
228367
|
+
*/
|
|
228368
|
+
channels: zod.z.array(LogChannelWindowPatchSchema).readonly().optional()
|
|
228369
|
+
});
|
|
228370
|
+
var GetLoggingSettingsInputSchema = zod.z.object({
|
|
228371
|
+
scopeNodeId: zod.z.string().optional(),
|
|
228372
|
+
/**
|
|
228373
|
+
* The declared CHANNEL this document is addressed at, when the caller wants
|
|
228374
|
+
* the `component` layer. Absent = the node/cluster hierarchy only.
|
|
228375
|
+
*
|
|
228376
|
+
* Naming it separately rather than overloading `scopeNodeId` keeps the two
|
|
228377
|
+
* axes from collapsing: a component level is cluster-wide, a node level is
|
|
228378
|
+
* not, and one selector for both would make "which of these two did I just
|
|
228379
|
+
* set" unanswerable — the exact ambiguity `explicit` exists to remove.
|
|
228380
|
+
*/
|
|
228381
|
+
scopeComponent: zod.z.string().optional()
|
|
227305
228382
|
});
|
|
227306
|
-
var GetLoggingSettingsInputSchema = zod.z.object({ scopeNodeId: zod.z.string().optional() });
|
|
227307
228383
|
var SetLoggingSettingsInputSchema = zod.z.object({
|
|
227308
228384
|
scopeNodeId: zod.z.string().optional(),
|
|
228385
|
+
scopeComponent: zod.z.string().optional(),
|
|
227309
228386
|
patch: LoggingSettingsPatchSchema
|
|
227310
228387
|
});
|
|
227311
228388
|
var LoggingSettingsStateSchema = zod.z.object({
|
|
227312
228389
|
/** The layer this document was read at. `null` = the cluster layer. */
|
|
227313
228390
|
scopeNodeId: zod.z.string().nullable(),
|
|
228391
|
+
/** The channel this document was read at. `null` = no component layer. */
|
|
228392
|
+
scopeComponent: zod.z.string().nullable(),
|
|
227314
228393
|
effective: LoggingEffectiveSchema,
|
|
227315
228394
|
explicit: LoggingExplicitSchema,
|
|
227316
228395
|
activeWindows: zod.z.array(DiagnosticWindowSchema).readonly(),
|
|
228396
|
+
/**
|
|
228397
|
+
* Every channel the cluster's addons DECLARE, gathered from the
|
|
228398
|
+
* `log-channels` providers. Not stored anywhere: assembled per read, so a
|
|
228399
|
+
* channel added by a redeployed addon appears without anybody editing a
|
|
228400
|
+
* list, and a channel whose addon is gone stops being offered.
|
|
228401
|
+
*/
|
|
228402
|
+
channels: zod.z.array(LogChannelDescriptorSchema).readonly(),
|
|
228403
|
+
/** The channels ARMED right now, each with its deadline. */
|
|
228404
|
+
activeChannels: zod.z.array(LogChannelWindowStateSchema).readonly(),
|
|
227317
228405
|
persisted: zod.z.boolean()
|
|
227318
228406
|
});
|
|
227319
228407
|
var systemCapability = {
|
|
@@ -230945,6 +232033,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
230945
232033
|
llmRuntime: "llm-runtime",
|
|
230946
232034
|
localNetwork: "local-network",
|
|
230947
232035
|
lockControl: "lock-control",
|
|
232036
|
+
logChannels: "log-channels",
|
|
230948
232037
|
logDestination: "log-destination",
|
|
230949
232038
|
loginMethod: "login-method",
|
|
230950
232039
|
mediaPlayer: "media-player",
|
|
@@ -231315,6 +232404,10 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
231315
232404
|
key: "lockControl",
|
|
231316
232405
|
name: "lock-control"
|
|
231317
232406
|
},
|
|
232407
|
+
{
|
|
232408
|
+
key: "logChannels",
|
|
232409
|
+
name: "log-channels"
|
|
232410
|
+
},
|
|
231318
232411
|
{
|
|
231319
232412
|
key: "logDestination",
|
|
231320
232413
|
name: "log-destination"
|
|
@@ -231694,6 +232787,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
231694
232787
|
llmRuntimeCapability,
|
|
231695
232788
|
localNetworkCapability,
|
|
231696
232789
|
lockControlCapability,
|
|
232790
|
+
logChannelsCapability,
|
|
231697
232791
|
logDestinationCapability,
|
|
231698
232792
|
loginMethodCapability,
|
|
231699
232793
|
mediaPlayerCapability,
|
|
@@ -232660,6 +233754,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
232660
233754
|
addonId: null,
|
|
232661
233755
|
access: "view"
|
|
232662
233756
|
},
|
|
233757
|
+
"dataStoreProvider.aggregate": {
|
|
233758
|
+
capName: "data-store-provider",
|
|
233759
|
+
capScope: "system",
|
|
233760
|
+
addonId: null,
|
|
233761
|
+
access: "view"
|
|
233762
|
+
},
|
|
232663
233763
|
"dataStoreProvider.count": {
|
|
232664
233764
|
capName: "data-store-provider",
|
|
232665
233765
|
capScope: "system",
|
|
@@ -233074,6 +234174,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
233074
234174
|
addonId: null,
|
|
233075
234175
|
access: "view"
|
|
233076
234176
|
},
|
|
234177
|
+
"deviceManager.getChildrenBatch": {
|
|
234178
|
+
capName: "device-manager",
|
|
234179
|
+
capScope: "system",
|
|
234180
|
+
addonId: null,
|
|
234181
|
+
access: "view"
|
|
234182
|
+
},
|
|
233077
234183
|
"deviceManager.getConfigSchema": {
|
|
233078
234184
|
capName: "device-manager",
|
|
233079
234185
|
capScope: "system",
|
|
@@ -234124,6 +235230,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
234124
235230
|
addonId: null,
|
|
234125
235231
|
access: "create"
|
|
234126
235232
|
},
|
|
235233
|
+
"logChannels.apply": {
|
|
235234
|
+
capName: "log-channels",
|
|
235235
|
+
capScope: "system",
|
|
235236
|
+
addonId: null,
|
|
235237
|
+
access: "create"
|
|
235238
|
+
},
|
|
235239
|
+
"logChannels.list": {
|
|
235240
|
+
capName: "log-channels",
|
|
235241
|
+
capScope: "system",
|
|
235242
|
+
addonId: null,
|
|
235243
|
+
access: "view"
|
|
235244
|
+
},
|
|
234127
235245
|
"logDestination.query": {
|
|
234128
235246
|
capName: "log-destination",
|
|
234129
235247
|
capScope: "system",
|
|
@@ -236278,6 +237396,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
236278
237396
|
addonId: null,
|
|
236279
237397
|
access: "create"
|
|
236280
237398
|
},
|
|
237399
|
+
"settingsStore.aggregate": {
|
|
237400
|
+
capName: "settings-store",
|
|
237401
|
+
capScope: "system",
|
|
237402
|
+
addonId: null,
|
|
237403
|
+
access: "view"
|
|
237404
|
+
},
|
|
236281
237405
|
"settingsStore.count": {
|
|
236282
237406
|
capName: "settings-store",
|
|
236283
237407
|
capScope: "system",
|
|
@@ -237626,6 +238750,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
237626
238750
|
"llm-runtime",
|
|
237627
238751
|
"local-network",
|
|
237628
238752
|
"lock-control",
|
|
238753
|
+
"log-channels",
|
|
237629
238754
|
"log-destination",
|
|
237630
238755
|
"login-method",
|
|
237631
238756
|
"media-player",
|
|
@@ -237782,6 +238907,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
237782
238907
|
"llm",
|
|
237783
238908
|
"llm-runtime",
|
|
237784
238909
|
"local-network",
|
|
238910
|
+
"log-channels",
|
|
237785
238911
|
"log-destination",
|
|
237786
238912
|
"login-method",
|
|
237787
238913
|
"mesh-network",
|
|
@@ -238109,6 +239235,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
238109
239235
|
form: "single",
|
|
238110
239236
|
optional: false
|
|
238111
239237
|
}],
|
|
239238
|
+
"deviceManager.getChildrenBatch": [{
|
|
239239
|
+
name: "parentDeviceIds",
|
|
239240
|
+
form: "array",
|
|
239241
|
+
optional: false
|
|
239242
|
+
}],
|
|
238112
239243
|
"deviceManager.getConfigSchema": [{
|
|
238113
239244
|
name: "deviceId",
|
|
238114
239245
|
form: "single",
|
|
@@ -239583,6 +240714,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
239583
240714
|
"deviceManager.getBindings",
|
|
239584
240715
|
"deviceManager.getBindingsBatch",
|
|
239585
240716
|
"deviceManager.getChildren",
|
|
240717
|
+
"deviceManager.getChildrenBatch",
|
|
239586
240718
|
"deviceManager.getConfigSchema",
|
|
239587
240719
|
"deviceManager.getDevice",
|
|
239588
240720
|
"deviceManager.getDeviceAggregate",
|
|
@@ -240275,6 +241407,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
240275
241407
|
listPersistedByAddon: (input) => dispatch("deviceManager", "listPersistedByAddon", "query", input),
|
|
240276
241408
|
listAll: (input) => dispatch("deviceManager", "listAll", "query", input),
|
|
240277
241409
|
getChildren: (input) => dispatch("deviceManager", "getChildren", "query", input),
|
|
241410
|
+
getChildrenBatch: (input) => dispatch("deviceManager", "getChildrenBatch", "query", input),
|
|
240278
241411
|
getLinkedDevicesBatch: (input) => dispatch("deviceManager", "getLinkedDevicesBatch", "query", input),
|
|
240279
241412
|
removeByIntegration: (input) => dispatch("deviceManager", "removeByIntegration", "mutation", input),
|
|
240280
241413
|
getBindingsBatch: (input) => dispatch("deviceManager", "getBindingsBatch", "query", input),
|
|
@@ -240589,6 +241722,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
240589
241722
|
deleteWhere: (input) => dispatch("settingsStore", "deleteWhere", "mutation", input),
|
|
240590
241723
|
updateWhere: (input) => dispatch("settingsStore", "updateWhere", "mutation", input),
|
|
240591
241724
|
count: (input) => dispatch("settingsStore", "count", "query", input),
|
|
241725
|
+
aggregate: (input) => dispatch("settingsStore", "aggregate", "query", input),
|
|
240592
241726
|
histogram: (input) => dispatch("settingsStore", "histogram", "query", input),
|
|
240593
241727
|
isEmpty: (input) => dispatch("settingsStore", "isEmpty", "query", input),
|
|
240594
241728
|
declareCollection: (input) => dispatch("settingsStore", "declareCollection", "mutation", input)
|
|
@@ -243598,6 +244732,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
243598
244732
|
exports.DETECTION_PIPELINE_CAP_NAME = DETECTION_PIPELINE_CAP_NAME;
|
|
243599
244733
|
exports.DEVICE_BACKEND_TO_FORMAT = DEVICE_BACKEND_TO_FORMAT;
|
|
243600
244734
|
exports.DEVICE_CAP_NAMES = DEVICE_CAP_NAMES;
|
|
244735
|
+
exports.DEVICE_CHILDREN_BATCH_MAX = DEVICE_CHILDREN_BATCH_MAX;
|
|
243601
244736
|
exports.DEVICE_PROFILES = DEVICE_PROFILES;
|
|
243602
244737
|
exports.DEVICE_SCOPED_CAPS = require_sleep.DEVICE_SCOPED_CAPS;
|
|
243603
244738
|
exports.DEVICE_SETTINGS_CONTRIBUTION_METHODS = require_sleep.DEVICE_SETTINGS_CONTRIBUTION_METHODS;
|
|
@@ -243748,6 +244883,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
243748
244883
|
exports.IntercomStatusSchema = IntercomStatusSchema;
|
|
243749
244884
|
exports.KNOWN_CAP_NAMES = KNOWN_CAP_NAMES;
|
|
243750
244885
|
exports.KeyEventSchema = KeyEventSchema;
|
|
244886
|
+
exports.LOG_CHANNEL_TICK_MS = LOG_CHANNEL_TICK_MS;
|
|
243751
244887
|
exports.LOG_LEVEL_RANK = LOG_LEVEL_RANK;
|
|
243752
244888
|
exports.LabelAttributionSchema = LabelAttributionSchema;
|
|
243753
244889
|
exports.LabelDefinitionSchema = LabelDefinitionSchema;
|
|
@@ -243783,6 +244919,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
243783
244919
|
exports.LocationStatSchema = LocationStatSchema;
|
|
243784
244920
|
exports.LockControlStatusSchema = LockControlStatusSchema;
|
|
243785
244921
|
exports.LockStateSchema = LockStateSchema;
|
|
244922
|
+
exports.LogChannelApplyResultSchema = LogChannelApplyResultSchema;
|
|
244923
|
+
exports.LogChannelDescriptorSchema = LogChannelDescriptorSchema;
|
|
244924
|
+
exports.LogChannelGate = LogChannelGate;
|
|
244925
|
+
exports.LogChannelLevelSchema = LogChannelLevelSchema;
|
|
244926
|
+
exports.LogChannelRegistry = LogChannelRegistry;
|
|
244927
|
+
exports.LogChannelWindowPatchSchema = LogChannelWindowPatchSchema;
|
|
244928
|
+
exports.LogChannelWindowSchema = LogChannelWindowSchema;
|
|
244929
|
+
exports.LogChannelWindowStateSchema = LogChannelWindowStateSchema;
|
|
243786
244930
|
exports.LogEntrySchema = LogEntrySchema;
|
|
243787
244931
|
exports.LogLevelSchema = LogLevelSchema;
|
|
243788
244932
|
exports.LogStreamEntrySchema = LogStreamEntrySchema;
|
|
@@ -244333,6 +245477,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
244333
245477
|
exports.ZoneRulesArraySchema = ZoneRulesArraySchema;
|
|
244334
245478
|
exports.ZoneSchema = ZoneSchema;
|
|
244335
245479
|
exports.ZoneScopeBreakdownSchema = ZoneScopeBreakdownSchema;
|
|
245480
|
+
exports.__resetLogChannelRegistryForTests = __resetLogChannelRegistryForTests;
|
|
244336
245481
|
exports.accessoriesCapability = accessoriesCapability;
|
|
244337
245482
|
exports.accessoryStableId = accessoryStableId;
|
|
244338
245483
|
exports.addonPagesCapability = addonPagesCapability;
|
|
@@ -244429,6 +245574,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
244429
245574
|
exports.createExpressionScope = createExpressionScope;
|
|
244430
245575
|
exports.createHwAccelCache = createHwAccelCache;
|
|
244431
245576
|
exports.createLazyTrpcSource = require_sleep.createLazyTrpcSource;
|
|
245577
|
+
exports.createLogChannelsProvider = createLogChannelsProvider;
|
|
244432
245578
|
exports.createMirrorSource = require_sleep.createMirrorSource;
|
|
244433
245579
|
exports.createRuntimeStateBridge = createRuntimeStateBridge;
|
|
244434
245580
|
exports.createSliceHandle = require_sleep.createSliceHandle;
|
|
@@ -244438,6 +245584,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
244438
245584
|
exports.dataStoreProviderCapability = dataStoreProviderCapability;
|
|
244439
245585
|
exports.dayNightCapability = dayNightCapability;
|
|
244440
245586
|
exports.declarationOwnerNodeId = declarationOwnerNodeId;
|
|
245587
|
+
exports.declareLogChannel = declareLogChannel;
|
|
244441
245588
|
exports.decodeVectorBase64 = decodeVectorBase64;
|
|
244442
245589
|
exports.decoderCapability = decoderCapability;
|
|
244443
245590
|
exports.defaultDeliveryForSection = defaultDeliveryForSection;
|
|
@@ -244500,6 +245647,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
244500
245647
|
exports.getAudioMacroClassIds = getAudioMacroClassIds;
|
|
244501
245648
|
exports.getByPath = getByPath;
|
|
244502
245649
|
exports.getCapsByProviderKind = getCapsByProviderKind;
|
|
245650
|
+
exports.getLogChannelRegistry = getLogChannelRegistry;
|
|
244503
245651
|
exports.getTaxonomyEntry = getTaxonomyEntry;
|
|
244504
245652
|
exports.hasMotionTrigger = hasMotionTrigger;
|
|
244505
245653
|
exports.hfModelUrl = hfModelUrl;
|
|
@@ -244553,6 +245701,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
244553
245701
|
exports.locationSimilarity = locationSimilarity;
|
|
244554
245702
|
exports.lockControlCapability = lockControlCapability;
|
|
244555
245703
|
exports.logBannerArgs = require_canonical_hash.logBannerArgs;
|
|
245704
|
+
exports.logChannelsCapability = logChannelsCapability;
|
|
244556
245705
|
exports.logDestinationCapability = logDestinationCapability;
|
|
244557
245706
|
exports.logLevelAtMost = logLevelAtMost;
|
|
244558
245707
|
exports.loginMethodCapability = loginMethodCapability;
|
|
@@ -401844,17 +402993,55 @@ var require_logging_settings = __commonJS({
|
|
|
401844
402993
|
"../../server/backend/dist/api/core/logging-settings.js"(exports) {
|
|
401845
402994
|
"use strict";
|
|
401846
402995
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
401847
|
-
exports.LoggingSettingsService = exports.HUB_NODE_ID = exports.LOGGING_SETTINGS_KEY = void 0;
|
|
402996
|
+
exports.LoggingSettingsService = exports.MAX_CHANNEL_WINDOW_MS = exports.EMPTY_LOG_CHANNEL_PLANE = exports.HUB_NODE_ID = exports.LOGGING_SETTINGS_KEY = void 0;
|
|
402997
|
+
exports.buildLogChannelPlane = buildLogChannelPlane;
|
|
401848
402998
|
exports.toLoggingSettingsRecord = toLoggingSettingsRecord;
|
|
401849
402999
|
exports.resolveLevel = resolveLevel;
|
|
401850
403000
|
exports.describeLayers = describeLayers;
|
|
401851
403001
|
exports.mergeLevel = mergeLevel;
|
|
403002
|
+
exports.validateChannelPatch = validateChannelPatch;
|
|
403003
|
+
exports.mergeChannelWindows = mergeChannelWindows;
|
|
403004
|
+
exports.liveChannelWindows = liveChannelWindows;
|
|
403005
|
+
exports.describeChannelWindows = describeChannelWindows;
|
|
401852
403006
|
var types_1 = require_dist4();
|
|
401853
403007
|
var system_1 = require_dist3();
|
|
401854
403008
|
exports.LOGGING_SETTINGS_KEY = "logging-settings";
|
|
401855
403009
|
var SYSTEM_SETTINGS_COLLECTION = "system-settings";
|
|
401856
403010
|
exports.HUB_NODE_ID = "hub";
|
|
401857
403011
|
var LOG_LEVELS = ["debug", "info", "warn", "error"];
|
|
403012
|
+
exports.EMPTY_LOG_CHANNEL_PLANE = {
|
|
403013
|
+
declarations: async () => [],
|
|
403014
|
+
apply: async () => {
|
|
403015
|
+
}
|
|
403016
|
+
};
|
|
403017
|
+
function buildLogChannelPlane(source, onProviderError) {
|
|
403018
|
+
return {
|
|
403019
|
+
declarations: async () => {
|
|
403020
|
+
const seen = /* @__PURE__ */ new Map();
|
|
403021
|
+
for (const [addonId, provider] of source.entries()) {
|
|
403022
|
+
try {
|
|
403023
|
+
for (const descriptor of await provider.list()) {
|
|
403024
|
+
if (!seen.has(descriptor.name))
|
|
403025
|
+
seen.set(descriptor.name, descriptor);
|
|
403026
|
+
}
|
|
403027
|
+
} catch (err) {
|
|
403028
|
+
onProviderError?.(addonId, "list", (0, types_1.errMsg)(err));
|
|
403029
|
+
}
|
|
403030
|
+
}
|
|
403031
|
+
return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
403032
|
+
},
|
|
403033
|
+
apply: async (windows) => {
|
|
403034
|
+
for (const [addonId, provider] of source.entries()) {
|
|
403035
|
+
try {
|
|
403036
|
+
await provider.apply({ windows });
|
|
403037
|
+
} catch (err) {
|
|
403038
|
+
onProviderError?.(addonId, "apply", (0, types_1.errMsg)(err));
|
|
403039
|
+
}
|
|
403040
|
+
}
|
|
403041
|
+
}
|
|
403042
|
+
};
|
|
403043
|
+
}
|
|
403044
|
+
exports.MAX_CHANNEL_WINDOW_MS = 30 * 6e4;
|
|
401858
403045
|
var LoggingSettingsService = class {
|
|
401859
403046
|
store;
|
|
401860
403047
|
gate;
|
|
@@ -401862,6 +403049,7 @@ var require_logging_settings = __commonJS({
|
|
|
401862
403049
|
localNodeId;
|
|
401863
403050
|
logger;
|
|
401864
403051
|
now;
|
|
403052
|
+
channels;
|
|
401865
403053
|
constructor(deps) {
|
|
401866
403054
|
this.store = deps.store;
|
|
401867
403055
|
this.gate = deps.gate;
|
|
@@ -401869,6 +403057,7 @@ var require_logging_settings = __commonJS({
|
|
|
401869
403057
|
this.localNodeId = deps.localNodeId ?? exports.HUB_NODE_ID;
|
|
401870
403058
|
this.logger = deps.logger;
|
|
401871
403059
|
this.now = deps.now ?? Date.now;
|
|
403060
|
+
this.channels = deps.channels ?? exports.EMPTY_LOG_CHANNEL_PLANE;
|
|
401872
403061
|
}
|
|
401873
403062
|
/**
|
|
401874
403063
|
* The document as it stands, resolved for `nodeId` (absent = the cluster).
|
|
@@ -401877,9 +403066,9 @@ var require_logging_settings = __commonJS({
|
|
|
401877
403066
|
* and, crucially, does not touch the mirror. What the operator sees is
|
|
401878
403067
|
* honestly labelled as un-persisted; what the hub does is unchanged.
|
|
401879
403068
|
*/
|
|
401880
|
-
async get(scopeNodeId) {
|
|
403069
|
+
async get(scopeNodeId, scopeComponent) {
|
|
401881
403070
|
const record = await this.read();
|
|
401882
|
-
return this.describe(record, scopeNodeId ?? null);
|
|
403071
|
+
return this.describe(record, scopeNodeId ?? null, scopeComponent ?? null);
|
|
401883
403072
|
}
|
|
401884
403073
|
/**
|
|
401885
403074
|
* Apply a patch. Fields absent from it are untouched.
|
|
@@ -401889,28 +403078,47 @@ var require_logging_settings = __commonJS({
|
|
|
401889
403078
|
* must still give the operator a live measurement, and must say so rather
|
|
401890
403079
|
* than reporting a durable arm that is not durable.
|
|
401891
403080
|
*/
|
|
401892
|
-
async set(patch, scopeNodeId) {
|
|
403081
|
+
async set(patch, scopeNodeId, scopeComponent) {
|
|
401893
403082
|
await this.applyDiagnostics(patch);
|
|
401894
403083
|
const scope = scopeNodeId ?? null;
|
|
403084
|
+
const component = scopeComponent ?? null;
|
|
401895
403085
|
const stored = await this.read();
|
|
401896
403086
|
const levelChanged = patch.level !== void 0;
|
|
401897
|
-
|
|
403087
|
+
const channelsChanged = patch.channels !== void 0;
|
|
403088
|
+
let persisted = true;
|
|
403089
|
+
let next = stored ?? {};
|
|
401898
403090
|
if (levelChanged) {
|
|
401899
|
-
|
|
401900
|
-
|
|
403091
|
+
next = mergeLevel(next, scope, component, patch.level ?? null, this.now());
|
|
403092
|
+
}
|
|
403093
|
+
if (channelsChanged) {
|
|
403094
|
+
const declared = await this.channels.declarations();
|
|
403095
|
+
const rejected = validateChannelPatch(patch.channels ?? [], declared);
|
|
403096
|
+
if (rejected.length > 0) {
|
|
403097
|
+
this.logger?.warn("log channel patch refused - nothing was armed or disarmed", {
|
|
403098
|
+
meta: { reasons: rejected }
|
|
403099
|
+
});
|
|
403100
|
+
throw new Error(`log channel patch refused: ${rejected.join("; ")}`);
|
|
403101
|
+
}
|
|
403102
|
+
next = mergeChannelWindows(next, patch.channels ?? [], this.now());
|
|
403103
|
+
}
|
|
403104
|
+
if (levelChanged || channelsChanged) {
|
|
403105
|
+
persisted = await this.write(next);
|
|
401901
403106
|
this.refreshMirror(next);
|
|
403107
|
+
await this.pushChannels(next);
|
|
401902
403108
|
this.logger?.info("logging settings written", {
|
|
401903
403109
|
meta: {
|
|
401904
|
-
scope: scope === null ? "cluster" : "node",
|
|
403110
|
+
scope: component !== null ? "component" : scope === null ? "cluster" : "node",
|
|
401905
403111
|
nodeId: scope,
|
|
401906
|
-
|
|
401907
|
-
|
|
403112
|
+
component,
|
|
403113
|
+
level: levelChanged ? patch.level ?? null : void 0,
|
|
403114
|
+
channels: (patch.channels ?? []).map((c) => `${c.channel}:${c.armMs}`),
|
|
403115
|
+
persisted
|
|
401908
403116
|
}
|
|
401909
403117
|
});
|
|
401910
|
-
const state = await this.describe(next, scope);
|
|
401911
|
-
return { ...state, persisted: state.persisted &&
|
|
403118
|
+
const state = await this.describe(next, scope, component);
|
|
403119
|
+
return { ...state, persisted: state.persisted && persisted };
|
|
401912
403120
|
}
|
|
401913
|
-
return this.describe(stored, scope);
|
|
403121
|
+
return this.describe(stored, scope, component);
|
|
401914
403122
|
}
|
|
401915
403123
|
/**
|
|
401916
403124
|
* Re-establish the mirror and re-arm any diagnostic window that outlived the
|
|
@@ -401923,8 +403131,10 @@ var require_logging_settings = __commonJS({
|
|
|
401923
403131
|
*/
|
|
401924
403132
|
async restore() {
|
|
401925
403133
|
const record = await this.read();
|
|
401926
|
-
if (record !== null)
|
|
403134
|
+
if (record !== null) {
|
|
401927
403135
|
this.refreshMirror(record);
|
|
403136
|
+
await this.pushChannels(record);
|
|
403137
|
+
}
|
|
401928
403138
|
await this.requestCensus.restore();
|
|
401929
403139
|
}
|
|
401930
403140
|
// ── internals ─────────────────────────────────────────────────────
|
|
@@ -401942,16 +403152,55 @@ var require_logging_settings = __commonJS({
|
|
|
401942
403152
|
});
|
|
401943
403153
|
}
|
|
401944
403154
|
}
|
|
401945
|
-
|
|
401946
|
-
|
|
403155
|
+
/**
|
|
403156
|
+
* Push the live windows to every declared gate.
|
|
403157
|
+
*
|
|
403158
|
+
* A failure here is logged and swallowed: the document is written and the
|
|
403159
|
+
* operator is told what it says, and a runner that could not be reached
|
|
403160
|
+
* simply keeps the mirror it has. Throwing would turn one unreachable addon
|
|
403161
|
+
* into a failed write for the whole cluster.
|
|
403162
|
+
*/
|
|
403163
|
+
async pushChannels(record) {
|
|
403164
|
+
try {
|
|
403165
|
+
await this.channels.apply(liveChannelWindows(record, this.now()));
|
|
403166
|
+
} catch (err) {
|
|
403167
|
+
this.logger?.warn("log channel windows written but NOT pushed to every gate", {
|
|
403168
|
+
meta: { error: (0, types_1.errMsg)(err) }
|
|
403169
|
+
});
|
|
403170
|
+
}
|
|
403171
|
+
}
|
|
403172
|
+
async describe(record, scopeNodeId, scopeComponent) {
|
|
403173
|
+
const resolved = resolveLevel(record ?? {}, scopeNodeId, scopeComponent);
|
|
401947
403174
|
return {
|
|
401948
403175
|
scopeNodeId,
|
|
403176
|
+
scopeComponent,
|
|
401949
403177
|
effective: { level: resolved.level, levelSource: resolved.source },
|
|
401950
|
-
explicit: { layers: describeLayers(record ?? {}, scopeNodeId) },
|
|
403178
|
+
explicit: { layers: describeLayers(record ?? {}, scopeNodeId, scopeComponent) },
|
|
401951
403179
|
activeWindows: await this.describeWindows(),
|
|
403180
|
+
channels: await this.declaredChannels(),
|
|
403181
|
+
activeChannels: describeChannelWindows(record ?? {}, this.now()),
|
|
401952
403182
|
persisted: record !== null
|
|
401953
403183
|
};
|
|
401954
403184
|
}
|
|
403185
|
+
/**
|
|
403186
|
+
* The declarations, assembled per read.
|
|
403187
|
+
*
|
|
403188
|
+
* Not cached and not stored: a channel added by a redeployed addon has to
|
|
403189
|
+
* appear without anybody editing a list, and a channel whose addon is gone
|
|
403190
|
+
* has to stop being offered. A read that throws yields the EMPTY list and
|
|
403191
|
+
* says so — "unknown" is the truth about a set of addons nobody reached, and
|
|
403192
|
+
* it changes no armed window (D49/D224).
|
|
403193
|
+
*/
|
|
403194
|
+
async declaredChannels() {
|
|
403195
|
+
try {
|
|
403196
|
+
return await this.channels.declarations();
|
|
403197
|
+
} catch (err) {
|
|
403198
|
+
this.logger?.warn("log channel declarations unreadable - reporting none", {
|
|
403199
|
+
meta: { error: (0, types_1.errMsg)(err) }
|
|
403200
|
+
});
|
|
403201
|
+
return [];
|
|
403202
|
+
}
|
|
403203
|
+
}
|
|
401955
403204
|
/**
|
|
401956
403205
|
* The armed windows, with their deadline.
|
|
401957
403206
|
*
|
|
@@ -402015,14 +403264,21 @@ var require_logging_settings = __commonJS({
|
|
|
402015
403264
|
return {};
|
|
402016
403265
|
const clusterLevel = asLogLevel(Reflect.get(value, "clusterLevel"));
|
|
402017
403266
|
const nodeLevels = asNodeLevels(Reflect.get(value, "nodeLevels"));
|
|
403267
|
+
const componentLevels = asNodeLevels(Reflect.get(value, "componentLevels"));
|
|
403268
|
+
const channelWindows = asChannelWindows(Reflect.get(value, "channelWindows"));
|
|
402018
403269
|
const updatedAt = Reflect.get(value, "updatedAt");
|
|
402019
403270
|
return {
|
|
402020
403271
|
...clusterLevel !== null ? { clusterLevel } : {},
|
|
402021
403272
|
...nodeLevels !== null ? { nodeLevels } : {},
|
|
403273
|
+
...componentLevels !== null ? { componentLevels } : {},
|
|
403274
|
+
...channelWindows !== null ? { channelWindows } : {},
|
|
402022
403275
|
...typeof updatedAt === "number" && Number.isFinite(updatedAt) ? { updatedAt } : {}
|
|
402023
403276
|
};
|
|
402024
403277
|
}
|
|
402025
|
-
function resolveLevel(record, nodeId) {
|
|
403278
|
+
function resolveLevel(record, nodeId, component = null) {
|
|
403279
|
+
const componentLevel = component === null ? void 0 : record.componentLevels?.[component];
|
|
403280
|
+
if (componentLevel !== void 0)
|
|
403281
|
+
return { level: componentLevel, source: "component" };
|
|
402026
403282
|
const nodeLevel = nodeId === null ? void 0 : record.nodeLevels?.[nodeId];
|
|
402027
403283
|
if (nodeLevel !== void 0)
|
|
402028
403284
|
return { level: nodeLevel, source: "node" };
|
|
@@ -402030,17 +403286,37 @@ var require_logging_settings = __commonJS({
|
|
|
402030
403286
|
return { level: record.clusterLevel, source: "cluster" };
|
|
402031
403287
|
return { level: system_1.DEFAULT_LOG_LEVEL, source: "default" };
|
|
402032
403288
|
}
|
|
402033
|
-
function describeLayers(record, nodeId) {
|
|
402034
|
-
const
|
|
402035
|
-
scope: "cluster",
|
|
402036
|
-
|
|
402037
|
-
|
|
402038
|
-
|
|
402039
|
-
|
|
402040
|
-
|
|
402041
|
-
|
|
403289
|
+
function describeLayers(record, nodeId, component = null) {
|
|
403290
|
+
const layers = [
|
|
403291
|
+
{ scope: "cluster", nodeId: null, component: null, level: record.clusterLevel ?? null }
|
|
403292
|
+
];
|
|
403293
|
+
if (nodeId !== null) {
|
|
403294
|
+
layers.push({
|
|
403295
|
+
scope: "node",
|
|
403296
|
+
nodeId,
|
|
403297
|
+
component: null,
|
|
403298
|
+
level: record.nodeLevels?.[nodeId] ?? null
|
|
403299
|
+
});
|
|
403300
|
+
}
|
|
403301
|
+
if (component !== null) {
|
|
403302
|
+
layers.push({
|
|
403303
|
+
scope: "component",
|
|
403304
|
+
nodeId: null,
|
|
403305
|
+
component,
|
|
403306
|
+
level: record.componentLevels?.[component] ?? null
|
|
403307
|
+
});
|
|
403308
|
+
}
|
|
403309
|
+
return layers;
|
|
402042
403310
|
}
|
|
402043
|
-
function mergeLevel(record, nodeId, next, updatedAt) {
|
|
403311
|
+
function mergeLevel(record, nodeId, component, next, updatedAt) {
|
|
403312
|
+
if (component !== null) {
|
|
403313
|
+
const { [component]: _clearedComponent, ...otherComponents } = record.componentLevels ?? {};
|
|
403314
|
+
const componentLevels = {
|
|
403315
|
+
...otherComponents,
|
|
403316
|
+
...next !== null ? { [component]: next } : {}
|
|
403317
|
+
};
|
|
403318
|
+
return { ...record, componentLevels, updatedAt };
|
|
403319
|
+
}
|
|
402044
403320
|
if (nodeId === null) {
|
|
402045
403321
|
const { clusterLevel: _dropped, ...rest } = record;
|
|
402046
403322
|
return { ...rest, ...next !== null ? { clusterLevel: next } : {}, updatedAt };
|
|
@@ -402056,6 +403332,81 @@ var require_logging_settings = __commonJS({
|
|
|
402056
403332
|
updatedAt
|
|
402057
403333
|
};
|
|
402058
403334
|
}
|
|
403335
|
+
function validateChannelPatch(patches, declared) {
|
|
403336
|
+
const byName = new Map(declared.map((d) => [d.name, d]));
|
|
403337
|
+
const reasons = [];
|
|
403338
|
+
for (const patch of patches) {
|
|
403339
|
+
const descriptor = byName.get(patch.channel);
|
|
403340
|
+
if (descriptor === void 0) {
|
|
403341
|
+
const known = [...byName.keys()].sort().slice(0, 8).join(", ");
|
|
403342
|
+
reasons.push(`"${patch.channel}" is not declared by any addon on this cluster` + (known.length > 0 ? ` (declared: ${known})` : ""));
|
|
403343
|
+
continue;
|
|
403344
|
+
}
|
|
403345
|
+
const ids = patch.deviceIds;
|
|
403346
|
+
if (ids !== void 0 && ids !== null && ids.length > 0 && !descriptor.perDevice) {
|
|
403347
|
+
reasons.push(`"${patch.channel}" is declared perDevice: false \u2014 its lines carry no tags.deviceId, so narrowing it to a camera would match nothing`);
|
|
403348
|
+
}
|
|
403349
|
+
}
|
|
403350
|
+
return reasons;
|
|
403351
|
+
}
|
|
403352
|
+
function mergeChannelWindows(record, patches, nowMs) {
|
|
403353
|
+
const windows = { ...record.channelWindows };
|
|
403354
|
+
for (const patch of patches) {
|
|
403355
|
+
if (patch.armMs <= 0) {
|
|
403356
|
+
delete windows[patch.channel];
|
|
403357
|
+
continue;
|
|
403358
|
+
}
|
|
403359
|
+
const ids = patch.deviceIds;
|
|
403360
|
+
windows[patch.channel] = {
|
|
403361
|
+
armedUntilMs: nowMs + Math.min(patch.armMs, exports.MAX_CHANNEL_WINDOW_MS),
|
|
403362
|
+
deviceIds: ids === void 0 || ids === null || ids.length === 0 ? null : [...ids]
|
|
403363
|
+
};
|
|
403364
|
+
}
|
|
403365
|
+
return { ...record, channelWindows: windows, updatedAt: nowMs };
|
|
403366
|
+
}
|
|
403367
|
+
function liveChannelWindows(record, nowMs) {
|
|
403368
|
+
const out = [];
|
|
403369
|
+
for (const [channel, window2] of Object.entries(record.channelWindows ?? {})) {
|
|
403370
|
+
if (window2.armedUntilMs <= nowMs)
|
|
403371
|
+
continue;
|
|
403372
|
+
out.push({ channel, armedUntilMs: window2.armedUntilMs, deviceIds: window2.deviceIds });
|
|
403373
|
+
}
|
|
403374
|
+
return out.sort((a, b) => a.channel.localeCompare(b.channel));
|
|
403375
|
+
}
|
|
403376
|
+
function describeChannelWindows(record, nowMs) {
|
|
403377
|
+
const out = [];
|
|
403378
|
+
for (const [channel, window2] of Object.entries(record.channelWindows ?? {})) {
|
|
403379
|
+
const armed = window2.armedUntilMs > nowMs;
|
|
403380
|
+
out.push({
|
|
403381
|
+
channel,
|
|
403382
|
+
armed,
|
|
403383
|
+
armedUntilMs: armed ? window2.armedUntilMs : 0,
|
|
403384
|
+
remainingMs: armed ? window2.armedUntilMs - nowMs : 0,
|
|
403385
|
+
deviceIds: armed ? window2.deviceIds : null
|
|
403386
|
+
});
|
|
403387
|
+
}
|
|
403388
|
+
return out.sort((a, b) => a.channel.localeCompare(b.channel));
|
|
403389
|
+
}
|
|
403390
|
+
function asChannelWindows(value) {
|
|
403391
|
+
if (typeof value !== "object" || value === null)
|
|
403392
|
+
return null;
|
|
403393
|
+
const out = {};
|
|
403394
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
403395
|
+
if (typeof raw !== "object" || raw === null)
|
|
403396
|
+
continue;
|
|
403397
|
+
const armedUntilMs = Reflect.get(raw, "armedUntilMs");
|
|
403398
|
+
if (typeof armedUntilMs !== "number" || !Number.isFinite(armedUntilMs))
|
|
403399
|
+
continue;
|
|
403400
|
+
out[key] = { armedUntilMs, deviceIds: asDeviceIds(Reflect.get(raw, "deviceIds")) };
|
|
403401
|
+
}
|
|
403402
|
+
return out;
|
|
403403
|
+
}
|
|
403404
|
+
function asDeviceIds(value) {
|
|
403405
|
+
if (!Array.isArray(value))
|
|
403406
|
+
return null;
|
|
403407
|
+
const ids = value.filter((id) => typeof id === "number" && Number.isInteger(id));
|
|
403408
|
+
return ids.length > 0 ? ids : null;
|
|
403409
|
+
}
|
|
402059
403410
|
function asLogLevel(value) {
|
|
402060
403411
|
return typeof value === "string" && isLogLevel(value) ? value : null;
|
|
402061
403412
|
}
|
|
@@ -402692,10 +404043,14 @@ var require_cap_providers = __commonJS({
|
|
|
402692
404043
|
store,
|
|
402693
404044
|
...logger !== void 0 ? { logger } : {}
|
|
402694
404045
|
});
|
|
404046
|
+
const channels = (0, logging_settings_js_1.buildLogChannelPlane)({ entries: () => registry?.getCollectionEntries("log-channels") ?? [] }, (addonId, phase, error) => {
|
|
404047
|
+
logger?.warn("log-channels provider unreachable", { meta: { addonId, phase, error } });
|
|
404048
|
+
});
|
|
402695
404049
|
const loggingSettings = new logging_settings_js_1.LoggingSettingsService({
|
|
402696
404050
|
store,
|
|
402697
404051
|
gate: (0, system_1.getLoggingGate)(),
|
|
402698
404052
|
requestCensus,
|
|
404053
|
+
channels,
|
|
402699
404054
|
...logger !== void 0 ? { logger } : {}
|
|
402700
404055
|
});
|
|
402701
404056
|
return {
|
|
@@ -402729,8 +404084,8 @@ var require_cap_providers = __commonJS({
|
|
|
402729
404084
|
setSiteLocation: async (input) => siteLocation.set(input),
|
|
402730
404085
|
detectSiteLocation: async () => siteLocation.detect(),
|
|
402731
404086
|
getRequestCensus: async () => requestCensus.status(),
|
|
402732
|
-
getLoggingSettings: async (input) => loggingSettings.get(input.scopeNodeId),
|
|
402733
|
-
setLoggingSettings: async (input) => loggingSettings.set(input.patch, input.scopeNodeId)
|
|
404087
|
+
getLoggingSettings: async (input) => loggingSettings.get(input.scopeNodeId, input.scopeComponent),
|
|
404088
|
+
setLoggingSettings: async (input) => loggingSettings.set(input.patch, input.scopeNodeId, input.scopeComponent)
|
|
402734
404089
|
};
|
|
402735
404090
|
}
|
|
402736
404091
|
function buildSiteLocationService(registry, logger) {
|
|
@@ -405277,6 +406632,7 @@ var require_device_config_secret_redaction = __commonJS({
|
|
|
405277
406632
|
exports.NON_ADMIN_CONFIG_REDACTED_METHODS = exports.REDACTED_SECRET = void 0;
|
|
405278
406633
|
exports.isSecretConfigKey = isSecretConfigKey;
|
|
405279
406634
|
exports.redactDeviceInfoSecrets = redactDeviceInfoSecrets;
|
|
406635
|
+
exports.redactDeviceInfoRecordSecrets = redactDeviceInfoRecordSecrets;
|
|
405280
406636
|
exports.redactSettingsSections = redactSettingsSections;
|
|
405281
406637
|
exports.redactSettingsAggregate = redactSettingsAggregate;
|
|
405282
406638
|
exports.redactConfigEntries = redactConfigEntries;
|
|
@@ -405314,6 +406670,14 @@ var require_device_config_secret_redaction = __commonJS({
|
|
|
405314
406670
|
return data;
|
|
405315
406671
|
return { ...data, config: redactRecord(config) };
|
|
405316
406672
|
}
|
|
406673
|
+
function redactDeviceInfoRecordSecrets(data) {
|
|
406674
|
+
if (!isRecord(data))
|
|
406675
|
+
return data;
|
|
406676
|
+
const out = {};
|
|
406677
|
+
for (const [key, value] of Object.entries(data))
|
|
406678
|
+
out[key] = redactDeviceInfoSecrets(value);
|
|
406679
|
+
return out;
|
|
406680
|
+
}
|
|
405317
406681
|
function redactField(field) {
|
|
405318
406682
|
if (!isRecord(field))
|
|
405319
406683
|
return field;
|
|
@@ -405372,6 +406736,7 @@ var require_device_config_secret_redaction = __commonJS({
|
|
|
405372
406736
|
["deviceManager.getDevice", redactDeviceInfoSecrets],
|
|
405373
406737
|
["deviceManager.listAll", redactDeviceInfoSecrets],
|
|
405374
406738
|
["deviceManager.getChildren", redactDeviceInfoSecrets],
|
|
406739
|
+
["deviceManager.getChildrenBatch", redactDeviceInfoRecordSecrets],
|
|
405375
406740
|
["deviceManager.getDeviceSettingsAggregate", redactSettingsSections],
|
|
405376
406741
|
["deviceManager.getSettingsSchema", redactSettingsSections],
|
|
405377
406742
|
["deviceManager.getDeviceAggregate", redactSettingsAggregate],
|
|
@@ -408962,6 +410327,234 @@ var require_addon_settings_provider = __commonJS({
|
|
|
408962
410327
|
}
|
|
408963
410328
|
});
|
|
408964
410329
|
|
|
410330
|
+
// ../../server/backend/dist/core/addon/single-flight-refresh.js
|
|
410331
|
+
var require_single_flight_refresh = __commonJS({
|
|
410332
|
+
"../../server/backend/dist/core/addon/single-flight-refresh.js"(exports) {
|
|
410333
|
+
"use strict";
|
|
410334
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
410335
|
+
exports.createSingleFlightRefresh = createSingleFlightRefresh;
|
|
410336
|
+
function widenScope(a, b) {
|
|
410337
|
+
if (a === null || b === null)
|
|
410338
|
+
return null;
|
|
410339
|
+
const out = new Set(a);
|
|
410340
|
+
for (const id of b)
|
|
410341
|
+
out.add(id);
|
|
410342
|
+
return out;
|
|
410343
|
+
}
|
|
410344
|
+
function createSingleFlightRefresh(run) {
|
|
410345
|
+
let inFlight = null;
|
|
410346
|
+
let pendingScope;
|
|
410347
|
+
let pendingPromise = null;
|
|
410348
|
+
let releasePending = () => {
|
|
410349
|
+
};
|
|
410350
|
+
const start = (scope) => {
|
|
410351
|
+
const active = (async () => {
|
|
410352
|
+
try {
|
|
410353
|
+
await run(scope);
|
|
410354
|
+
} finally {
|
|
410355
|
+
inFlight = null;
|
|
410356
|
+
if (pendingScope !== void 0) {
|
|
410357
|
+
const next = pendingScope;
|
|
410358
|
+
pendingScope = void 0;
|
|
410359
|
+
const release = releasePending;
|
|
410360
|
+
releasePending = () => {
|
|
410361
|
+
};
|
|
410362
|
+
pendingPromise = null;
|
|
410363
|
+
void start(next).finally(release);
|
|
410364
|
+
}
|
|
410365
|
+
}
|
|
410366
|
+
})();
|
|
410367
|
+
inFlight = active;
|
|
410368
|
+
return active;
|
|
410369
|
+
};
|
|
410370
|
+
return {
|
|
410371
|
+
request: async (scope) => {
|
|
410372
|
+
if (scope !== null && scope.size === 0)
|
|
410373
|
+
return;
|
|
410374
|
+
if (inFlight === null) {
|
|
410375
|
+
await start(scope);
|
|
410376
|
+
return;
|
|
410377
|
+
}
|
|
410378
|
+
pendingScope = pendingScope === void 0 ? scope : widenScope(pendingScope, scope);
|
|
410379
|
+
if (pendingPromise === null) {
|
|
410380
|
+
pendingPromise = new Promise((resolve) => {
|
|
410381
|
+
releasePending = resolve;
|
|
410382
|
+
});
|
|
410383
|
+
}
|
|
410384
|
+
const wait = pendingPromise;
|
|
410385
|
+
if (wait !== null)
|
|
410386
|
+
await wait;
|
|
410387
|
+
}
|
|
410388
|
+
};
|
|
410389
|
+
}
|
|
410390
|
+
}
|
|
410391
|
+
});
|
|
410392
|
+
|
|
410393
|
+
// ../../server/backend/dist/core/addon/device-meta-mirror.js
|
|
410394
|
+
var require_device_meta_mirror = __commonJS({
|
|
410395
|
+
"../../server/backend/dist/core/addon/device-meta-mirror.js"(exports) {
|
|
410396
|
+
"use strict";
|
|
410397
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
410398
|
+
exports.DeviceMetaMirror = exports.MIRROR_IRRELEVANT_META_FIELDS = void 0;
|
|
410399
|
+
exports.classifyDeviceEvent = classifyDeviceEvent;
|
|
410400
|
+
var types_1 = require_dist4();
|
|
410401
|
+
var single_flight_refresh_js_1 = require_single_flight_refresh();
|
|
410402
|
+
exports.MIRROR_IRRELEVANT_META_FIELDS = /* @__PURE__ */ new Set([
|
|
410403
|
+
"name",
|
|
410404
|
+
"disabled",
|
|
410405
|
+
"metadata",
|
|
410406
|
+
"display",
|
|
410407
|
+
"role",
|
|
410408
|
+
"integrationId",
|
|
410409
|
+
"linkDeviceId",
|
|
410410
|
+
"primaryChildEntityId",
|
|
410411
|
+
"childLayout"
|
|
410412
|
+
]);
|
|
410413
|
+
function classifyDeviceEvent(data) {
|
|
410414
|
+
if (data === null || typeof data !== "object")
|
|
410415
|
+
return { kind: "fleet" };
|
|
410416
|
+
const field = Reflect.get(data, "field");
|
|
410417
|
+
if (typeof field === "string" && exports.MIRROR_IRRELEVANT_META_FIELDS.has(field)) {
|
|
410418
|
+
return { kind: "none" };
|
|
410419
|
+
}
|
|
410420
|
+
const deviceId = Reflect.get(data, "deviceId");
|
|
410421
|
+
if (typeof deviceId !== "number" || !Number.isFinite(deviceId))
|
|
410422
|
+
return { kind: "fleet" };
|
|
410423
|
+
return { kind: "device", deviceId };
|
|
410424
|
+
}
|
|
410425
|
+
var MAX_ANCESTOR_HOPS = 8;
|
|
410426
|
+
var DeviceMetaMirror = class {
|
|
410427
|
+
read;
|
|
410428
|
+
live;
|
|
410429
|
+
logger;
|
|
410430
|
+
parents = /* @__PURE__ */ new Map();
|
|
410431
|
+
meta = /* @__PURE__ */ new Map();
|
|
410432
|
+
gate;
|
|
410433
|
+
constructor(read, live, logger) {
|
|
410434
|
+
this.read = read;
|
|
410435
|
+
this.live = live;
|
|
410436
|
+
this.logger = logger;
|
|
410437
|
+
this.gate = (0, single_flight_refresh_js_1.createSingleFlightRefresh)((scope) => this.refresh(scope));
|
|
410438
|
+
}
|
|
410439
|
+
/** Ask for a refresh covering exactly what the event changed. */
|
|
410440
|
+
onDeviceEvent(data) {
|
|
410441
|
+
const scope = classifyDeviceEvent(data);
|
|
410442
|
+
if (scope.kind === "none")
|
|
410443
|
+
return;
|
|
410444
|
+
void this.gate.request(scope.kind === "fleet" ? null : /* @__PURE__ */ new Set([scope.deviceId]));
|
|
410445
|
+
}
|
|
410446
|
+
/** Warm (or re-warm) the whole fleet — the boot sweep, and the D8 reconcile
|
|
410447
|
+
* that repairs anything a dropped event would have left behind. */
|
|
410448
|
+
async refreshAll() {
|
|
410449
|
+
await this.gate.request(null);
|
|
410450
|
+
}
|
|
410451
|
+
/** Parent of a device: mirror first (covers forked devices), then the live
|
|
410452
|
+
* hub registry (covers a hub-local device before the first warm). */
|
|
410453
|
+
parentOf(deviceId) {
|
|
410454
|
+
const mirrored = this.parents.get(deviceId);
|
|
410455
|
+
if (mirrored !== void 0)
|
|
410456
|
+
return mirrored;
|
|
410457
|
+
return this.live.parentDeviceId(deviceId);
|
|
410458
|
+
}
|
|
410459
|
+
/** Persisted `DeviceType` string, or null when unknown. */
|
|
410460
|
+
typeOf(deviceId) {
|
|
410461
|
+
const mirrored = this.meta.get(deviceId);
|
|
410462
|
+
if (mirrored !== void 0)
|
|
410463
|
+
return mirrored.type;
|
|
410464
|
+
return this.live.type(deviceId);
|
|
410465
|
+
}
|
|
410466
|
+
/** Persisted operator `location` label, or null when unset/unknown. */
|
|
410467
|
+
locationOf(deviceId) {
|
|
410468
|
+
const mirrored = this.meta.get(deviceId);
|
|
410469
|
+
if (mirrored !== void 0)
|
|
410470
|
+
return mirrored.location;
|
|
410471
|
+
return this.live.location(deviceId);
|
|
410472
|
+
}
|
|
410473
|
+
/** The whole mirrored fleet — fuels fleet-wide selector expansion. */
|
|
410474
|
+
list() {
|
|
410475
|
+
const out = [];
|
|
410476
|
+
for (const [id, m] of this.meta) {
|
|
410477
|
+
out.push({
|
|
410478
|
+
id,
|
|
410479
|
+
type: m.type,
|
|
410480
|
+
location: m.location,
|
|
410481
|
+
parentDeviceId: this.parents.get(id) ?? null
|
|
410482
|
+
});
|
|
410483
|
+
}
|
|
410484
|
+
return out;
|
|
410485
|
+
}
|
|
410486
|
+
/** Ancestor chain (parent, grandparent, …), bounded. Empty for a top-level
|
|
410487
|
+
* device or one the hub has never heard of. */
|
|
410488
|
+
ancestorsOf(deviceId) {
|
|
410489
|
+
const out = [];
|
|
410490
|
+
let current = deviceId;
|
|
410491
|
+
for (let hop = 0; hop < MAX_ANCESTOR_HOPS; hop++) {
|
|
410492
|
+
const parent = this.parentOf(current);
|
|
410493
|
+
if (parent === null || parent === current)
|
|
410494
|
+
break;
|
|
410495
|
+
out.push(parent);
|
|
410496
|
+
current = parent;
|
|
410497
|
+
}
|
|
410498
|
+
return out;
|
|
410499
|
+
}
|
|
410500
|
+
async refresh(scope) {
|
|
410501
|
+
try {
|
|
410502
|
+
const ids = scope === null ? null : [...scope];
|
|
410503
|
+
const rows = await this.read(ids);
|
|
410504
|
+
if (!Array.isArray(rows))
|
|
410505
|
+
return;
|
|
410506
|
+
const nextParents = /* @__PURE__ */ new Map();
|
|
410507
|
+
const nextMeta = /* @__PURE__ */ new Map();
|
|
410508
|
+
for (const row of rows) {
|
|
410509
|
+
if (row === null || typeof row !== "object")
|
|
410510
|
+
continue;
|
|
410511
|
+
const id = Reflect.get(row, "id");
|
|
410512
|
+
if (typeof id !== "number")
|
|
410513
|
+
continue;
|
|
410514
|
+
const parent = Reflect.get(row, "parentDeviceId");
|
|
410515
|
+
if (typeof parent === "number")
|
|
410516
|
+
nextParents.set(id, parent);
|
|
410517
|
+
const type = Reflect.get(row, "type");
|
|
410518
|
+
const location = Reflect.get(row, "location");
|
|
410519
|
+
nextMeta.set(id, {
|
|
410520
|
+
type: typeof type === "string" ? type : "",
|
|
410521
|
+
location: typeof location === "string" ? location : null
|
|
410522
|
+
});
|
|
410523
|
+
}
|
|
410524
|
+
if (ids === null) {
|
|
410525
|
+
this.parents.clear();
|
|
410526
|
+
for (const [k, v] of nextParents)
|
|
410527
|
+
this.parents.set(k, v);
|
|
410528
|
+
this.meta.clear();
|
|
410529
|
+
for (const [k, v] of nextMeta)
|
|
410530
|
+
this.meta.set(k, v);
|
|
410531
|
+
return;
|
|
410532
|
+
}
|
|
410533
|
+
for (const id of ids) {
|
|
410534
|
+
const m = nextMeta.get(id);
|
|
410535
|
+
if (m === void 0) {
|
|
410536
|
+
this.parents.delete(id);
|
|
410537
|
+
this.meta.delete(id);
|
|
410538
|
+
continue;
|
|
410539
|
+
}
|
|
410540
|
+
this.meta.set(id, m);
|
|
410541
|
+
const parent = nextParents.get(id);
|
|
410542
|
+
if (parent === void 0)
|
|
410543
|
+
this.parents.delete(id);
|
|
410544
|
+
else
|
|
410545
|
+
this.parents.set(id, parent);
|
|
410546
|
+
}
|
|
410547
|
+
} catch (err) {
|
|
410548
|
+
this.logger.debug("device-meta mirror refresh failed \u2014 keeping previous", {
|
|
410549
|
+
meta: { error: (0, types_1.errMsg)(err), scope: scope === null ? "fleet" : scope.size }
|
|
410550
|
+
});
|
|
410551
|
+
}
|
|
410552
|
+
}
|
|
410553
|
+
};
|
|
410554
|
+
exports.DeviceMetaMirror = DeviceMetaMirror;
|
|
410555
|
+
}
|
|
410556
|
+
});
|
|
410557
|
+
|
|
408965
410558
|
// ../../server/backend/dist/core/addon/integration-visibility.js
|
|
408966
410559
|
var require_integration_visibility = __commonJS({
|
|
408967
410560
|
"../../server/backend/dist/core/addon/integration-visibility.js"(exports) {
|
|
@@ -409221,58 +410814,6 @@ var require_runner_spawn_fanout = __commonJS({
|
|
|
409221
410814
|
}
|
|
409222
410815
|
});
|
|
409223
410816
|
|
|
409224
|
-
// ../../server/backend/dist/core/addon/single-flight-refresh.js
|
|
409225
|
-
var require_single_flight_refresh = __commonJS({
|
|
409226
|
-
"../../server/backend/dist/core/addon/single-flight-refresh.js"(exports) {
|
|
409227
|
-
"use strict";
|
|
409228
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
409229
|
-
exports.createSingleFlightRefresh = createSingleFlightRefresh;
|
|
409230
|
-
function createSingleFlightRefresh(run) {
|
|
409231
|
-
let inFlight = null;
|
|
409232
|
-
let pending = false;
|
|
409233
|
-
let pendingPromise = null;
|
|
409234
|
-
let releasePending = () => {
|
|
409235
|
-
};
|
|
409236
|
-
const start = () => {
|
|
409237
|
-
const active = (async () => {
|
|
409238
|
-
try {
|
|
409239
|
-
await run();
|
|
409240
|
-
} finally {
|
|
409241
|
-
inFlight = null;
|
|
409242
|
-
if (pending) {
|
|
409243
|
-
pending = false;
|
|
409244
|
-
const release = releasePending;
|
|
409245
|
-
releasePending = () => {
|
|
409246
|
-
};
|
|
409247
|
-
pendingPromise = null;
|
|
409248
|
-
void start().finally(release);
|
|
409249
|
-
}
|
|
409250
|
-
}
|
|
409251
|
-
})();
|
|
409252
|
-
inFlight = active;
|
|
409253
|
-
return active;
|
|
409254
|
-
};
|
|
409255
|
-
return {
|
|
409256
|
-
request: async () => {
|
|
409257
|
-
if (inFlight === null) {
|
|
409258
|
-
await start();
|
|
409259
|
-
return;
|
|
409260
|
-
}
|
|
409261
|
-
if (!pending) {
|
|
409262
|
-
pending = true;
|
|
409263
|
-
pendingPromise = new Promise((resolve) => {
|
|
409264
|
-
releasePending = resolve;
|
|
409265
|
-
});
|
|
409266
|
-
}
|
|
409267
|
-
const wait = pendingPromise;
|
|
409268
|
-
if (wait !== null)
|
|
409269
|
-
await wait;
|
|
409270
|
-
}
|
|
409271
|
-
};
|
|
409272
|
-
}
|
|
409273
|
-
}
|
|
409274
|
-
});
|
|
409275
|
-
|
|
409276
410817
|
// ../../server/backend/dist/core/addon/addon-registry.service.js
|
|
409277
410818
|
var require_addon_registry_service = __commonJS({
|
|
409278
410819
|
"../../server/backend/dist/core/addon/addon-registry.service.js"(exports) {
|
|
@@ -409329,13 +410870,13 @@ var require_addon_registry_service = __commonJS({
|
|
|
409329
410870
|
var addon_call_gateway_js_1 = require_addon_call_gateway();
|
|
409330
410871
|
var addon_row_manifest_1 = require_addon_row_manifest();
|
|
409331
410872
|
var addon_settings_provider_js_1 = require_addon_settings_provider();
|
|
410873
|
+
var device_meta_mirror_js_1 = require_device_meta_mirror();
|
|
409332
410874
|
var integration_visibility_js_1 = require_integration_visibility();
|
|
409333
410875
|
var package_dir_utils_1 = require_package_dir_utils();
|
|
409334
410876
|
var prune_misplaced_addons_js_1 = require_prune_misplaced_addons();
|
|
409335
410877
|
var require_cache_js_1 = require_require_cache();
|
|
409336
410878
|
var runner_convergence_1 = require_runner_convergence();
|
|
409337
410879
|
var runner_spawn_fanout_js_1 = require_runner_spawn_fanout();
|
|
409338
|
-
var single_flight_refresh_js_1 = require_single_flight_refresh();
|
|
409339
410880
|
function shouldEvictMissingOnDisk(entry, id, onDiskIds) {
|
|
409340
410881
|
return entry.source === "installed" && entry.packageName !== "@camstack/system" && !onDiskIds.has(id);
|
|
409341
410882
|
}
|
|
@@ -409542,6 +411083,14 @@ var require_addon_registry_service = __commonJS({
|
|
|
409542
411083
|
this.streamProbe = streamProbe;
|
|
409543
411084
|
this.logger = this.loggingService.createLogger("AddonRegistry");
|
|
409544
411085
|
this.addonLoader = new system_1.AddonLoader(this.loggingService.createLogger("AddonLoader"));
|
|
411086
|
+
this.deviceMirror = new device_meta_mirror_js_1.DeviceMetaMirror(async (deviceIds) => this.getBrokerApi().deviceManager.listAll.query({
|
|
411087
|
+
projection: "slim",
|
|
411088
|
+
...deviceIds === null ? {} : { deviceIds: [...deviceIds] }
|
|
411089
|
+
}), {
|
|
411090
|
+
parentDeviceId: (deviceId) => this.deviceRegistry.getById(deviceId)?.parentDeviceId ?? null,
|
|
411091
|
+
type: (deviceId) => this.deviceRegistry.getById(deviceId)?.type ?? null,
|
|
411092
|
+
location: (deviceId) => this.deviceRegistry.getById(deviceId)?.location ?? null
|
|
411093
|
+
}, this.logger);
|
|
409545
411094
|
this.healthMonitor = new system_1.AddonHealthMonitor({
|
|
409546
411095
|
eventBus: this.eventBusService,
|
|
409547
411096
|
logger: this.loggingService.createLogger("AddonHealthMonitor"),
|
|
@@ -410103,56 +411652,22 @@ var require_addon_registry_service = __commonJS({
|
|
|
410103
411652
|
// fleet — forked devices included. This is a hub-process, SYNCHRONOUSLY
|
|
410104
411653
|
// readable mirror of that parentage, refreshed OFF the request path on every
|
|
410105
411654
|
// device-meta lifecycle event — never a per-call DB query.
|
|
410106
|
-
|
|
410107
|
-
|
|
410108
|
-
|
|
410109
|
-
|
|
410110
|
-
|
|
410111
|
-
* matcher resolves a selector against a device's type/location synchronously,
|
|
410112
|
-
* off the request path (D49). Empty for a device the hub has never heard of.
|
|
410113
|
-
*/
|
|
410114
|
-
deviceMetaMirror = /* @__PURE__ */ new Map();
|
|
410115
|
-
/** Parent of a device: the persisted mirror first (covers forked devices),
|
|
410116
|
-
* falling back to the live hub registry (covers a hub-local device before the
|
|
410117
|
-
* first mirror warm). null when top-level or unknown. */
|
|
410118
|
-
parentOfDevice(deviceId) {
|
|
410119
|
-
const mirrored = this.deviceParentMirror.get(deviceId);
|
|
410120
|
-
if (mirrored !== void 0)
|
|
410121
|
-
return mirrored;
|
|
410122
|
-
return this.deviceRegistry.getById(deviceId)?.parentDeviceId ?? null;
|
|
410123
|
-
}
|
|
411655
|
+
//
|
|
411656
|
+
// The mirror itself, and the rule that decides how much of the fleet one
|
|
411657
|
+
// event may re-read, live in `device-meta-mirror.ts` — with the 4 904-call /
|
|
411658
|
+
// 4 987 368-row boot census that made the rule necessary.
|
|
411659
|
+
deviceMirror;
|
|
410124
411660
|
/** Persisted `DeviceType` string of a device, or null when unknown. Mirror
|
|
410125
411661
|
* first (covers forked devices), then the live hub registry. */
|
|
410126
|
-
getPersistedType = (deviceId) =>
|
|
410127
|
-
const mirrored = this.deviceMetaMirror.get(deviceId);
|
|
410128
|
-
if (mirrored !== void 0)
|
|
410129
|
-
return mirrored.type;
|
|
410130
|
-
return this.deviceRegistry.getById(deviceId)?.type ?? null;
|
|
410131
|
-
};
|
|
411662
|
+
getPersistedType = (deviceId) => this.deviceMirror.typeOf(deviceId);
|
|
410132
411663
|
/** Persisted operator `location` label of a device, or null when unset/unknown. */
|
|
410133
|
-
getPersistedLocation = (deviceId) =>
|
|
410134
|
-
const mirrored = this.deviceMetaMirror.get(deviceId);
|
|
410135
|
-
if (mirrored !== void 0)
|
|
410136
|
-
return mirrored.location;
|
|
410137
|
-
return this.deviceRegistry.getById(deviceId)?.location ?? null;
|
|
410138
|
-
};
|
|
411664
|
+
getPersistedLocation = (deviceId) => this.deviceMirror.locationOf(deviceId);
|
|
410139
411665
|
/**
|
|
410140
411666
|
* The whole persisted fleet, slim — id + type + location + parentDeviceId.
|
|
410141
411667
|
* Fuels the FLEET-wide selector expansion (response projection + `auth.me`
|
|
410142
411668
|
* counts). Read synchronously off the mirror, never a per-call DB query.
|
|
410143
411669
|
*/
|
|
410144
|
-
getPersistedDeviceList = () =>
|
|
410145
|
-
const out = [];
|
|
410146
|
-
for (const [id, m] of this.deviceMetaMirror) {
|
|
410147
|
-
out.push({
|
|
410148
|
-
id,
|
|
410149
|
-
type: m.type,
|
|
410150
|
-
location: m.location,
|
|
410151
|
-
parentDeviceId: this.deviceParentMirror.get(id) ?? null
|
|
410152
|
-
});
|
|
410153
|
-
}
|
|
410154
|
-
return out;
|
|
410155
|
-
};
|
|
411670
|
+
getPersistedDeviceList = () => this.deviceMirror.list();
|
|
410156
411671
|
/**
|
|
410157
411672
|
* Ancestor chain (parent, grandparent, …) of a device, bounded to 8 hops
|
|
410158
411673
|
* (defence-in-depth against a corrupt registry cycle). Synchronous — it is
|
|
@@ -410160,80 +411675,31 @@ var require_addon_registry_service = __commonJS({
|
|
|
410160
411675
|
* a FORKED camera covers its accessory children. Empty for a top-level device
|
|
410161
411676
|
* or one the hub has never heard of.
|
|
410162
411677
|
*/
|
|
410163
|
-
getPersistedAncestors = (deviceId) =>
|
|
410164
|
-
|
|
410165
|
-
|
|
410166
|
-
|
|
410167
|
-
|
|
410168
|
-
|
|
410169
|
-
|
|
410170
|
-
|
|
410171
|
-
|
|
410172
|
-
|
|
410173
|
-
|
|
410174
|
-
};
|
|
410175
|
-
/** One full-fleet rebuild in flight, at most one queued behind it. Every
|
|
410176
|
-
* event below asks for the SAME total rebuild, so running it once per event
|
|
410177
|
-
* was ~1 000 rebuilds of a 974-row mirror on a boot — each one a 625 KB read
|
|
410178
|
-
* and parse on hub-main's event loop. No timer: the first event still
|
|
410179
|
-
* refreshes immediately, because this mirror backs scope enforcement.
|
|
410180
|
-
* See `single-flight-refresh.ts`. */
|
|
410181
|
-
deviceMirrorGate = (0, single_flight_refresh_js_1.createSingleFlightRefresh)(() => this.refreshDeviceParentMirror());
|
|
410182
|
-
/** Subscribe the mirror to every device-meta lifecycle event, and warm it once
|
|
410183
|
-
* the addon set (device-manager included) is up. */
|
|
411678
|
+
getPersistedAncestors = (deviceId) => this.deviceMirror.ancestorsOf(deviceId);
|
|
411679
|
+
/**
|
|
411680
|
+
* Subscribe the mirror to every device-meta lifecycle event, and warm it once
|
|
411681
|
+
* the addon set (device-manager included) is up.
|
|
411682
|
+
*
|
|
411683
|
+
* The device events ask for a SCOPED refresh — the one device they name, or
|
|
411684
|
+
* nothing at all when they name a field the mirror does not hold.
|
|
411685
|
+
* `SystemAddonsReady` is the only fleet-wide read, and it is also the D8
|
|
411686
|
+
* reconcile: whatever a dropped event would have left stale, the warm
|
|
411687
|
+
* repairs.
|
|
411688
|
+
*/
|
|
410184
411689
|
wireDeviceParentMirror() {
|
|
410185
|
-
const refresh = () => {
|
|
410186
|
-
void this.deviceMirrorGate.request();
|
|
410187
|
-
};
|
|
410188
411690
|
for (const category of [
|
|
410189
411691
|
types_1.EventCategory.DeviceMetaChanged,
|
|
410190
411692
|
types_1.EventCategory.DeviceRegistered,
|
|
410191
411693
|
types_1.EventCategory.DeviceUnregistered,
|
|
410192
|
-
types_1.EventCategory.DeviceProvisioned
|
|
410193
|
-
types_1.EventCategory.SystemAddonsReady
|
|
411694
|
+
types_1.EventCategory.DeviceProvisioned
|
|
410194
411695
|
]) {
|
|
410195
|
-
this.eventBusService.subscribe({ category },
|
|
410196
|
-
|
|
410197
|
-
}
|
|
410198
|
-
/** Rebuild the parent mirror from the device-manager's persisted fleet
|
|
410199
|
-
* (`listAll`, slim projection — no config blob). Off the request path; a
|
|
410200
|
-
* failed read KEEPS the previous mirror (D49 — a read that fails changes
|
|
410201
|
-
* nothing, and must never look like an unbind that destroys inheritance). */
|
|
410202
|
-
async refreshDeviceParentMirror() {
|
|
410203
|
-
try {
|
|
410204
|
-
const api = this.getBrokerApi();
|
|
410205
|
-
const rows = await api.deviceManager.listAll.query({ projection: "slim" });
|
|
410206
|
-
if (!Array.isArray(rows))
|
|
410207
|
-
return;
|
|
410208
|
-
const nextParents = /* @__PURE__ */ new Map();
|
|
410209
|
-
const nextMeta = /* @__PURE__ */ new Map();
|
|
410210
|
-
for (const row of rows) {
|
|
410211
|
-
if (row === null || typeof row !== "object")
|
|
410212
|
-
continue;
|
|
410213
|
-
const id = Reflect.get(row, "id");
|
|
410214
|
-
if (typeof id !== "number")
|
|
410215
|
-
continue;
|
|
410216
|
-
const parent = Reflect.get(row, "parentDeviceId");
|
|
410217
|
-
if (typeof parent === "number")
|
|
410218
|
-
nextParents.set(id, parent);
|
|
410219
|
-
const type = Reflect.get(row, "type");
|
|
410220
|
-
const location = Reflect.get(row, "location");
|
|
410221
|
-
nextMeta.set(id, {
|
|
410222
|
-
type: typeof type === "string" ? type : "",
|
|
410223
|
-
location: typeof location === "string" ? location : null
|
|
410224
|
-
});
|
|
410225
|
-
}
|
|
410226
|
-
this.deviceParentMirror.clear();
|
|
410227
|
-
for (const [k, v] of nextParents)
|
|
410228
|
-
this.deviceParentMirror.set(k, v);
|
|
410229
|
-
this.deviceMetaMirror.clear();
|
|
410230
|
-
for (const [k, v] of nextMeta)
|
|
410231
|
-
this.deviceMetaMirror.set(k, v);
|
|
410232
|
-
} catch (err) {
|
|
410233
|
-
this.logger.debug("device-parent mirror refresh failed \u2014 keeping previous", {
|
|
410234
|
-
meta: { error: (0, types_1.errMsg)(err) }
|
|
411696
|
+
this.eventBusService.subscribe({ category }, (event) => {
|
|
411697
|
+
this.deviceMirror.onDeviceEvent(event.data);
|
|
410235
411698
|
});
|
|
410236
411699
|
}
|
|
411700
|
+
this.eventBusService.subscribe({ category: types_1.EventCategory.SystemAddonsReady }, () => {
|
|
411701
|
+
void this.deviceMirror.refreshAll();
|
|
411702
|
+
});
|
|
410237
411703
|
}
|
|
410238
411704
|
/** Load persisted collection disabled-lists from settings-store into the registry */
|
|
410239
411705
|
loadCollectionPreferences() {
|
|
@@ -416301,7 +417767,7 @@ var require_main4 = __commonJS({
|
|
|
416301
417767
|
async function bootstrap() {
|
|
416302
417768
|
const heapReclaimer = (0, system_1.createV8Reclaimer)();
|
|
416303
417769
|
const heapReclaim = heapReclaimer === void 0 ? void 0 : { reclaim: heapReclaimer };
|
|
416304
|
-
(0, system_1.startHeapWatch)("hub-main", void 0, void 0, heapReclaim);
|
|
417770
|
+
(0, system_1.startHeapWatch)("hub-main", void 0, void 0, heapReclaim, void 0, void 0, true, (0, system_1.hubMainRssBudget)());
|
|
416305
417771
|
cleanupOrphanProcesses();
|
|
416306
417772
|
let spaIndexHtml = null;
|
|
416307
417773
|
const configPath = process.env.CONFIG_PATH ?? path.join(process.env.CAMSTACK_DATA ?? path.join(process.cwd(), "camstack-data"), "config.yaml");
|