camstack 1.2.111 → 1.2.112
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-jZb-HKI5.js
|
|
23637
|
+
var require_dist_jZb_HKI5 = __commonJS({
|
|
23638
|
+
"../system/dist/dist-jZb-HKI5.js"(exports) {
|
|
23639
23639
|
"use strict";
|
|
23640
23640
|
var zod = require_zod();
|
|
23641
23641
|
var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
|
|
@@ -26081,46 +26081,6 @@ var require_dist_CcV3k_hD = __commonJS({
|
|
|
26081
26081
|
function logLevelAtMost(level, threshold) {
|
|
26082
26082
|
return LOG_LEVEL_RANK[level] <= LOG_LEVEL_RANK[threshold];
|
|
26083
26083
|
}
|
|
26084
|
-
var LogChannelLevelSchema = zod.z.enum([
|
|
26085
|
-
"info",
|
|
26086
|
-
"warn",
|
|
26087
|
-
"error"
|
|
26088
|
-
]);
|
|
26089
|
-
var LogChannelDescriptorSchema = zod.z.object({
|
|
26090
|
-
/**
|
|
26091
|
-
* Dotted `area.thing`, unique across the workspace. `area` is conventionally
|
|
26092
|
-
* the addon's short name so an operator reading a channel list can tell who
|
|
26093
|
-
* owns it without a second lookup.
|
|
26094
|
-
*/
|
|
26095
|
-
name: zod.z.string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
|
|
26096
|
-
/** One sentence: what the operator will SEE after arming it. */
|
|
26097
|
-
description: zod.z.string().min(1),
|
|
26098
|
-
/** The level its lines are emitted at. Never below `info`. */
|
|
26099
|
-
defaultLevel: LogChannelLevelSchema,
|
|
26100
|
-
/**
|
|
26101
|
-
* Whether this channel can be narrowed to a camera.
|
|
26102
|
-
*
|
|
26103
|
-
* `true` is a PROMISE with two halves, and both must hold: the gate is
|
|
26104
|
-
* consulted with the numeric device id, AND every line the channel admits
|
|
26105
|
-
* carries `tags: { deviceId }` with that same numeric id. The second half is
|
|
26106
|
-
* what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
|
|
26107
|
-
* keeps `deviceId` out of the stream labels for cardinality, so the tag in
|
|
26108
|
-
* the body is the only way to filter.
|
|
26109
|
-
*
|
|
26110
|
-
* A channel whose lines carry the device only in `meta` (or not at all) is
|
|
26111
|
-
* declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
|
|
26112
|
-
* the operator narrows to one camera, sees nothing, and concludes the code
|
|
26113
|
-
* path was never taken.
|
|
26114
|
-
*/
|
|
26115
|
-
perDevice: zod.z.boolean()
|
|
26116
|
-
});
|
|
26117
|
-
var LogChannelWindowSchema = zod.z.object({
|
|
26118
|
-
channel: zod.z.string().min(1),
|
|
26119
|
-
/** Epoch ms the window closes at. */
|
|
26120
|
-
armedUntilMs: zod.z.number(),
|
|
26121
|
-
/** `null` = every camera. A non-empty list narrows to those numeric ids. */
|
|
26122
|
-
deviceIds: zod.z.array(zod.z.number().int()).readonly().nullable()
|
|
26123
|
-
});
|
|
26124
26084
|
var OpsLogDomainSchema = zod.z.enum(["recording", "events"]);
|
|
26125
26085
|
var OpsLogOpSchema = zod.z.enum([
|
|
26126
26086
|
"prune",
|
|
@@ -26711,21 +26671,20 @@ var require_dist_CcV3k_hD = __commonJS({
|
|
|
26711
26671
|
* flag at upsert time, not here (the schema is provider-agnostic).
|
|
26712
26672
|
*/
|
|
26713
26673
|
nodeId: zod.z.string().optional(),
|
|
26714
|
-
isDefault: zod.z.boolean().default(false),
|
|
26715
26674
|
isSystem: zod.z.boolean().default(false),
|
|
26716
26675
|
/**
|
|
26717
|
-
*
|
|
26718
|
-
*
|
|
26719
|
-
*
|
|
26720
|
-
*
|
|
26721
|
-
*
|
|
26676
|
+
* THE write switch, and the only one (D383). `enabled: true` means every
|
|
26677
|
+
* consumer that chooses a write target for this type may write here, and all
|
|
26678
|
+
* enabled locations of a type are used TOGETHER; `false` means read-only —
|
|
26679
|
+
* still read, still played back, still age-swept, still drained, never
|
|
26680
|
+
* written.
|
|
26722
26681
|
*
|
|
26723
|
-
* OPTIONAL
|
|
26724
|
-
*
|
|
26725
|
-
*
|
|
26726
|
-
*
|
|
26727
|
-
*
|
|
26728
|
-
*
|
|
26682
|
+
* OPTIONAL only for the wire: an upsert that omits it means "leave what is
|
|
26683
|
+
* stored" on an update and "born inert unless it is the first location of its
|
|
26684
|
+
* type" on a create. On a PERSISTED row absence is legacy and it means
|
|
26685
|
+
* enabled — {@link isLocationEnabled} is the one place that says so, and the
|
|
26686
|
+
* orchestrator stamps every flagless row `true` once at hydrate so absence
|
|
26687
|
+
* stops existing rather than being re-derived on every read.
|
|
26729
26688
|
*/
|
|
26730
26689
|
enabled: zod.z.boolean().optional(),
|
|
26731
26690
|
/** COMPUTED at read time by the orchestrator (statfs of the backing volume
|
|
@@ -26738,6 +26697,10 @@ var require_dist_CcV3k_hD = __commonJS({
|
|
|
26738
26697
|
createdAt: zod.z.number(),
|
|
26739
26698
|
updatedAt: zod.z.number()
|
|
26740
26699
|
});
|
|
26700
|
+
var LegacyStorageLocationDefaultSchema = zod.z.object({ isDefault: zod.z.boolean().optional() });
|
|
26701
|
+
function isLocationEnabled(location) {
|
|
26702
|
+
return location.enabled !== false;
|
|
26703
|
+
}
|
|
26741
26704
|
var StorageLocationRefSchema = zod.z.union([StorageLocationTypeSchema, zod.z.string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/)]);
|
|
26742
26705
|
var StorageAccessSchema = zod.z.enum(["local-path", "cap-mediated"]);
|
|
26743
26706
|
var STORAGE_ACCESS_FALLBACK = "local-path";
|
|
@@ -26865,6 +26828,46 @@ var require_dist_CcV3k_hD = __commonJS({
|
|
|
26865
26828
|
*/
|
|
26866
26829
|
debug: zod.z.boolean().optional()
|
|
26867
26830
|
});
|
|
26831
|
+
var LogChannelLevelSchema = zod.z.enum([
|
|
26832
|
+
"info",
|
|
26833
|
+
"warn",
|
|
26834
|
+
"error"
|
|
26835
|
+
]);
|
|
26836
|
+
var LogChannelDescriptorSchema = zod.z.object({
|
|
26837
|
+
/**
|
|
26838
|
+
* Dotted `area.thing`, unique across the workspace. `area` is conventionally
|
|
26839
|
+
* the addon's short name so an operator reading a channel list can tell who
|
|
26840
|
+
* owns it without a second lookup.
|
|
26841
|
+
*/
|
|
26842
|
+
name: zod.z.string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
|
|
26843
|
+
/** One sentence: what the operator will SEE after arming it. */
|
|
26844
|
+
description: zod.z.string().min(1),
|
|
26845
|
+
/** The level its lines are emitted at. Never below `info`. */
|
|
26846
|
+
defaultLevel: LogChannelLevelSchema,
|
|
26847
|
+
/**
|
|
26848
|
+
* Whether this channel can be narrowed to a camera.
|
|
26849
|
+
*
|
|
26850
|
+
* `true` is a PROMISE with two halves, and both must hold: the gate is
|
|
26851
|
+
* consulted with the numeric device id, AND every line the channel admits
|
|
26852
|
+
* carries `tags: { deviceId }` with that same numeric id. The second half is
|
|
26853
|
+
* what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
|
|
26854
|
+
* keeps `deviceId` out of the stream labels for cardinality, so the tag in
|
|
26855
|
+
* the body is the only way to filter.
|
|
26856
|
+
*
|
|
26857
|
+
* A channel whose lines carry the device only in `meta` (or not at all) is
|
|
26858
|
+
* declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
|
|
26859
|
+
* the operator narrows to one camera, sees nothing, and concludes the code
|
|
26860
|
+
* path was never taken.
|
|
26861
|
+
*/
|
|
26862
|
+
perDevice: zod.z.boolean()
|
|
26863
|
+
});
|
|
26864
|
+
var LogChannelWindowSchema = zod.z.object({
|
|
26865
|
+
channel: zod.z.string().min(1),
|
|
26866
|
+
/** Epoch ms the window closes at. */
|
|
26867
|
+
armedUntilMs: zod.z.number(),
|
|
26868
|
+
/** `null` = every camera. A non-empty list narrows to those numeric ids. */
|
|
26869
|
+
deviceIds: zod.z.array(zod.z.number().int()).readonly().nullable()
|
|
26870
|
+
});
|
|
26868
26871
|
var UNATTRIBUTED_BUCKET_KEY = "__unattributed__";
|
|
26869
26872
|
var ROOT_BUCKET_KEY = "__root__";
|
|
26870
26873
|
function bucketFor(row) {
|
|
@@ -40670,7 +40673,6 @@ var require_dist_CcV3k_hD = __commonJS({
|
|
|
40670
40673
|
}), zod.z.instanceof(Uint8Array)),
|
|
40671
40674
|
endDownload: method(zod.z.object({ downloadId: zod.z.string() }), zod.z.void(), { kind: "mutation" }),
|
|
40672
40675
|
listLocations: method(zod.z.object({ type: StorageLocationTypeSchema.optional() }), zod.z.array(StorageLocationSchema).readonly()),
|
|
40673
|
-
getDefaultLocation: method(zod.z.object({ type: StorageLocationTypeSchema }), StorageLocationSchema.nullable()),
|
|
40674
40676
|
listLocationDeclarations: method(zod.z.void(), zod.z.array(StorageLocationDeclarationSchema).readonly()),
|
|
40675
40677
|
upsertLocation: method(StorageLocationSchema.omit({
|
|
40676
40678
|
createdAt: true,
|
|
@@ -50706,6 +50708,26 @@ var require_dist_CcV3k_hD = __commonJS({
|
|
|
50706
50708
|
"ffmpeg.threadCount": 0,
|
|
50707
50709
|
"auth.tokenExpiry": "30d"
|
|
50708
50710
|
};
|
|
50711
|
+
var AccessoryKind = {
|
|
50712
|
+
Siren: DeviceRole.Siren,
|
|
50713
|
+
Floodlight: DeviceRole.Floodlight,
|
|
50714
|
+
Spotlight: DeviceRole.Spotlight,
|
|
50715
|
+
PirSensor: DeviceRole.PirSensor,
|
|
50716
|
+
Chime: DeviceRole.Chime,
|
|
50717
|
+
Autotrack: DeviceRole.Autotrack,
|
|
50718
|
+
Nightvision: DeviceRole.Nightvision,
|
|
50719
|
+
PrivacyMask: DeviceRole.PrivacyMask
|
|
50720
|
+
};
|
|
50721
|
+
AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
|
|
50722
|
+
function deriveBatteryPresence(input) {
|
|
50723
|
+
const status = input.status;
|
|
50724
|
+
if (!status) return "sleeping";
|
|
50725
|
+
if (status.sleeping !== true) return "awake";
|
|
50726
|
+
const lastContactAt = status.lastContactAt;
|
|
50727
|
+
if (typeof lastContactAt !== "number" || lastContactAt <= 0) return "sleeping";
|
|
50728
|
+
const budget = input.unreachableAfterMs ?? 216e5;
|
|
50729
|
+
return input.nowMs - lastContactAt > budget ? "unreachable" : "sleeping";
|
|
50730
|
+
}
|
|
50709
50731
|
var CONTAINER_CHILD_PRIORITY = [
|
|
50710
50732
|
"media-player",
|
|
50711
50733
|
"alarm-panel",
|
|
@@ -50762,26 +50784,6 @@ var require_dist_CcV3k_hD = __commonJS({
|
|
|
50762
50784
|
const namesake = (c) => containerName !== void 0 && containerName.length > 0 && c.name !== void 0 && c.name.toLowerCase() === containerName.toLowerCase() ? 0 : 1;
|
|
50763
50785
|
return [...children].toSorted((a, b) => roleRank(a.role) - roleRank(b.role) || namesake(a) - namesake(b) || rank(a.type) - rank(b.type))[0] ?? null;
|
|
50764
50786
|
}
|
|
50765
|
-
var AccessoryKind = {
|
|
50766
|
-
Siren: DeviceRole.Siren,
|
|
50767
|
-
Floodlight: DeviceRole.Floodlight,
|
|
50768
|
-
Spotlight: DeviceRole.Spotlight,
|
|
50769
|
-
PirSensor: DeviceRole.PirSensor,
|
|
50770
|
-
Chime: DeviceRole.Chime,
|
|
50771
|
-
Autotrack: DeviceRole.Autotrack,
|
|
50772
|
-
Nightvision: DeviceRole.Nightvision,
|
|
50773
|
-
PrivacyMask: DeviceRole.PrivacyMask
|
|
50774
|
-
};
|
|
50775
|
-
AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
|
|
50776
|
-
function deriveBatteryPresence(input) {
|
|
50777
|
-
const status = input.status;
|
|
50778
|
-
if (!status) return "sleeping";
|
|
50779
|
-
if (status.sleeping !== true) return "awake";
|
|
50780
|
-
const lastContactAt = status.lastContactAt;
|
|
50781
|
-
if (typeof lastContactAt !== "number" || lastContactAt <= 0) return "sleeping";
|
|
50782
|
-
const budget = input.unreachableAfterMs ?? 216e5;
|
|
50783
|
-
return input.nowMs - lastContactAt > budget ? "unreachable" : "sleeping";
|
|
50784
|
-
}
|
|
50785
50787
|
var DECLARED_INTEGRATION_FIXED_KEY = "fixed";
|
|
50786
50788
|
function declarationOwnerNodeId(localNodeId) {
|
|
50787
50789
|
const raw = localNodeId ?? "hub";
|
|
@@ -56221,12 +56223,6 @@ var require_dist_CcV3k_hD = __commonJS({
|
|
|
56221
56223
|
addonId: null,
|
|
56222
56224
|
access: "view"
|
|
56223
56225
|
},
|
|
56224
|
-
"storage.getDefaultLocation": {
|
|
56225
|
-
capName: "storage",
|
|
56226
|
-
capScope: "system",
|
|
56227
|
-
addonId: null,
|
|
56228
|
-
access: "view"
|
|
56229
|
-
},
|
|
56230
56226
|
"storage.list": {
|
|
56231
56227
|
capName: "storage",
|
|
56232
56228
|
capScope: "system",
|
|
@@ -60112,6 +60108,12 @@ var require_dist_CcV3k_hD = __commonJS({
|
|
|
60112
60108
|
return LOG_LEVEL_RANK;
|
|
60113
60109
|
}
|
|
60114
60110
|
});
|
|
60111
|
+
Object.defineProperty(exports, "LegacyStorageLocationDefaultSchema", {
|
|
60112
|
+
enumerable: true,
|
|
60113
|
+
get: function() {
|
|
60114
|
+
return LegacyStorageLocationDefaultSchema;
|
|
60115
|
+
}
|
|
60116
|
+
});
|
|
60115
60117
|
Object.defineProperty(exports, "METHOD_ACCESS_MAP", {
|
|
60116
60118
|
enumerable: true,
|
|
60117
60119
|
get: function() {
|
|
@@ -60442,6 +60444,12 @@ var require_dist_CcV3k_hD = __commonJS({
|
|
|
60442
60444
|
return isDeviceConfigCap;
|
|
60443
60445
|
}
|
|
60444
60446
|
});
|
|
60447
|
+
Object.defineProperty(exports, "isLocationEnabled", {
|
|
60448
|
+
enumerable: true,
|
|
60449
|
+
get: function() {
|
|
60450
|
+
return isLocationEnabled;
|
|
60451
|
+
}
|
|
60452
|
+
});
|
|
60445
60453
|
Object.defineProperty(exports, "isObjectInput", {
|
|
60446
60454
|
enumerable: true,
|
|
60447
60455
|
get: function() {
|
|
@@ -60694,7 +60702,7 @@ var require_alerts_addon = __commonJS({
|
|
|
60694
60702
|
[Symbol.toStringTag]: { value: "Module" }
|
|
60695
60703
|
});
|
|
60696
60704
|
require_chunk_Cek0wNdY();
|
|
60697
|
-
var require_dist10 =
|
|
60705
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
60698
60706
|
function selectExpired(alerts, cutoffMs) {
|
|
60699
60707
|
return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
|
|
60700
60708
|
}
|
|
@@ -61513,7 +61521,7 @@ var require_console_logging = __commonJS({
|
|
|
61513
61521
|
[Symbol.toStringTag]: { value: "Module" }
|
|
61514
61522
|
});
|
|
61515
61523
|
require_chunk_Cek0wNdY();
|
|
61516
|
-
var require_dist10 =
|
|
61524
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
61517
61525
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
61518
61526
|
var LEVEL_RANK = {
|
|
61519
61527
|
debug: 0,
|
|
@@ -61607,7 +61615,7 @@ var require_core_blocks_addon = __commonJS({
|
|
|
61607
61615
|
"use strict";
|
|
61608
61616
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
61609
61617
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
61610
|
-
var require_dist10 =
|
|
61618
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
61611
61619
|
var node_crypto = __require("crypto");
|
|
61612
61620
|
var node_fs_promises = __require("fs/promises");
|
|
61613
61621
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -62504,11 +62512,11 @@ var require_core_blocks = __commonJS({
|
|
|
62504
62512
|
}
|
|
62505
62513
|
});
|
|
62506
62514
|
|
|
62507
|
-
// ../system/dist/retired-settings-keys-
|
|
62508
|
-
var
|
|
62509
|
-
"../system/dist/retired-settings-keys-
|
|
62515
|
+
// ../system/dist/retired-settings-keys-Dxv9S_lO.js
|
|
62516
|
+
var require_retired_settings_keys_Dxv9S_lO = __commonJS({
|
|
62517
|
+
"../system/dist/retired-settings-keys-Dxv9S_lO.js"(exports) {
|
|
62510
62518
|
"use strict";
|
|
62511
|
-
var require_dist10 =
|
|
62519
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
62512
62520
|
function settingsStoreIsAuthoritativeHere(env) {
|
|
62513
62521
|
const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
|
|
62514
62522
|
return raw === "" || raw === "hub";
|
|
@@ -64722,8 +64730,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
64722
64730
|
[Symbol.toStringTag]: { value: "Module" }
|
|
64723
64731
|
});
|
|
64724
64732
|
require_chunk_Cek0wNdY();
|
|
64725
|
-
var require_dist10 =
|
|
64726
|
-
var require_retired_settings_keys =
|
|
64733
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
64734
|
+
var require_retired_settings_keys = require_retired_settings_keys_Dxv9S_lO();
|
|
64727
64735
|
var node_crypto = __require("crypto");
|
|
64728
64736
|
var _camstack_types_node = require_node();
|
|
64729
64737
|
var JOB_HISTORY = 20;
|
|
@@ -70348,7 +70356,7 @@ var require_hub_forwarder = __commonJS({
|
|
|
70348
70356
|
[Symbol.toStringTag]: { value: "Module" }
|
|
70349
70357
|
});
|
|
70350
70358
|
require_chunk_Cek0wNdY();
|
|
70351
|
-
var require_dist10 =
|
|
70359
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
70352
70360
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
70353
70361
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
70354
70362
|
var HubForwarderDestination = class {
|
|
@@ -70485,7 +70493,7 @@ var require_liveness_monitor_addon = __commonJS({
|
|
|
70485
70493
|
"use strict";
|
|
70486
70494
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
70487
70495
|
require_chunk_Cek0wNdY();
|
|
70488
|
-
var require_dist10 =
|
|
70496
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
70489
70497
|
var NO_DEVICES = "liveness:no-devices";
|
|
70490
70498
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
70491
70499
|
var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
|
|
@@ -70681,7 +70689,7 @@ var require_local_auth_addon = __commonJS({
|
|
|
70681
70689
|
[Symbol.toStringTag]: { value: "Module" }
|
|
70682
70690
|
});
|
|
70683
70691
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
70684
|
-
var require_dist10 =
|
|
70692
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
70685
70693
|
var node_crypto = __require("crypto");
|
|
70686
70694
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
70687
70695
|
var crypto$1 = __require("crypto");
|
|
@@ -78494,7 +78502,7 @@ var require_loki_logging = __commonJS({
|
|
|
78494
78502
|
[Symbol.toStringTag]: { value: "Module" }
|
|
78495
78503
|
});
|
|
78496
78504
|
require_chunk_Cek0wNdY();
|
|
78497
|
-
var require_dist10 =
|
|
78505
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
78498
78506
|
function sanitizeLabelName(raw) {
|
|
78499
78507
|
const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
|
|
78500
78508
|
return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
|
|
@@ -79059,7 +79067,7 @@ var require_native_metrics_addon = __commonJS({
|
|
|
79059
79067
|
[Symbol.toStringTag]: { value: "Module" }
|
|
79060
79068
|
});
|
|
79061
79069
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
79062
|
-
var require_dist10 =
|
|
79070
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
79063
79071
|
var node_fs_promises = __require("fs/promises");
|
|
79064
79072
|
var node_child_process = __require("child_process");
|
|
79065
79073
|
var node_util = __require("util");
|
|
@@ -81643,7 +81651,7 @@ var require_filesystem_storage_addon = __commonJS({
|
|
|
81643
81651
|
[Symbol.toStringTag]: { value: "Module" }
|
|
81644
81652
|
});
|
|
81645
81653
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
81646
|
-
var require_dist10 =
|
|
81654
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
81647
81655
|
var node_crypto = __require("crypto");
|
|
81648
81656
|
var node_fs_promises = __require("fs/promises");
|
|
81649
81657
|
var node_path = __require("path");
|
|
@@ -82759,8 +82767,8 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
82759
82767
|
[Symbol.toStringTag]: { value: "Module" }
|
|
82760
82768
|
});
|
|
82761
82769
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
82762
|
-
var require_dist10 =
|
|
82763
|
-
var require_retired_settings_keys =
|
|
82770
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
82771
|
+
var require_retired_settings_keys = require_retired_settings_keys_Dxv9S_lO();
|
|
82764
82772
|
var node_crypto = __require("crypto");
|
|
82765
82773
|
var node_fs = __require("fs");
|
|
82766
82774
|
var node_module = __require("module");
|
|
@@ -85250,7 +85258,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85250
85258
|
[Symbol.toStringTag]: { value: "Module" }
|
|
85251
85259
|
});
|
|
85252
85260
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
85253
|
-
var require_dist10 =
|
|
85261
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
85254
85262
|
var require_hub_cap_forward = require_hub_cap_forward_DmHNjbB0();
|
|
85255
85263
|
var zod = require_zod();
|
|
85256
85264
|
var node_crypto = __require("crypto");
|
|
@@ -85397,7 +85405,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85397
85405
|
display_name: loc.displayName,
|
|
85398
85406
|
provider_id: loc.providerId,
|
|
85399
85407
|
config: storedConfig,
|
|
85400
|
-
is_default:
|
|
85408
|
+
is_default: 0,
|
|
85401
85409
|
is_system: loc.isSystem ? 1 : 0,
|
|
85402
85410
|
created_at: loc.createdAt,
|
|
85403
85411
|
updated_at: loc.updatedAt
|
|
@@ -85426,7 +85434,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85426
85434
|
config,
|
|
85427
85435
|
...nodeId !== void 0 ? { nodeId } : {},
|
|
85428
85436
|
...enabled !== void 0 ? { enabled } : {},
|
|
85429
|
-
isDefault: row["is_default"] === 1 || row["is_default"] === true,
|
|
85437
|
+
...require_dist10.LegacyStorageLocationDefaultSchema.parse({ isDefault: row["is_default"] === 1 || row["is_default"] === true }),
|
|
85430
85438
|
isSystem: row["is_system"] === 1 || row["is_system"] === true,
|
|
85431
85439
|
createdAt: Number(row["created_at"] ?? 0),
|
|
85432
85440
|
updatedAt: Number(row["updated_at"] ?? 0)
|
|
@@ -85475,16 +85483,18 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85475
85483
|
const targetId = input.destinations[storageClass];
|
|
85476
85484
|
if (targetId === void 0) continue;
|
|
85477
85485
|
const namedSourceId = input.sources?.[storageClass];
|
|
85478
|
-
const
|
|
85479
|
-
const
|
|
85480
|
-
if (!
|
|
85486
|
+
const writeSet = this.deps.locations.writeTargets(storageClass);
|
|
85487
|
+
const implicitSources = writeSet.filter((loc) => loc.id !== targetId);
|
|
85488
|
+
if (!namedSourceId && implicitSources.length > 1) throw new Error(`"${storageClass}" writes to ${String(implicitSources.length)} locations (${implicitSources.map((loc) => loc.id).join(", ")}) \u2014 name the one to cut over explicitly with \`sources\`.`);
|
|
85489
|
+
const source = namedSourceId ? this.deps.locations.getLocationById(namedSourceId) : implicitSources[0];
|
|
85490
|
+
if (!source) throw new Error(namedSourceId ? `Storage location "${namedSourceId}" not found` : `Storage location "${targetId}" is already the only write target for "${storageClass}" \u2014 there is nothing to cut over. Use storageMigration.drain to move what is left behind.`);
|
|
85481
85491
|
if (source.type !== storageClass) throw new Error(`Storage location "${source.id}" is type "${source.type}", expected "${storageClass}"`);
|
|
85482
85492
|
const target = this.deps.locations.getLocationById(targetId);
|
|
85483
85493
|
if (!target) throw new Error(`Storage location "${targetId}" not found`);
|
|
85484
85494
|
if (target.type !== storageClass) throw new Error(`Storage location "${targetId}" is type "${target.type}", expected "${storageClass}"`);
|
|
85485
|
-
if (source.id === target.id) throw new Error(namedSourceId ? `Source and destination are the same location ("${targetId}")` : `Storage location "${targetId}" is already the "${storageClass}"
|
|
85486
|
-
const freezeSource =
|
|
85487
|
-
if (!MOVER_CLASSES.includes(storageClass)) throw new Error(`No mover owns "${storageClass}" \u2014 the migration can
|
|
85495
|
+
if (source.id === target.id) throw new Error(namedSourceId ? `Source and destination are the same location ("${targetId}")` : `Storage location "${targetId}" is already the only write target for "${storageClass}"`);
|
|
85496
|
+
const freezeSource = !writeSet.some((loc) => loc.id === source.id);
|
|
85497
|
+
if (!MOVER_CLASSES.includes(storageClass)) throw new Error(`No mover owns "${storageClass}" \u2014 the migration can move its write set but cannot move its bytes. Move it by hand, then enable the destination (and disable the source) with upsertLocation.`);
|
|
85488
85498
|
moves.push({
|
|
85489
85499
|
storageClass,
|
|
85490
85500
|
fromLocationId: source.id,
|
|
@@ -85501,7 +85511,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85501
85511
|
if (moves.some((move) => move.storageClass === "eventMedia")) {
|
|
85502
85512
|
const unstamped = await this.deps.participants.analytics.countUnstamped();
|
|
85503
85513
|
if (unstamped === null) {
|
|
85504
|
-
if (mode === "nonBlocking") throw new Error("could not determine whether any event-media row still carries no locationId. A non-blocking cutover
|
|
85514
|
+
if (mode === "nonBlocking") throw new Error("could not determine whether any event-media row still carries no locationId. A non-blocking cutover moves the write set and would silently orphan every such row, so an unmeasured collection is refused exactly like a non-empty one. Retry, or run the blocking mode, which stamps rows as it moves them.");
|
|
85505
85515
|
findings.push({
|
|
85506
85516
|
code: "unstampedEventMediaRows",
|
|
85507
85517
|
storageClass: "eventMedia",
|
|
@@ -85510,7 +85520,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85510
85520
|
} else if (unstamped.anyPresent) {
|
|
85511
85521
|
const scale = unstamped.total === null ? "an unknown number of" : unstamped.total.toString();
|
|
85512
85522
|
const perLane = `${describeUnstamped(unstamped.media)} media, ${describeUnstamped(unstamped.retrainFrames)} retrain frames`;
|
|
85513
|
-
if (mode === "nonBlocking") throw new Error(`${scale} event-media row(s) still carry no locationId (${perLane}). A non-blocking cutover would silently orphan them. Run the seal first: pipelineAnalytics.relocateMedia({ mode: "seal", toLocationId: "${this.deps.locations.
|
|
85523
|
+
if (mode === "nonBlocking") throw new Error(`${scale} event-media row(s) still carry no locationId (${perLane}). A non-blocking cutover would silently orphan them. Run the seal first: pipelineAnalytics.relocateMedia({ mode: "seal", toLocationId: "${this.deps.locations.writeTargets("eventMedia")[0]?.id ?? "eventMedia"}" }).`);
|
|
85514
85524
|
findings.push({
|
|
85515
85525
|
code: "unstampedEventMediaRows",
|
|
85516
85526
|
storageClass: "eventMedia",
|
|
@@ -85649,7 +85659,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85649
85659
|
* remaining" action.
|
|
85650
85660
|
*
|
|
85651
85661
|
* A class appears here only when something is (or might be) left on a
|
|
85652
|
-
* location
|
|
85662
|
+
* location outside its write set. An empty result therefore means exactly
|
|
85653
85663
|
* "there is nothing to drain", which is what lets the UI offer the action
|
|
85654
85664
|
* only when it is true, and what lets {@link drain} refuse rather than start
|
|
85655
85665
|
* a job that would move nothing and report `done` — the failure mode D295
|
|
@@ -85663,11 +85673,13 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85663
85673
|
const out = [];
|
|
85664
85674
|
for (const storageClass of STORAGE_CLASSES) {
|
|
85665
85675
|
if (!MOVER_CLASSES.includes(storageClass)) continue;
|
|
85666
|
-
const
|
|
85676
|
+
const writeSet = this.deps.locations.writeTargets(storageClass);
|
|
85677
|
+
const target = writeSet[0];
|
|
85667
85678
|
if (!target) continue;
|
|
85679
|
+
const inWriteSet = new Set(writeSet.map((loc) => loc.id));
|
|
85668
85680
|
if (laneOf(storageClass) === "media") {
|
|
85669
85681
|
for (const source of this.deps.locations.listLocations({ type: storageClass })) {
|
|
85670
|
-
if (source.id
|
|
85682
|
+
if (inWriteSet.has(source.id)) continue;
|
|
85671
85683
|
const count = await unanswerable(this.deps.participants.analytics.residue({
|
|
85672
85684
|
toLocationId: target.id,
|
|
85673
85685
|
fromLocationId: source.id,
|
|
@@ -85685,7 +85697,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85685
85697
|
continue;
|
|
85686
85698
|
}
|
|
85687
85699
|
for (const source of this.deps.locations.listLocations({ type: storageClass })) {
|
|
85688
|
-
if (source.id
|
|
85700
|
+
if (inWriteSet.has(source.id)) continue;
|
|
85689
85701
|
const census = await unanswerable(this.deps.participants.recorder.residue({
|
|
85690
85702
|
fromLocationId: source.id,
|
|
85691
85703
|
footageClass: storageClass === "recordingsLow" ? "recordingsLow" : "recordings"
|
|
@@ -85703,12 +85715,12 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85703
85715
|
return out;
|
|
85704
85716
|
}
|
|
85705
85717
|
/**
|
|
85706
|
-
* Run the DRAIN half alone, against classes whose
|
|
85718
|
+
* Run the DRAIN half alone, against classes whose write set has already moved.
|
|
85707
85719
|
*
|
|
85708
85720
|
* ## Why this is a second verb rather than a looser `start`
|
|
85709
85721
|
*
|
|
85710
|
-
* `start` refuses a destination that is already the class's
|
|
85711
|
-
* (`"recordingsLow:ssd" is already the "recordingsLow"
|
|
85722
|
+
* `start` refuses a destination that is already the class's only write target
|
|
85723
|
+
* (`"recordingsLow:ssd" is already the only write target for "recordingsLow"`). That refusal
|
|
85712
85724
|
* is correct and it is load-bearing: there is genuinely nothing left to
|
|
85713
85725
|
* repoint, and an operator must never be able to re-repoint a migrated class
|
|
85714
85726
|
* by accident. Making `start` idempotent — "an already-repointed class
|
|
@@ -85717,7 +85729,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85717
85729
|
* operator reads ("pauses the writers…") would be a lie half the time.
|
|
85718
85730
|
*
|
|
85719
85731
|
* `drain` instead cannot repoint AT ALL: it never touches
|
|
85720
|
-
* `
|
|
85732
|
+
* `setWriteTargets`, and its job starts in `draining` with `repointed`
|
|
85721
85733
|
* already true, so the `repointing` / `refreshing` / `resuming` blocks of
|
|
85722
85734
|
* {@link run} are behind it and unreachable. The two verbs are disjoint, and
|
|
85723
85735
|
* `start`'s refusal keeps meaning exactly what it meant.
|
|
@@ -85727,7 +85739,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85727
85739
|
* The finished job is the audit of what happened; re-opening it destroys
|
|
85728
85740
|
* that. And a drain is needed in cases where no migration job ever existed
|
|
85729
85741
|
* (a mover armed by hand, footage stranded on a location an operator added
|
|
85730
|
-
* and then
|
|
85742
|
+
* and then disabled again). One job = one operation, and the work list comes
|
|
85731
85743
|
* from {@link residue} — the archive — not from what a previous job believed.
|
|
85732
85744
|
*/
|
|
85733
85745
|
async drain(input) {
|
|
@@ -85750,9 +85762,9 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85750
85762
|
const destinations = {};
|
|
85751
85763
|
for (const storageClass of input.classes) {
|
|
85752
85764
|
const remaining = residue.filter((entry) => entry.storageClass === storageClass);
|
|
85753
|
-
if (remaining.length === 0) throw new Error(`"${storageClass}" has nothing left outside its
|
|
85765
|
+
if (remaining.length === 0) throw new Error(`"${storageClass}" has nothing left outside its write set \u2014 there is nothing to drain. To CHANGE where it writes, use storageMigration.start with a new destination.`);
|
|
85754
85766
|
for (const entry of remaining) {
|
|
85755
|
-
if (this.deps.locations.getLocationById(entry.toLocationId)?.enabled === false) throw new Error(`The "${storageClass}" drain destination "${entry.toLocationId}" is DISABLED \u2014 a disabled location takes no writes, and a drain is the largest write there is. Enable it on the Storage screen
|
|
85767
|
+
if (this.deps.locations.getLocationById(entry.toLocationId)?.enabled === false) throw new Error(`The "${storageClass}" drain destination "${entry.toLocationId}" is DISABLED \u2014 a disabled location takes no writes, and a drain is the largest write there is. Enable it on the Storage screen first.`);
|
|
85756
85768
|
moves.push({
|
|
85757
85769
|
storageClass,
|
|
85758
85770
|
fromLocationId: entry.fromLocationId,
|
|
@@ -85870,8 +85882,13 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85870
85882
|
await this.setPhase(job, "repointing");
|
|
85871
85883
|
}
|
|
85872
85884
|
if (job.phase === "repointing") {
|
|
85873
|
-
const targets =
|
|
85874
|
-
|
|
85885
|
+
const targets = /* @__PURE__ */ new Map();
|
|
85886
|
+
for (const move of job.moves) {
|
|
85887
|
+
if (move.freezeSource === true) continue;
|
|
85888
|
+
targets.set(move.toLocationId, true);
|
|
85889
|
+
targets.set(move.fromLocationId, false);
|
|
85890
|
+
}
|
|
85891
|
+
if (targets.size > 0) await this.deps.locations.setWriteTargets(targets);
|
|
85875
85892
|
job.repointed = true;
|
|
85876
85893
|
await this.setPhase(job, "refreshing");
|
|
85877
85894
|
}
|
|
@@ -86071,9 +86088,9 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86071
86088
|
for (const move of job.moves) if (move.state !== "done") throw new Error(`${move.storageClass} move did not complete verification`);
|
|
86072
86089
|
}
|
|
86073
86090
|
/**
|
|
86074
|
-
* After a from→to copy whose source was NOT
|
|
86075
|
-
*
|
|
86076
|
-
* naming a
|
|
86091
|
+
* After a from→to copy whose source was NOT a write target: stop new writes
|
|
86092
|
+
* to `from`. The rest of the write set is unchanged (that is the whole point
|
|
86093
|
+
* of naming a source outside it). A location that is already disabled is
|
|
86077
86094
|
* left alone.
|
|
86078
86095
|
*/
|
|
86079
86096
|
freezeDrainedSources(job) {
|
|
@@ -86671,33 +86688,77 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86671
86688
|
backfilled: backfill.length,
|
|
86672
86689
|
total: this.locations.size
|
|
86673
86690
|
} });
|
|
86674
|
-
this.
|
|
86691
|
+
this.migrateOntoTheWriteSet(rows);
|
|
86675
86692
|
}
|
|
86676
86693
|
/**
|
|
86677
|
-
*
|
|
86694
|
+
* ONE explicit migration onto the enabled write set (D383), run once per
|
|
86695
|
+
* boot against the rows that came off disk. Idempotent: the second boot finds
|
|
86696
|
+
* nothing to do.
|
|
86678
86697
|
*
|
|
86679
|
-
*
|
|
86680
|
-
*
|
|
86681
|
-
* hand-edited store or a row written by another build can too. It matters
|
|
86682
|
-
* because every bare-type ref still resolves through {@link getDefaultLocation}
|
|
86683
|
-
* until Stage 5 of the enabled-write-set design: on such a row
|
|
86684
|
-
* `storage.write({ location: 'eventMedia' })` and the migration coordinator's
|
|
86685
|
-
* drain destination both point at a disk the operator turned off.
|
|
86698
|
+
* Two steps, in order, and each one is a WRITE — the point is that "absent"
|
|
86699
|
+
* stops existing rather than being re-interpreted on every read forever:
|
|
86686
86700
|
*
|
|
86687
|
-
*
|
|
86688
|
-
*
|
|
86689
|
-
*
|
|
86690
|
-
*
|
|
86691
|
-
*
|
|
86692
|
-
|
|
86693
|
-
|
|
86701
|
+
* 1. A flagless row (persisted before `enabled` existed) is stamped
|
|
86702
|
+
* `enabled: true`. Absence already meant active, so this preserves
|
|
86703
|
+
* behaviour exactly; it is the operator's "the initial locations are
|
|
86704
|
+
* enabled" spelled into the store.
|
|
86705
|
+
* 2. A type left with NO enabled row — only reachable from a hand-edited
|
|
86706
|
+
* store or a row an older build wrote — gets one enabled back, chosen as
|
|
86707
|
+
* the row the RETIRED default flag named, else the `:default`-slugged
|
|
86708
|
+
* row, else the lexicographically first. Loudly: a class with no write
|
|
86709
|
+
* target records nowhere, and refusing to repair it here would leave the
|
|
86710
|
+
* hub silently unable to write with no operator action pending.
|
|
86711
|
+
*
|
|
86712
|
+
* An operator's explicit `enabled: false` is never reversed by step 1 — only
|
|
86713
|
+
* step 2 touches it, and only when the alternative is a class that cannot
|
|
86714
|
+
* write at all.
|
|
86715
|
+
*/
|
|
86716
|
+
migrateOntoTheWriteSet(rows) {
|
|
86717
|
+
const legacyDefaultOfType = /* @__PURE__ */ new Map();
|
|
86718
|
+
for (const row of rows) if (row.isDefault === true) legacyDefaultOfType.set(row.type, row.id);
|
|
86719
|
+
const stamped = [];
|
|
86694
86720
|
for (const loc of this.locations.values()) {
|
|
86695
|
-
if (
|
|
86696
|
-
this.
|
|
86697
|
-
|
|
86698
|
-
|
|
86699
|
-
}
|
|
86721
|
+
if (loc.enabled !== void 0) continue;
|
|
86722
|
+
this.persistMigrated({
|
|
86723
|
+
...loc,
|
|
86724
|
+
enabled: true
|
|
86725
|
+
});
|
|
86726
|
+
stamped.push(loc.id);
|
|
86700
86727
|
}
|
|
86728
|
+
const repaired = [];
|
|
86729
|
+
for (const type of new Set([...this.locations.values()].map((l) => l.type))) {
|
|
86730
|
+
const ofType = [...this.locations.values()].filter((l) => l.type === type);
|
|
86731
|
+
if (ofType.some((l) => require_dist10.isLocationEnabled(l))) continue;
|
|
86732
|
+
const legacyDefault = legacyDefaultOfType.get(type);
|
|
86733
|
+
const pick = ofType.find((l) => l.id === legacyDefault) ?? ofType.find((l) => l.id === `${type}:default`) ?? ofType.toSorted((a, b) => a.id.localeCompare(b.id))[0];
|
|
86734
|
+
if (!pick) continue;
|
|
86735
|
+
this.persistMigrated({
|
|
86736
|
+
...pick,
|
|
86737
|
+
enabled: true
|
|
86738
|
+
});
|
|
86739
|
+
repaired.push(pick.id);
|
|
86740
|
+
}
|
|
86741
|
+
if (stamped.length > 0 || repaired.length > 0) this.logger.info("storage-orchestrator: migrated locations onto the enabled write set", { meta: {
|
|
86742
|
+
stamped,
|
|
86743
|
+
repaired
|
|
86744
|
+
} });
|
|
86745
|
+
for (const id of repaired) this.logger.warn("storage-orchestrator: a storage type had NO enabled location \u2014 re-enabled one so it can write. Every other location of that type stays read-only until you enable it.", { meta: { id } });
|
|
86746
|
+
}
|
|
86747
|
+
/** Apply one migrated row in memory and mirror it to the store. */
|
|
86748
|
+
persistMigrated(next) {
|
|
86749
|
+
const withStamp = {
|
|
86750
|
+
...next,
|
|
86751
|
+
updatedAt: Date.now()
|
|
86752
|
+
};
|
|
86753
|
+
this.locations.set(withStamp.id, withStamp);
|
|
86754
|
+
const store = this.locationStore;
|
|
86755
|
+
if (!store) return;
|
|
86756
|
+
store.upsert(withStamp).catch((err) => {
|
|
86757
|
+
this.logger.error("storage-orchestrator: write-set migration persist failed", { meta: {
|
|
86758
|
+
id: withStamp.id,
|
|
86759
|
+
error: err instanceof Error ? err.message : String(err)
|
|
86760
|
+
} });
|
|
86761
|
+
});
|
|
86701
86762
|
}
|
|
86702
86763
|
/**
|
|
86703
86764
|
* Inject the declaration-driven cardinality source. Called once by the
|
|
@@ -86752,7 +86813,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86752
86813
|
const rows = await this.locationStore.loadAll();
|
|
86753
86814
|
for (const loc of rows) this.locations.set(loc.id, loc);
|
|
86754
86815
|
this.logger.info("storage-orchestrator: hydrated locations from store", { meta: { loaded: this.locations.size } });
|
|
86755
|
-
this.
|
|
86816
|
+
this.migrateOntoTheWriteSet(rows);
|
|
86756
86817
|
}
|
|
86757
86818
|
/**
|
|
86758
86819
|
* Boot backfill (SP1): stamp `nodeId` on every persisted node-local
|
|
@@ -86801,40 +86862,51 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86801
86862
|
const all = [...this.locations.values()];
|
|
86802
86863
|
return filter?.type ? all.filter((l) => l.type === filter.type) : all;
|
|
86803
86864
|
}
|
|
86804
|
-
getDefaultLocation(type) {
|
|
86805
|
-
for (const loc of this.locations.values()) if (loc.type === type && loc.isDefault) return loc;
|
|
86806
|
-
return null;
|
|
86807
|
-
}
|
|
86808
86865
|
/**
|
|
86809
|
-
*
|
|
86810
|
-
*
|
|
86811
|
-
* a
|
|
86812
|
-
* the
|
|
86866
|
+
* The WRITE SET of a type: every enabled location of it, in stable id order.
|
|
86867
|
+
* This replaced `getDefaultLocation` (D383) — a type does not have one write
|
|
86868
|
+
* target any more, it has a pool, and every consumer that must pick one picks
|
|
86869
|
+
* from here by its own rule (the recorder by headroom, a drain by an operator
|
|
86870
|
+
* -named destination).
|
|
86813
86871
|
*/
|
|
86814
|
-
|
|
86815
|
-
|
|
86872
|
+
writeTargets(type) {
|
|
86873
|
+
return [...this.locations.values()].filter((loc) => loc.type === type && require_dist10.isLocationEnabled(loc)).toSorted((a, b) => a.id.localeCompare(b.id));
|
|
86874
|
+
}
|
|
86875
|
+
/**
|
|
86876
|
+
* Change the WRITE SET as one compensated commit: each named location is
|
|
86877
|
+
* enabled (it joins the pool) or disabled (it becomes read-only — still read,
|
|
86878
|
+
* still played back, still age-swept, still drained).
|
|
86879
|
+
*
|
|
86880
|
+
* This is the cutover verb, and its only caller is the migration coordinator,
|
|
86881
|
+
* which persists its `repointing` phase BEFORE calling so a crash mid-write
|
|
86882
|
+
* is recovered by replaying the same idempotent set before writers resume.
|
|
86883
|
+
*
|
|
86884
|
+
* It moves ONLY the locations it is given: a cutover from A to C on a class
|
|
86885
|
+
* that also writes to B leaves B in the pool, because the operator's pool is
|
|
86886
|
+
* the authority and a migration is not a licence to shrink it. The commit is
|
|
86887
|
+
* refused when it would leave a write-serving type with nothing enabled —
|
|
86888
|
+
* the same refusal `upsertLocation` applies, judged on the RESULT so an
|
|
86889
|
+
* enable and a disable in one commit are weighed together.
|
|
86890
|
+
*/
|
|
86891
|
+
async setWriteTargets(enabledByLocationId) {
|
|
86892
|
+
if (enabledByLocationId.size === 0) return;
|
|
86816
86893
|
const now = Date.now();
|
|
86817
86894
|
const nextById = new Map(this.locations);
|
|
86818
86895
|
const touched = /* @__PURE__ */ new Map();
|
|
86819
|
-
for (const [
|
|
86820
|
-
const
|
|
86821
|
-
if (!
|
|
86822
|
-
if (
|
|
86823
|
-
|
|
86824
|
-
|
|
86825
|
-
|
|
86826
|
-
|
|
86827
|
-
|
|
86828
|
-
|
|
86829
|
-
|
|
86830
|
-
isDefault,
|
|
86831
|
-
...enable ? { enabled: true } : {},
|
|
86832
|
-
updatedAt: now
|
|
86833
|
-
};
|
|
86834
|
-
nextById.set(id, updated);
|
|
86835
|
-
touched.set(id, updated);
|
|
86836
|
-
}
|
|
86896
|
+
for (const [id, enabled] of enabledByLocationId) {
|
|
86897
|
+
const location = nextById.get(id);
|
|
86898
|
+
if (!location) throw new Error(`Storage location "${id}" not found`);
|
|
86899
|
+
if (location.enabled === enabled) continue;
|
|
86900
|
+
const updated = {
|
|
86901
|
+
...location,
|
|
86902
|
+
enabled,
|
|
86903
|
+
updatedAt: now
|
|
86904
|
+
};
|
|
86905
|
+
nextById.set(id, updated);
|
|
86906
|
+
touched.set(id, updated);
|
|
86837
86907
|
}
|
|
86908
|
+
if (touched.size === 0) return;
|
|
86909
|
+
refuseTypeWithoutWriteTarget(touched.values(), nextById.values());
|
|
86838
86910
|
if (this.locationStore) {
|
|
86839
86911
|
const previous = /* @__PURE__ */ new Map();
|
|
86840
86912
|
for (const id of touched.keys()) {
|
|
@@ -86855,13 +86927,10 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86855
86927
|
for (const [id, location] of nextById) this.locations.set(id, location);
|
|
86856
86928
|
}
|
|
86857
86929
|
/**
|
|
86858
|
-
* Insert or update a location.
|
|
86859
|
-
* demotes any other default for the same type — operators always see
|
|
86860
|
-
* exactly one default per type.
|
|
86930
|
+
* Insert or update a location.
|
|
86861
86931
|
*
|
|
86862
|
-
* When a persistence store is wired (Task 6), the
|
|
86863
|
-
*
|
|
86864
|
-
* mutation returns. Persistence errors propagate to the caller.
|
|
86932
|
+
* When a persistence store is wired (Task 6), the record is mirrored to it;
|
|
86933
|
+
* the mirror is fire-and-forget and its errors reach the logger.
|
|
86865
86934
|
*/
|
|
86866
86935
|
/**
|
|
86867
86936
|
* Refuse a `(location kind, provider)` pair the kind cannot use.
|
|
@@ -86947,30 +87016,17 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86947
87016
|
updatedAt: now
|
|
86948
87017
|
};
|
|
86949
87018
|
refuseLastWriteTargetDisabled(existing, next, this.locations.values());
|
|
86950
|
-
const demoted = [];
|
|
86951
|
-
if (next.isDefault) {
|
|
86952
|
-
for (const [otherId, otherLoc] of this.locations) if (otherLoc.type === next.type && otherLoc.id !== next.id && otherLoc.isDefault) {
|
|
86953
|
-
const updated = {
|
|
86954
|
-
...otherLoc,
|
|
86955
|
-
isDefault: false,
|
|
86956
|
-
updatedAt: now
|
|
86957
|
-
};
|
|
86958
|
-
this.locations.set(otherId, updated);
|
|
86959
|
-
demoted.push(updated);
|
|
86960
|
-
}
|
|
86961
|
-
}
|
|
86962
87019
|
this.locations.set(next.id, next);
|
|
86963
87020
|
this.logger.debug("storage-orchestrator: upsertLocation", { meta: {
|
|
86964
87021
|
id: next.id,
|
|
86965
87022
|
type: next.type,
|
|
86966
87023
|
providerId: next.providerId,
|
|
86967
|
-
|
|
87024
|
+
enabled: next.enabled
|
|
86968
87025
|
} });
|
|
86969
87026
|
if (this.locationStore) {
|
|
86970
87027
|
const store = this.locationStore;
|
|
86971
87028
|
(async () => {
|
|
86972
87029
|
try {
|
|
86973
|
-
for (const d of demoted) await store.upsert(d);
|
|
86974
87030
|
await store.upsert(next);
|
|
86975
87031
|
} catch (err) {
|
|
86976
87032
|
this.logger.error("storage-orchestrator: upsert persistence failed", { meta: {
|
|
@@ -86985,8 +87041,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86985
87041
|
/**
|
|
86986
87042
|
* Remove a location. Refuses the last location of a type and the last
|
|
86987
87043
|
* enabled location of a type. `isSystem` is informational — a frozen
|
|
86988
|
-
* recordings `:default` is deletable once a sibling is live.
|
|
86989
|
-
* the flagged default promotes an enabled sibling. Occupancy still
|
|
87044
|
+
* recordings `:default` is deletable once a sibling is live. Occupancy still
|
|
86990
87045
|
* refuses unless `force` is set; force never overrides uniqueness /
|
|
86991
87046
|
* last-enabled.
|
|
86992
87047
|
*/
|
|
@@ -86997,8 +87052,6 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86997
87052
|
if (ofType.length <= 1) throw new Error(`Cannot delete "${id}" \u2014 it is the only location for type "${loc.type}"`);
|
|
86998
87053
|
const enabledOfType = ofType.filter((l) => l.enabled !== false);
|
|
86999
87054
|
if (loc.enabled !== false && enabledOfType.length <= 1) throw new Error(`Cannot delete "${id}" \u2014 it is the only enabled location for type "${loc.type}"`);
|
|
87000
|
-
const successor = loc.isDefault === true ? ofType.find((l) => l.id !== id && l.enabled !== false) : void 0;
|
|
87001
|
-
if (loc.isDefault === true && successor === void 0) throw new Error(`Cannot delete "${id}" \u2014 it is the only enabled location for type "${loc.type}"`);
|
|
87002
87055
|
const occupancy = await this.probeOccupancy(loc);
|
|
87003
87056
|
if (occupancy !== "empty") {
|
|
87004
87057
|
if (options?.force !== true) throw new Error(occupancy === "occupied" ? `Storage location "${id}" still holds data \u2014 drain it first (storage migration / relocate), or pass force to delete the record anyway and strand what is on it` : `Storage location "${id}" could not be checked for remaining data (unreachable provider, another node, or an unmounted root) \u2014 pass force to delete the record anyway`);
|
|
@@ -87008,21 +87061,6 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
87008
87061
|
occupancy
|
|
87009
87062
|
} });
|
|
87010
87063
|
}
|
|
87011
|
-
if (successor !== void 0 && successor.isDefault !== true) {
|
|
87012
|
-
const now = Date.now();
|
|
87013
|
-
const promoted = {
|
|
87014
|
-
...successor,
|
|
87015
|
-
isDefault: true,
|
|
87016
|
-
updatedAt: now
|
|
87017
|
-
};
|
|
87018
|
-
this.locations.set(successor.id, promoted);
|
|
87019
|
-
if (this.locationStore) this.locationStore.upsert(promoted).catch((err) => {
|
|
87020
|
-
this.logger.error("storage-orchestrator: default promotion persist failed", { meta: {
|
|
87021
|
-
id: successor.id,
|
|
87022
|
-
error: err instanceof Error ? err.message : String(err)
|
|
87023
|
-
} });
|
|
87024
|
-
});
|
|
87025
|
-
}
|
|
87026
87064
|
this.locations.delete(id);
|
|
87027
87065
|
if (this.locationStore) this.locationStore.delete(id).catch((err) => {
|
|
87028
87066
|
this.logger.error("storage-orchestrator: delete persistence failed", { meta: {
|
|
@@ -87101,9 +87139,17 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
87101
87139
|
if (!loc) throw new Error(`Storage location "${ref}" not found`);
|
|
87102
87140
|
return loc;
|
|
87103
87141
|
}
|
|
87104
|
-
const
|
|
87105
|
-
if (
|
|
87106
|
-
|
|
87142
|
+
const ofType = [...this.locations.values()].filter((loc) => loc.type === ref);
|
|
87143
|
+
if (ofType.length === 1) return ofType[0];
|
|
87144
|
+
if (ofType.length === 0) throw new Error(`No storage location configured for type "${ref}" \u2014 register one or pass a fully-qualified id (\`<type>:<slug>\`)`);
|
|
87145
|
+
const slugged = ofType.find((loc) => loc.id === `${ref}:default`);
|
|
87146
|
+
if (!slugged) throw new Error(`Storage type "${ref}" has ${String(ofType.length)} locations (${ofType.map((loc) => loc.id).toSorted().join(", ")}) \u2014 a bare type ref is ambiguous, pass a fully-qualified id (\`<type>:<slug>\`)`);
|
|
87147
|
+
this.logger.warn("storage-orchestrator: DEPRECATED bare-type ref on a type with several locations \u2014 resolved to the legacy `:default`-slugged row. Pass a fully-qualified location id.", { meta: {
|
|
87148
|
+
ref,
|
|
87149
|
+
resolved: slugged.id,
|
|
87150
|
+
candidates: ofType.length
|
|
87151
|
+
} });
|
|
87152
|
+
return slugged;
|
|
87107
87153
|
}
|
|
87108
87154
|
/**
|
|
87109
87155
|
* Find the storage-provider that backs a given location — the entry point
|
|
@@ -87207,9 +87253,11 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
87207
87253
|
* declarations on a populated map is a no-op (each declared `id`
|
|
87208
87254
|
* already resolves to an existing `<id>:default`).
|
|
87209
87255
|
*
|
|
87210
|
-
*
|
|
87211
|
-
* `
|
|
87212
|
-
*
|
|
87256
|
+
* The seeded row is born ENABLED — a fresh install must be able to write the
|
|
87257
|
+
* moment it boots, and `enabled` is the only write switch there is (D383).
|
|
87258
|
+
* The `:default` suffix is a NAME kept for idempotency across existing
|
|
87259
|
+
* installs, not a flag: the stored `id` is `'recordings:default'` so it
|
|
87260
|
+
* satisfies the
|
|
87213
87261
|
* `^[a-z][a-z0-9-]*:[a-z0-9-]+$` regex on `StorageLocationSchema.id`.
|
|
87214
87262
|
*
|
|
87215
87263
|
* `basePath` is the storage root (e.g. `<CAMSTACK_DATA>`). Each
|
|
@@ -87234,7 +87282,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
87234
87282
|
displayName: d.displayName,
|
|
87235
87283
|
providerId: input.providerId,
|
|
87236
87284
|
config: { basePath: base },
|
|
87237
|
-
|
|
87285
|
+
enabled: true,
|
|
87238
87286
|
isSystem: false
|
|
87239
87287
|
});
|
|
87240
87288
|
added++;
|
|
@@ -87254,7 +87302,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
87254
87302
|
const requested = input.enabled ?? existing?.enabled;
|
|
87255
87303
|
if (requested === void 0) {
|
|
87256
87304
|
if (existing) return {};
|
|
87257
|
-
return { enabled:
|
|
87305
|
+
return { enabled: !typeAlreadyHasLocation };
|
|
87258
87306
|
}
|
|
87259
87307
|
return { enabled: requested };
|
|
87260
87308
|
}
|
|
@@ -87270,6 +87318,16 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
87270
87318
|
}
|
|
87271
87319
|
throw new Error(`Cannot disable "${next.id}": it is the last enabled location of type "${next.type}". Enable another location of this type first.`);
|
|
87272
87320
|
}
|
|
87321
|
+
function refuseTypeWithoutWriteTarget(touched, result) {
|
|
87322
|
+
const rows = [...result];
|
|
87323
|
+
for (const type of new Set([...touched].map((loc) => loc.type))) {
|
|
87324
|
+
if (OPTIONAL_WRITE_TYPES.has(type)) continue;
|
|
87325
|
+
const ofType = rows.filter((loc) => loc.type === type);
|
|
87326
|
+
if (ofType.length === 0) continue;
|
|
87327
|
+
if (ofType.some((loc) => require_dist10.isLocationEnabled(loc))) continue;
|
|
87328
|
+
throw new Error(`Refusing the write-set change: it would leave type "${type}" with no enabled location. Enable one of ${ofType.map((loc) => loc.id).toSorted().join(", ")} first.`);
|
|
87329
|
+
}
|
|
87330
|
+
}
|
|
87273
87331
|
var DEFAULT_MAX_ROUNDS = 8;
|
|
87274
87332
|
var DEFAULT_HYSTERESIS_PERCENT = 1;
|
|
87275
87333
|
var StoragePressureManager = class {
|
|
@@ -87474,13 +87532,9 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
87474
87532
|
const rows = type !== void 0 ? service.listLocations({ type }) : service.listLocations();
|
|
87475
87533
|
return Promise.all(rows.map(async (loc) => ({
|
|
87476
87534
|
...this.redacted(loc),
|
|
87477
|
-
capacity: loc
|
|
87535
|
+
capacity: require_dist10.isLocationEnabled(loc) ? await this.localCapacityOf(loc) : null
|
|
87478
87536
|
})));
|
|
87479
87537
|
},
|
|
87480
|
-
getDefaultLocation: async ({ type }) => {
|
|
87481
|
-
const loc = service.getDefaultLocation(type);
|
|
87482
|
-
return loc === null ? null : this.redacted(loc);
|
|
87483
|
-
},
|
|
87484
87538
|
listLocationDeclarations: async () => service.listDeclarations(),
|
|
87485
87539
|
upsertLocation: async (input) => {
|
|
87486
87540
|
const stored = service.getLocationById(input.id);
|
|
@@ -88201,7 +88255,7 @@ var require_system_config_addon = __commonJS({
|
|
|
88201
88255
|
[Symbol.toStringTag]: { value: "Module" }
|
|
88202
88256
|
});
|
|
88203
88257
|
require_chunk_Cek0wNdY();
|
|
88204
|
-
var require_dist10 =
|
|
88258
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
88205
88259
|
var SECTION_TITLES = {
|
|
88206
88260
|
server: "Server",
|
|
88207
88261
|
auth: "Authentication"
|
|
@@ -106262,7 +106316,7 @@ var require_winston_logging = __commonJS({
|
|
|
106262
106316
|
[Symbol.toStringTag]: { value: "Module" }
|
|
106263
106317
|
});
|
|
106264
106318
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
106265
|
-
var require_dist10 =
|
|
106319
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
106266
106320
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
106267
106321
|
var node_path = __require("path");
|
|
106268
106322
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -112094,47 +112148,17 @@ var require_addon = __commonJS({
|
|
|
112094
112148
|
},
|
|
112095
112149
|
"setDeviceConfig": { "config": { "__nested__": { "bands": [] } } }
|
|
112096
112150
|
},
|
|
112097
|
-
"storage": { "upsertLocation": {
|
|
112098
|
-
"isDefault": false,
|
|
112099
|
-
"isSystem": false
|
|
112100
|
-
} },
|
|
112151
|
+
"storage": { "upsertLocation": { "isSystem": false } },
|
|
112101
112152
|
"storage-provider": {
|
|
112102
|
-
"beginDownload": { "location": { "__nested__": {
|
|
112103
|
-
|
|
112104
|
-
|
|
112105
|
-
} } },
|
|
112106
|
-
"
|
|
112107
|
-
|
|
112108
|
-
|
|
112109
|
-
} } },
|
|
112110
|
-
"
|
|
112111
|
-
"isDefault": false,
|
|
112112
|
-
"isSystem": false
|
|
112113
|
-
} } },
|
|
112114
|
-
"exists": { "location": { "__nested__": {
|
|
112115
|
-
"isDefault": false,
|
|
112116
|
-
"isSystem": false
|
|
112117
|
-
} } },
|
|
112118
|
-
"getAvailableSpace": { "location": { "__nested__": {
|
|
112119
|
-
"isDefault": false,
|
|
112120
|
-
"isSystem": false
|
|
112121
|
-
} } },
|
|
112122
|
-
"list": { "location": { "__nested__": {
|
|
112123
|
-
"isDefault": false,
|
|
112124
|
-
"isSystem": false
|
|
112125
|
-
} } },
|
|
112126
|
-
"read": { "location": { "__nested__": {
|
|
112127
|
-
"isDefault": false,
|
|
112128
|
-
"isSystem": false
|
|
112129
|
-
} } },
|
|
112130
|
-
"resolve": { "location": { "__nested__": {
|
|
112131
|
-
"isDefault": false,
|
|
112132
|
-
"isSystem": false
|
|
112133
|
-
} } },
|
|
112134
|
-
"write": { "location": { "__nested__": {
|
|
112135
|
-
"isDefault": false,
|
|
112136
|
-
"isSystem": false
|
|
112137
|
-
} } }
|
|
112153
|
+
"beginDownload": { "location": { "__nested__": { "isSystem": false } } },
|
|
112154
|
+
"beginUpload": { "location": { "__nested__": { "isSystem": false } } },
|
|
112155
|
+
"delete": { "location": { "__nested__": { "isSystem": false } } },
|
|
112156
|
+
"exists": { "location": { "__nested__": { "isSystem": false } } },
|
|
112157
|
+
"getAvailableSpace": { "location": { "__nested__": { "isSystem": false } } },
|
|
112158
|
+
"list": { "location": { "__nested__": { "isSystem": false } } },
|
|
112159
|
+
"read": { "location": { "__nested__": { "isSystem": false } } },
|
|
112160
|
+
"resolve": { "location": { "__nested__": { "isSystem": false } } },
|
|
112161
|
+
"write": { "location": { "__nested__": { "isSystem": false } } }
|
|
112138
112162
|
},
|
|
112139
112163
|
"stream-broker": {
|
|
112140
112164
|
"produceEventMedia": {
|
|
@@ -130900,7 +130924,7 @@ var require_dist3 = __commonJS({
|
|
|
130900
130924
|
"use strict";
|
|
130901
130925
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
130902
130926
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
130903
|
-
var require_dist10 =
|
|
130927
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
130904
130928
|
var require_builtins_alerts_alerts_addon = require_alerts_addon();
|
|
130905
130929
|
require_alerts();
|
|
130906
130930
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
@@ -212907,253 +212931,6 @@ var require_dist4 = __commonJS({
|
|
|
212907
212931
|
function logLevelAtMost(level, threshold) {
|
|
212908
212932
|
return LOG_LEVEL_RANK[level] <= LOG_LEVEL_RANK[threshold];
|
|
212909
212933
|
}
|
|
212910
|
-
var LogChannelLevelSchema = zod.z.enum([
|
|
212911
|
-
"info",
|
|
212912
|
-
"warn",
|
|
212913
|
-
"error"
|
|
212914
|
-
]);
|
|
212915
|
-
var LogChannelDescriptorSchema = zod.z.object({
|
|
212916
|
-
/**
|
|
212917
|
-
* Dotted `area.thing`, unique across the workspace. `area` is conventionally
|
|
212918
|
-
* the addon's short name so an operator reading a channel list can tell who
|
|
212919
|
-
* owns it without a second lookup.
|
|
212920
|
-
*/
|
|
212921
|
-
name: zod.z.string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
|
|
212922
|
-
/** One sentence: what the operator will SEE after arming it. */
|
|
212923
|
-
description: zod.z.string().min(1),
|
|
212924
|
-
/** The level its lines are emitted at. Never below `info`. */
|
|
212925
|
-
defaultLevel: LogChannelLevelSchema,
|
|
212926
|
-
/**
|
|
212927
|
-
* Whether this channel can be narrowed to a camera.
|
|
212928
|
-
*
|
|
212929
|
-
* `true` is a PROMISE with two halves, and both must hold: the gate is
|
|
212930
|
-
* consulted with the numeric device id, AND every line the channel admits
|
|
212931
|
-
* carries `tags: { deviceId }` with that same numeric id. The second half is
|
|
212932
|
-
* what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
|
|
212933
|
-
* keeps `deviceId` out of the stream labels for cardinality, so the tag in
|
|
212934
|
-
* the body is the only way to filter.
|
|
212935
|
-
*
|
|
212936
|
-
* A channel whose lines carry the device only in `meta` (or not at all) is
|
|
212937
|
-
* declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
|
|
212938
|
-
* the operator narrows to one camera, sees nothing, and concludes the code
|
|
212939
|
-
* path was never taken.
|
|
212940
|
-
*/
|
|
212941
|
-
perDevice: zod.z.boolean()
|
|
212942
|
-
});
|
|
212943
|
-
var LogChannelWindowSchema = zod.z.object({
|
|
212944
|
-
channel: zod.z.string().min(1),
|
|
212945
|
-
/** Epoch ms the window closes at. */
|
|
212946
|
-
armedUntilMs: zod.z.number(),
|
|
212947
|
-
/** `null` = every camera. A non-empty list narrows to those numeric ids. */
|
|
212948
|
-
deviceIds: zod.z.array(zod.z.number().int()).readonly().nullable()
|
|
212949
|
-
});
|
|
212950
|
-
var LogChannelGate = class {
|
|
212951
|
-
descriptor;
|
|
212952
|
-
/**
|
|
212953
|
-
* HOT PATH GUARD. A plain data FIELD, and it must stay one.
|
|
212954
|
-
*
|
|
212955
|
-
* `log-channel.spec.ts` asserts the property descriptor has no getter and
|
|
212956
|
-
* booby-traps the device set, so turning this into an accessor — or reading
|
|
212957
|
-
* anything before it — fails the spec instead of taxing every line the
|
|
212958
|
-
* process emits.
|
|
212959
|
-
*/
|
|
212960
|
-
on = false;
|
|
212961
|
-
/** `null` while armed for every camera. Never read while `on` is false. */
|
|
212962
|
-
devices = null;
|
|
212963
|
-
level;
|
|
212964
|
-
closesAtMs = 0;
|
|
212965
|
-
constructor(descriptor) {
|
|
212966
|
-
this.descriptor = descriptor;
|
|
212967
|
-
this.level = descriptor.defaultLevel;
|
|
212968
|
-
}
|
|
212969
|
-
/** Epoch ms this channel disarms itself at. 0 when disarmed. */
|
|
212970
|
-
get armedUntilMs() {
|
|
212971
|
-
return this.on ? this.closesAtMs : 0;
|
|
212972
|
-
}
|
|
212973
|
-
/**
|
|
212974
|
-
* Does this channel want a line about `deviceId`?
|
|
212975
|
-
*
|
|
212976
|
-
* Call it only behind `gate.on &&`. On its own it is still correct — the
|
|
212977
|
-
* guard is repeated inside — but the point of the prefix is that a disarmed
|
|
212978
|
-
* channel must not pay the call at all.
|
|
212979
|
-
*/
|
|
212980
|
-
wants(deviceId) {
|
|
212981
|
-
if (!this.on) return false;
|
|
212982
|
-
return this.devices === null || this.devices.has(deviceId);
|
|
212983
|
-
}
|
|
212984
|
-
/**
|
|
212985
|
-
* Emit one line on this channel, at the channel's declared level.
|
|
212986
|
-
*
|
|
212987
|
-
* The channel name is added as `tags.logChannel` so LogQL can select the
|
|
212988
|
-
* channel without matching on the message text, and whatever `tags` the
|
|
212989
|
-
* caller passed — `deviceId` above all — is preserved.
|
|
212990
|
-
*/
|
|
212991
|
-
log(logger, message, extras) {
|
|
212992
|
-
if (!this.on) return;
|
|
212993
|
-
const tags = {
|
|
212994
|
-
...extras.tags,
|
|
212995
|
-
logChannel: this.descriptor.name
|
|
212996
|
-
};
|
|
212997
|
-
const line = {
|
|
212998
|
-
...extras,
|
|
212999
|
-
tags
|
|
213000
|
-
};
|
|
213001
|
-
if (this.level === "error") logger.error(message, line);
|
|
213002
|
-
else if (this.level === "warn") logger.warn(message, line);
|
|
213003
|
-
else logger.info(message, line);
|
|
213004
|
-
}
|
|
213005
|
-
/**
|
|
213006
|
-
* Arm (or RE-arm, restarting) this channel. Off the hot path only.
|
|
213007
|
-
*
|
|
213008
|
-
* An empty `deviceIds` list is treated as "every camera" rather than "no
|
|
213009
|
-
* camera": a window that matches nothing is indistinguishable from a
|
|
213010
|
-
* disarmed one, and the operator who asked for it would wait for lines that
|
|
213011
|
-
* can never come.
|
|
213012
|
-
*/
|
|
213013
|
-
arm(window2) {
|
|
213014
|
-
const ids = window2.deviceIds;
|
|
213015
|
-
this.devices = ids === null || ids.length === 0 ? null : new Set(ids);
|
|
213016
|
-
this.closesAtMs = window2.armedUntilMs;
|
|
213017
|
-
this.on = true;
|
|
213018
|
-
}
|
|
213019
|
-
/** Disarm. Off the hot path only. */
|
|
213020
|
-
disarm() {
|
|
213021
|
-
this.on = false;
|
|
213022
|
-
this.devices = null;
|
|
213023
|
-
this.closesAtMs = 0;
|
|
213024
|
-
}
|
|
213025
|
-
};
|
|
213026
|
-
var LogChannelRegistry = class {
|
|
213027
|
-
gates = /* @__PURE__ */ new Map();
|
|
213028
|
-
/**
|
|
213029
|
-
* Declare a channel and get its gate.
|
|
213030
|
-
*
|
|
213031
|
-
* A duplicate name throws. Two declarations of one name is a programming
|
|
213032
|
-
* error, not a merge: the operator would arm one and the other would stay
|
|
213033
|
-
* dark, which is the dead-knob shape (D62) with an extra step.
|
|
213034
|
-
*/
|
|
213035
|
-
declare(descriptor) {
|
|
213036
|
-
const parsed = LogChannelDescriptorSchema.parse(descriptor);
|
|
213037
|
-
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`);
|
|
213038
|
-
const gate = new LogChannelGate(parsed);
|
|
213039
|
-
this.gates.set(parsed.name, gate);
|
|
213040
|
-
return gate;
|
|
213041
|
-
}
|
|
213042
|
-
/** The declarations, sorted by name so a list is stable to read and diff. */
|
|
213043
|
-
list() {
|
|
213044
|
-
return [...this.gates.values()].map((gate) => gate.descriptor).sort((a, b) => a.name.localeCompare(b.name));
|
|
213045
|
-
}
|
|
213046
|
-
/** The gate for a declared channel, or `undefined`. */
|
|
213047
|
-
gate(name) {
|
|
213048
|
-
return this.gates.get(name);
|
|
213049
|
-
}
|
|
213050
|
-
/**
|
|
213051
|
-
* Apply the FULL set of armed windows. Off the hot path.
|
|
213052
|
-
*
|
|
213053
|
-
* Full, not incremental, and that is the whole design: the document is the
|
|
213054
|
-
* authority, so a channel the document does not name is disarmed here. An
|
|
213055
|
-
* incremental apply would let a disarm get lost in transit and leave a
|
|
213056
|
-
* channel running that nobody can see is running.
|
|
213057
|
-
*
|
|
213058
|
-
* A window already past its deadline is ignored rather than armed — a
|
|
213059
|
-
* restore that re-armed an expired window would make a forgotten diagnostic
|
|
213060
|
-
* immortal across restarts.
|
|
213061
|
-
*
|
|
213062
|
-
* Returns the names it could not place, so the caller can log them: a
|
|
213063
|
-
* channel named in the document that this process does not declare is
|
|
213064
|
-
* either a typo or an addon that has not booted yet, and both deserve a
|
|
213065
|
-
* line rather than silence.
|
|
213066
|
-
*/
|
|
213067
|
-
apply(windows, nowMs) {
|
|
213068
|
-
const wanted = /* @__PURE__ */ new Map();
|
|
213069
|
-
const unknown = [];
|
|
213070
|
-
for (const window2 of windows) {
|
|
213071
|
-
if (window2.armedUntilMs <= nowMs) continue;
|
|
213072
|
-
if (!this.gates.has(window2.channel)) {
|
|
213073
|
-
unknown.push(window2.channel);
|
|
213074
|
-
continue;
|
|
213075
|
-
}
|
|
213076
|
-
wanted.set(window2.channel, window2);
|
|
213077
|
-
}
|
|
213078
|
-
for (const [name, gate] of this.gates) {
|
|
213079
|
-
const window2 = wanted.get(name);
|
|
213080
|
-
if (window2 === void 0) gate.disarm();
|
|
213081
|
-
else gate.arm(window2);
|
|
213082
|
-
}
|
|
213083
|
-
return unknown;
|
|
213084
|
-
}
|
|
213085
|
-
/**
|
|
213086
|
-
* Disarm whatever has run out. Called on a timer, NEVER from a log path — a
|
|
213087
|
-
* diagnostic that adds a `Date.now()` to the path it is measuring measures
|
|
213088
|
-
* itself.
|
|
213089
|
-
*
|
|
213090
|
-
* Returns the names it closed, so the caller can write the one line that
|
|
213091
|
-
* says a window ended and stops "it went quiet" from reading as "the branch
|
|
213092
|
-
* was not taken".
|
|
213093
|
-
*/
|
|
213094
|
-
tick(nowMs) {
|
|
213095
|
-
const closed = [];
|
|
213096
|
-
for (const [name, gate] of this.gates) if (gate.on && gate.armedUntilMs <= nowMs) {
|
|
213097
|
-
gate.disarm();
|
|
213098
|
-
closed.push(name);
|
|
213099
|
-
}
|
|
213100
|
-
return closed;
|
|
213101
|
-
}
|
|
213102
|
-
/** The channels armed right now, as the document would describe them. */
|
|
213103
|
-
armed() {
|
|
213104
|
-
const out = [];
|
|
213105
|
-
for (const [name, gate] of this.gates) if (gate.on) out.push({
|
|
213106
|
-
channel: name,
|
|
213107
|
-
armedUntilMs: gate.armedUntilMs,
|
|
213108
|
-
deviceIds: null
|
|
213109
|
-
});
|
|
213110
|
-
return out;
|
|
213111
|
-
}
|
|
213112
|
-
};
|
|
213113
|
-
var instance = null;
|
|
213114
|
-
function getLogChannelRegistry() {
|
|
213115
|
-
instance ??= new LogChannelRegistry();
|
|
213116
|
-
return instance;
|
|
213117
|
-
}
|
|
213118
|
-
function declareLogChannel(descriptor) {
|
|
213119
|
-
return getLogChannelRegistry().declare(descriptor);
|
|
213120
|
-
}
|
|
213121
|
-
function __resetLogChannelRegistryForTests() {
|
|
213122
|
-
instance = null;
|
|
213123
|
-
}
|
|
213124
|
-
var LOG_CHANNEL_TICK_MS = 5e3;
|
|
213125
|
-
function createLogChannelsProvider(logger, options = {}) {
|
|
213126
|
-
const registry = getLogChannelRegistry();
|
|
213127
|
-
const now = options.now ?? Date.now;
|
|
213128
|
-
const tickMs = options.tickMs ?? 5e3;
|
|
213129
|
-
const timer = setInterval(() => {
|
|
213130
|
-
const closed = registry.tick(now());
|
|
213131
|
-
for (const name of closed) logger.info("log channel window closed", {
|
|
213132
|
-
tags: { logChannel: name },
|
|
213133
|
-
meta: { channel: name }
|
|
213134
|
-
});
|
|
213135
|
-
}, tickMs);
|
|
213136
|
-
timer.unref?.();
|
|
213137
|
-
return {
|
|
213138
|
-
list: () => registry.list(),
|
|
213139
|
-
apply: (input) => {
|
|
213140
|
-
const unknown = registry.apply(input.windows, now());
|
|
213141
|
-
const armed = registry.armed();
|
|
213142
|
-
logger.info("log channels applied", { meta: {
|
|
213143
|
-
armed: armed.map((window2) => window2.channel),
|
|
213144
|
-
unknown,
|
|
213145
|
-
declared: registry.list().length
|
|
213146
|
-
} });
|
|
213147
|
-
return {
|
|
213148
|
-
armed: armed.length,
|
|
213149
|
-
unknown
|
|
213150
|
-
};
|
|
213151
|
-
},
|
|
213152
|
-
stop: () => {
|
|
213153
|
-
clearInterval(timer);
|
|
213154
|
-
}
|
|
213155
|
-
};
|
|
213156
|
-
}
|
|
213157
212934
|
var OpsLogDomainSchema = zod.z.enum(["recording", "events"]);
|
|
213158
212935
|
var OpsLogOpSchema = zod.z.enum([
|
|
213159
212936
|
"prune",
|
|
@@ -213859,21 +213636,20 @@ var require_dist4 = __commonJS({
|
|
|
213859
213636
|
* flag at upsert time, not here (the schema is provider-agnostic).
|
|
213860
213637
|
*/
|
|
213861
213638
|
nodeId: zod.z.string().optional(),
|
|
213862
|
-
isDefault: zod.z.boolean().default(false),
|
|
213863
213639
|
isSystem: zod.z.boolean().default(false),
|
|
213864
213640
|
/**
|
|
213865
|
-
*
|
|
213866
|
-
*
|
|
213867
|
-
*
|
|
213868
|
-
*
|
|
213869
|
-
*
|
|
213641
|
+
* THE write switch, and the only one (D383). `enabled: true` means every
|
|
213642
|
+
* consumer that chooses a write target for this type may write here, and all
|
|
213643
|
+
* enabled locations of a type are used TOGETHER; `false` means read-only —
|
|
213644
|
+
* still read, still played back, still age-swept, still drained, never
|
|
213645
|
+
* written.
|
|
213870
213646
|
*
|
|
213871
|
-
* OPTIONAL
|
|
213872
|
-
*
|
|
213873
|
-
*
|
|
213874
|
-
*
|
|
213875
|
-
*
|
|
213876
|
-
*
|
|
213647
|
+
* OPTIONAL only for the wire: an upsert that omits it means "leave what is
|
|
213648
|
+
* stored" on an update and "born inert unless it is the first location of its
|
|
213649
|
+
* type" on a create. On a PERSISTED row absence is legacy and it means
|
|
213650
|
+
* enabled — {@link isLocationEnabled} is the one place that says so, and the
|
|
213651
|
+
* orchestrator stamps every flagless row `true` once at hydrate so absence
|
|
213652
|
+
* stops existing rather than being re-derived on every read.
|
|
213877
213653
|
*/
|
|
213878
213654
|
enabled: zod.z.boolean().optional(),
|
|
213879
213655
|
/** COMPUTED at read time by the orchestrator (statfs of the backing volume
|
|
@@ -213886,6 +213662,10 @@ var require_dist4 = __commonJS({
|
|
|
213886
213662
|
createdAt: zod.z.number(),
|
|
213887
213663
|
updatedAt: zod.z.number()
|
|
213888
213664
|
});
|
|
213665
|
+
var LegacyStorageLocationDefaultSchema = zod.z.object({ isDefault: zod.z.boolean().optional() });
|
|
213666
|
+
function isLocationEnabled(location) {
|
|
213667
|
+
return location.enabled !== false;
|
|
213668
|
+
}
|
|
213889
213669
|
var StorageLocationRefSchema = zod.z.union([StorageLocationTypeSchema, zod.z.string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/)]);
|
|
213890
213670
|
var StorageAccessSchema = zod.z.enum(["local-path", "cap-mediated"]);
|
|
213891
213671
|
var STORAGE_ACCESS_FALLBACK = "local-path";
|
|
@@ -214183,163 +213963,253 @@ var require_dist4 = __commonJS({
|
|
|
214183
213963
|
function findTimezone(id) {
|
|
214184
213964
|
return TIMEZONES.find((tz) => tz.id === id);
|
|
214185
213965
|
}
|
|
214186
|
-
|
|
214187
|
-
|
|
214188
|
-
|
|
214189
|
-
|
|
214190
|
-
|
|
214191
|
-
|
|
214192
|
-
|
|
214193
|
-
|
|
214194
|
-
|
|
214195
|
-
|
|
214196
|
-
|
|
214197
|
-
|
|
214198
|
-
|
|
214199
|
-
|
|
214200
|
-
|
|
213966
|
+
var LogChannelLevelSchema = zod.z.enum([
|
|
213967
|
+
"info",
|
|
213968
|
+
"warn",
|
|
213969
|
+
"error"
|
|
213970
|
+
]);
|
|
213971
|
+
var LogChannelDescriptorSchema = zod.z.object({
|
|
213972
|
+
/**
|
|
213973
|
+
* Dotted `area.thing`, unique across the workspace. `area` is conventionally
|
|
213974
|
+
* the addon's short name so an operator reading a channel list can tell who
|
|
213975
|
+
* owns it without a second lookup.
|
|
213976
|
+
*/
|
|
213977
|
+
name: zod.z.string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
|
|
213978
|
+
/** One sentence: what the operator will SEE after arming it. */
|
|
213979
|
+
description: zod.z.string().min(1),
|
|
213980
|
+
/** The level its lines are emitted at. Never below `info`. */
|
|
213981
|
+
defaultLevel: LogChannelLevelSchema,
|
|
213982
|
+
/**
|
|
213983
|
+
* Whether this channel can be narrowed to a camera.
|
|
213984
|
+
*
|
|
213985
|
+
* `true` is a PROMISE with two halves, and both must hold: the gate is
|
|
213986
|
+
* consulted with the numeric device id, AND every line the channel admits
|
|
213987
|
+
* carries `tags: { deviceId }` with that same numeric id. The second half is
|
|
213988
|
+
* what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
|
|
213989
|
+
* keeps `deviceId` out of the stream labels for cardinality, so the tag in
|
|
213990
|
+
* the body is the only way to filter.
|
|
213991
|
+
*
|
|
213992
|
+
* A channel whose lines carry the device only in `meta` (or not at all) is
|
|
213993
|
+
* declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
|
|
213994
|
+
* the operator narrows to one camera, sees nothing, and concludes the code
|
|
213995
|
+
* path was never taken.
|
|
213996
|
+
*/
|
|
213997
|
+
perDevice: zod.z.boolean()
|
|
213998
|
+
});
|
|
213999
|
+
var LogChannelWindowSchema = zod.z.object({
|
|
214000
|
+
channel: zod.z.string().min(1),
|
|
214001
|
+
/** Epoch ms the window closes at. */
|
|
214002
|
+
armedUntilMs: zod.z.number(),
|
|
214003
|
+
/** `null` = every camera. A non-empty list narrows to those numeric ids. */
|
|
214004
|
+
deviceIds: zod.z.array(zod.z.number().int()).readonly().nullable()
|
|
214005
|
+
});
|
|
214006
|
+
var LogChannelGate = class {
|
|
214007
|
+
descriptor;
|
|
214008
|
+
/**
|
|
214009
|
+
* HOT PATH GUARD. A plain data FIELD, and it must stay one.
|
|
214010
|
+
*
|
|
214011
|
+
* `log-channel.spec.ts` asserts the property descriptor has no getter and
|
|
214012
|
+
* booby-traps the device set, so turning this into an accessor — or reading
|
|
214013
|
+
* anything before it — fails the spec instead of taxing every line the
|
|
214014
|
+
* process emits.
|
|
214015
|
+
*/
|
|
214016
|
+
on = false;
|
|
214017
|
+
/** `null` while armed for every camera. Never read while `on` is false. */
|
|
214018
|
+
devices = null;
|
|
214019
|
+
level;
|
|
214020
|
+
closesAtMs = 0;
|
|
214021
|
+
constructor(descriptor) {
|
|
214022
|
+
this.descriptor = descriptor;
|
|
214023
|
+
this.level = descriptor.defaultLevel;
|
|
214201
214024
|
}
|
|
214202
|
-
|
|
214203
|
-
|
|
214204
|
-
|
|
214205
|
-
if (target) {
|
|
214206
|
-
const withRes = eligible.filter((e) => e.resolution !== void 0);
|
|
214207
|
-
if (withRes.length > 0) {
|
|
214208
|
-
const picked = pickClosestResolution(withRes, target);
|
|
214209
|
-
if (picked) return toPicked(picked);
|
|
214210
|
-
}
|
|
214025
|
+
/** Epoch ms this channel disarms itself at. 0 when disarmed. */
|
|
214026
|
+
get armedUntilMs() {
|
|
214027
|
+
return this.on ? this.closesAtMs : 0;
|
|
214211
214028
|
}
|
|
214212
|
-
|
|
214213
|
-
|
|
214214
|
-
|
|
214215
|
-
|
|
214216
|
-
|
|
214217
|
-
|
|
214218
|
-
|
|
214219
|
-
|
|
214220
|
-
|
|
214221
|
-
|
|
214222
|
-
if (!bestAbove || pixels < bestAbove.pixels) bestAbove = {
|
|
214223
|
-
entry: e,
|
|
214224
|
-
pixels
|
|
214225
|
-
};
|
|
214226
|
-
} else if (!bestBelow || pixels > bestBelow.pixels) bestBelow = {
|
|
214227
|
-
entry: e,
|
|
214228
|
-
pixels
|
|
214229
|
-
};
|
|
214029
|
+
/**
|
|
214030
|
+
* Does this channel want a line about `deviceId`?
|
|
214031
|
+
*
|
|
214032
|
+
* Call it only behind `gate.on &&`. On its own it is still correct — the
|
|
214033
|
+
* guard is repeated inside — but the point of the prefix is that a disarmed
|
|
214034
|
+
* channel must not pay the call at all.
|
|
214035
|
+
*/
|
|
214036
|
+
wants(deviceId) {
|
|
214037
|
+
if (!this.on) return false;
|
|
214038
|
+
return this.devices === null || this.devices.has(deviceId);
|
|
214230
214039
|
}
|
|
214231
|
-
|
|
214232
|
-
|
|
214233
|
-
|
|
214234
|
-
|
|
214235
|
-
|
|
214236
|
-
|
|
214237
|
-
|
|
214238
|
-
|
|
214239
|
-
|
|
214240
|
-
|
|
214241
|
-
|
|
214242
|
-
|
|
214243
|
-
};
|
|
214244
|
-
return {
|
|
214245
|
-
key: UNATTRIBUTED_BUCKET_KEY,
|
|
214246
|
-
kind: "unattributed"
|
|
214247
|
-
};
|
|
214248
|
-
}
|
|
214249
|
-
function deci(value) {
|
|
214250
|
-
return Math.round(value * 10) / 10;
|
|
214251
|
-
}
|
|
214252
|
-
function foldSnapshotByFunction(rows, atMs) {
|
|
214253
|
-
const acc = /* @__PURE__ */ new Map();
|
|
214254
|
-
for (const row of rows) {
|
|
214255
|
-
const { key, kind } = bucketFor(row);
|
|
214256
|
-
const cur = acc.get(key) ?? {
|
|
214257
|
-
kind,
|
|
214258
|
-
main: 0,
|
|
214259
|
-
gc: 0,
|
|
214260
|
-
lifetime: 0,
|
|
214261
|
-
memory: 0,
|
|
214262
|
-
count: 0,
|
|
214263
|
-
splitKnown: true
|
|
214040
|
+
/**
|
|
214041
|
+
* Emit one line on this channel, at the channel's declared level.
|
|
214042
|
+
*
|
|
214043
|
+
* The channel name is added as `tags.logChannel` so LogQL can select the
|
|
214044
|
+
* channel without matching on the message text, and whatever `tags` the
|
|
214045
|
+
* caller passed — `deviceId` above all — is preserved.
|
|
214046
|
+
*/
|
|
214047
|
+
log(logger, message, extras) {
|
|
214048
|
+
if (!this.on) return;
|
|
214049
|
+
const tags = {
|
|
214050
|
+
...extras.tags,
|
|
214051
|
+
logChannel: this.descriptor.name
|
|
214264
214052
|
};
|
|
214265
|
-
const
|
|
214266
|
-
|
|
214267
|
-
|
|
214268
|
-
|
|
214269
|
-
|
|
214270
|
-
|
|
214271
|
-
|
|
214272
|
-
count: cur.count + 1,
|
|
214273
|
-
splitKnown: cur.splitKnown && known
|
|
214274
|
-
});
|
|
214053
|
+
const line = {
|
|
214054
|
+
...extras,
|
|
214055
|
+
tags
|
|
214056
|
+
};
|
|
214057
|
+
if (this.level === "error") logger.error(message, line);
|
|
214058
|
+
else if (this.level === "warn") logger.warn(message, line);
|
|
214059
|
+
else logger.info(message, line);
|
|
214275
214060
|
}
|
|
214276
|
-
|
|
214277
|
-
|
|
214278
|
-
|
|
214279
|
-
|
|
214280
|
-
|
|
214281
|
-
|
|
214282
|
-
|
|
214283
|
-
|
|
214284
|
-
|
|
214285
|
-
|
|
214286
|
-
|
|
214287
|
-
|
|
214288
|
-
|
|
214289
|
-
|
|
214290
|
-
|
|
214291
|
-
|
|
214292
|
-
|
|
214293
|
-
|
|
214294
|
-
|
|
214295
|
-
|
|
214061
|
+
/**
|
|
214062
|
+
* Arm (or RE-arm, restarting) this channel. Off the hot path only.
|
|
214063
|
+
*
|
|
214064
|
+
* An empty `deviceIds` list is treated as "every camera" rather than "no
|
|
214065
|
+
* camera": a window that matches nothing is indistinguishable from a
|
|
214066
|
+
* disarmed one, and the operator who asked for it would wait for lines that
|
|
214067
|
+
* can never come.
|
|
214068
|
+
*/
|
|
214069
|
+
arm(window2) {
|
|
214070
|
+
const ids = window2.deviceIds;
|
|
214071
|
+
this.devices = ids === null || ids.length === 0 ? null : new Set(ids);
|
|
214072
|
+
this.closesAtMs = window2.armedUntilMs;
|
|
214073
|
+
this.on = true;
|
|
214074
|
+
}
|
|
214075
|
+
/** Disarm. Off the hot path only. */
|
|
214076
|
+
disarm() {
|
|
214077
|
+
this.on = false;
|
|
214078
|
+
this.devices = null;
|
|
214079
|
+
this.closesAtMs = 0;
|
|
214080
|
+
}
|
|
214081
|
+
};
|
|
214082
|
+
var LogChannelRegistry = class {
|
|
214083
|
+
gates = /* @__PURE__ */ new Map();
|
|
214084
|
+
/**
|
|
214085
|
+
* Declare a channel and get its gate.
|
|
214086
|
+
*
|
|
214087
|
+
* A duplicate name throws. Two declarations of one name is a programming
|
|
214088
|
+
* error, not a merge: the operator would arm one and the other would stay
|
|
214089
|
+
* dark, which is the dead-knob shape (D62) with an extra step.
|
|
214090
|
+
*/
|
|
214091
|
+
declare(descriptor) {
|
|
214092
|
+
const parsed = LogChannelDescriptorSchema.parse(descriptor);
|
|
214093
|
+
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`);
|
|
214094
|
+
const gate = new LogChannelGate(parsed);
|
|
214095
|
+
this.gates.set(parsed.name, gate);
|
|
214096
|
+
return gate;
|
|
214097
|
+
}
|
|
214098
|
+
/** The declarations, sorted by name so a list is stable to read and diff. */
|
|
214099
|
+
list() {
|
|
214100
|
+
return [...this.gates.values()].map((gate) => gate.descriptor).sort((a, b) => a.name.localeCompare(b.name));
|
|
214101
|
+
}
|
|
214102
|
+
/** The gate for a declared channel, or `undefined`. */
|
|
214103
|
+
gate(name) {
|
|
214104
|
+
return this.gates.get(name);
|
|
214105
|
+
}
|
|
214106
|
+
/**
|
|
214107
|
+
* Apply the FULL set of armed windows. Off the hot path.
|
|
214108
|
+
*
|
|
214109
|
+
* Full, not incremental, and that is the whole design: the document is the
|
|
214110
|
+
* authority, so a channel the document does not name is disarmed here. An
|
|
214111
|
+
* incremental apply would let a disarm get lost in transit and leave a
|
|
214112
|
+
* channel running that nobody can see is running.
|
|
214113
|
+
*
|
|
214114
|
+
* A window already past its deadline is ignored rather than armed — a
|
|
214115
|
+
* restore that re-armed an expired window would make a forgotten diagnostic
|
|
214116
|
+
* immortal across restarts.
|
|
214117
|
+
*
|
|
214118
|
+
* Returns the names it could not place, so the caller can log them: a
|
|
214119
|
+
* channel named in the document that this process does not declare is
|
|
214120
|
+
* either a typo or an addon that has not booted yet, and both deserve a
|
|
214121
|
+
* line rather than silence.
|
|
214122
|
+
*/
|
|
214123
|
+
apply(windows, nowMs) {
|
|
214124
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
214125
|
+
const unknown = [];
|
|
214126
|
+
for (const window2 of windows) {
|
|
214127
|
+
if (window2.armedUntilMs <= nowMs) continue;
|
|
214128
|
+
if (!this.gates.has(window2.channel)) {
|
|
214129
|
+
unknown.push(window2.channel);
|
|
214130
|
+
continue;
|
|
214296
214131
|
}
|
|
214297
|
-
|
|
214298
|
-
|
|
214132
|
+
wanted.set(window2.channel, window2);
|
|
214133
|
+
}
|
|
214134
|
+
for (const [name, gate] of this.gates) {
|
|
214135
|
+
const window2 = wanted.get(name);
|
|
214136
|
+
if (window2 === void 0) gate.disarm();
|
|
214137
|
+
else gate.arm(window2);
|
|
214138
|
+
}
|
|
214139
|
+
return unknown;
|
|
214140
|
+
}
|
|
214141
|
+
/**
|
|
214142
|
+
* Disarm whatever has run out. Called on a timer, NEVER from a log path — a
|
|
214143
|
+
* diagnostic that adds a `Date.now()` to the path it is measuring measures
|
|
214144
|
+
* itself.
|
|
214145
|
+
*
|
|
214146
|
+
* Returns the names it closed, so the caller can write the one line that
|
|
214147
|
+
* says a window ended and stops "it went quiet" from reading as "the branch
|
|
214148
|
+
* was not taken".
|
|
214149
|
+
*/
|
|
214150
|
+
tick(nowMs) {
|
|
214151
|
+
const closed = [];
|
|
214152
|
+
for (const [name, gate] of this.gates) if (gate.on && gate.armedUntilMs <= nowMs) {
|
|
214153
|
+
gate.disarm();
|
|
214154
|
+
closed.push(name);
|
|
214155
|
+
}
|
|
214156
|
+
return closed;
|
|
214157
|
+
}
|
|
214158
|
+
/** The channels armed right now, as the document would describe them. */
|
|
214159
|
+
armed() {
|
|
214160
|
+
const out = [];
|
|
214161
|
+
for (const [name, gate] of this.gates) if (gate.on) out.push({
|
|
214162
|
+
channel: name,
|
|
214163
|
+
armedUntilMs: gate.armedUntilMs,
|
|
214164
|
+
deviceIds: null
|
|
214165
|
+
});
|
|
214166
|
+
return out;
|
|
214167
|
+
}
|
|
214168
|
+
};
|
|
214169
|
+
var instance = null;
|
|
214170
|
+
function getLogChannelRegistry() {
|
|
214171
|
+
instance ??= new LogChannelRegistry();
|
|
214172
|
+
return instance;
|
|
214299
214173
|
}
|
|
214300
|
-
function
|
|
214301
|
-
|
|
214302
|
-
return a < b ? a : b;
|
|
214174
|
+
function declareLogChannel(descriptor) {
|
|
214175
|
+
return getLogChannelRegistry().declare(descriptor);
|
|
214303
214176
|
}
|
|
214304
|
-
function
|
|
214305
|
-
|
|
214306
|
-
return a > b ? a : b;
|
|
214177
|
+
function __resetLogChannelRegistryForTests() {
|
|
214178
|
+
instance = null;
|
|
214307
214179
|
}
|
|
214308
|
-
|
|
214180
|
+
var LOG_CHANNEL_TICK_MS = 5e3;
|
|
214181
|
+
function createLogChannelsProvider(logger, options = {}) {
|
|
214182
|
+
const registry = getLogChannelRegistry();
|
|
214183
|
+
const now = options.now ?? Date.now;
|
|
214184
|
+
const tickMs = options.tickMs ?? 5e3;
|
|
214185
|
+
const timer = setInterval(() => {
|
|
214186
|
+
const closed = registry.tick(now());
|
|
214187
|
+
for (const name of closed) logger.info("log channel window closed", {
|
|
214188
|
+
tags: { logChannel: name },
|
|
214189
|
+
meta: { channel: name }
|
|
214190
|
+
});
|
|
214191
|
+
}, tickMs);
|
|
214192
|
+
timer.unref?.();
|
|
214309
214193
|
return {
|
|
214310
|
-
|
|
214311
|
-
|
|
214312
|
-
|
|
214313
|
-
|
|
214314
|
-
|
|
214315
|
-
|
|
214316
|
-
|
|
214317
|
-
|
|
214318
|
-
|
|
214319
|
-
|
|
214320
|
-
|
|
214321
|
-
|
|
214194
|
+
list: () => registry.list(),
|
|
214195
|
+
apply: (input) => {
|
|
214196
|
+
const unknown = registry.apply(input.windows, now());
|
|
214197
|
+
const armed = registry.armed();
|
|
214198
|
+
logger.info("log channels applied", { meta: {
|
|
214199
|
+
armed: armed.map((window2) => window2.channel),
|
|
214200
|
+
unknown,
|
|
214201
|
+
declared: registry.list().length
|
|
214202
|
+
} });
|
|
214203
|
+
return {
|
|
214204
|
+
armed: armed.length,
|
|
214205
|
+
unknown
|
|
214206
|
+
};
|
|
214207
|
+
},
|
|
214208
|
+
stop: () => {
|
|
214209
|
+
clearInterval(timer);
|
|
214210
|
+
}
|
|
214322
214211
|
};
|
|
214323
214212
|
}
|
|
214324
|
-
function resolveBucketMs(spanMs, cadenceMs, maxPoints) {
|
|
214325
|
-
if (maxPoints <= 0 || cadenceMs <= 0 || spanMs <= 0) return Math.max(cadenceMs, 1);
|
|
214326
|
-
const wanted = spanMs / maxPoints;
|
|
214327
|
-
if (wanted <= cadenceMs) return cadenceMs;
|
|
214328
|
-
return Math.ceil(wanted / cadenceMs) * cadenceMs;
|
|
214329
|
-
}
|
|
214330
|
-
function reducePoints(points, bucketMs, origin) {
|
|
214331
|
-
if (bucketMs <= 0 || points.length === 0) return points;
|
|
214332
|
-
const buckets = /* @__PURE__ */ new Map();
|
|
214333
|
-
for (const point of points) {
|
|
214334
|
-
const start = origin + Math.floor((point.atMs - origin) / bucketMs) * bucketMs;
|
|
214335
|
-
const held = buckets.get(start);
|
|
214336
|
-
buckets.set(start, held === void 0 ? {
|
|
214337
|
-
...point,
|
|
214338
|
-
atMs: start
|
|
214339
|
-
} : mergePoints(held, point, start));
|
|
214340
|
-
}
|
|
214341
|
-
return [...buckets.values()].toSorted((a, b) => a.atMs - b.atMs);
|
|
214342
|
-
}
|
|
214343
214213
|
var MAX_REASONS_PER_KEY = 16;
|
|
214344
214214
|
var MAX_KEYS = 1024;
|
|
214345
214215
|
var OVERFLOW_REASON = "other";
|
|
@@ -214431,6 +214301,163 @@ var require_dist4 = __commonJS({
|
|
|
214431
214301
|
if (sample.attempts <= 0) return null;
|
|
214432
214302
|
return (sample.attempts - sample.succeeded) / sample.attempts;
|
|
214433
214303
|
}
|
|
214304
|
+
var UNATTRIBUTED_BUCKET_KEY = "__unattributed__";
|
|
214305
|
+
var ROOT_BUCKET_KEY = "__root__";
|
|
214306
|
+
function bucketFor(row) {
|
|
214307
|
+
if (row.addonId !== null) return {
|
|
214308
|
+
key: row.addonId,
|
|
214309
|
+
kind: "addon"
|
|
214310
|
+
};
|
|
214311
|
+
if (row.classification === "root") return {
|
|
214312
|
+
key: ROOT_BUCKET_KEY,
|
|
214313
|
+
kind: "root"
|
|
214314
|
+
};
|
|
214315
|
+
return {
|
|
214316
|
+
key: UNATTRIBUTED_BUCKET_KEY,
|
|
214317
|
+
kind: "unattributed"
|
|
214318
|
+
};
|
|
214319
|
+
}
|
|
214320
|
+
function deci(value) {
|
|
214321
|
+
return Math.round(value * 10) / 10;
|
|
214322
|
+
}
|
|
214323
|
+
function foldSnapshotByFunction(rows, atMs) {
|
|
214324
|
+
const acc = /* @__PURE__ */ new Map();
|
|
214325
|
+
for (const row of rows) {
|
|
214326
|
+
const { key, kind } = bucketFor(row);
|
|
214327
|
+
const cur = acc.get(key) ?? {
|
|
214328
|
+
kind,
|
|
214329
|
+
main: 0,
|
|
214330
|
+
gc: 0,
|
|
214331
|
+
lifetime: 0,
|
|
214332
|
+
memory: 0,
|
|
214333
|
+
count: 0,
|
|
214334
|
+
splitKnown: true
|
|
214335
|
+
};
|
|
214336
|
+
const known = row.cpuMainPercent !== null && row.cpuGcPercent !== null;
|
|
214337
|
+
acc.set(key, {
|
|
214338
|
+
kind: cur.kind,
|
|
214339
|
+
main: cur.main + (row.cpuMainPercent ?? 0),
|
|
214340
|
+
gc: cur.gc + (row.cpuGcPercent ?? 0),
|
|
214341
|
+
lifetime: cur.lifetime + row.cpuPercent,
|
|
214342
|
+
memory: cur.memory + row.memoryRssBytes,
|
|
214343
|
+
count: cur.count + 1,
|
|
214344
|
+
splitKnown: cur.splitKnown && known
|
|
214345
|
+
});
|
|
214346
|
+
}
|
|
214347
|
+
return [...acc.entries()].map(([key, a]) => {
|
|
214348
|
+
const main = a.splitKnown ? deci(a.main) : null;
|
|
214349
|
+
const gc = a.splitKnown ? deci(a.gc) : null;
|
|
214350
|
+
const lifetime = deci(a.lifetime);
|
|
214351
|
+
return {
|
|
214352
|
+
key,
|
|
214353
|
+
kind: a.kind,
|
|
214354
|
+
point: {
|
|
214355
|
+
atMs,
|
|
214356
|
+
samples: 1,
|
|
214357
|
+
cpuMainPercent: main,
|
|
214358
|
+
cpuMainPercentMin: main,
|
|
214359
|
+
cpuGcPercent: gc,
|
|
214360
|
+
cpuGcPercentMin: gc,
|
|
214361
|
+
cpuLifetimePercent: lifetime,
|
|
214362
|
+
cpuLifetimePercentMin: lifetime,
|
|
214363
|
+
memoryRssBytes: a.memory,
|
|
214364
|
+
memoryRssBytesMin: a.memory,
|
|
214365
|
+
processCount: a.count,
|
|
214366
|
+
processCountMin: a.count
|
|
214367
|
+
}
|
|
214368
|
+
};
|
|
214369
|
+
});
|
|
214370
|
+
}
|
|
214371
|
+
function minNullable(a, b) {
|
|
214372
|
+
if (a === null || b === null) return null;
|
|
214373
|
+
return a < b ? a : b;
|
|
214374
|
+
}
|
|
214375
|
+
function maxNullable(a, b) {
|
|
214376
|
+
if (a === null || b === null) return null;
|
|
214377
|
+
return a > b ? a : b;
|
|
214378
|
+
}
|
|
214379
|
+
function mergePoints(held, next, atMs) {
|
|
214380
|
+
return {
|
|
214381
|
+
atMs,
|
|
214382
|
+
samples: held.samples + next.samples,
|
|
214383
|
+
cpuMainPercent: maxNullable(held.cpuMainPercent, next.cpuMainPercent),
|
|
214384
|
+
cpuMainPercentMin: minNullable(held.cpuMainPercentMin, next.cpuMainPercentMin),
|
|
214385
|
+
cpuGcPercent: maxNullable(held.cpuGcPercent, next.cpuGcPercent),
|
|
214386
|
+
cpuGcPercentMin: minNullable(held.cpuGcPercentMin, next.cpuGcPercentMin),
|
|
214387
|
+
cpuLifetimePercent: Math.max(held.cpuLifetimePercent, next.cpuLifetimePercent),
|
|
214388
|
+
cpuLifetimePercentMin: Math.min(held.cpuLifetimePercentMin, next.cpuLifetimePercentMin),
|
|
214389
|
+
memoryRssBytes: Math.max(held.memoryRssBytes, next.memoryRssBytes),
|
|
214390
|
+
memoryRssBytesMin: Math.min(held.memoryRssBytesMin, next.memoryRssBytesMin),
|
|
214391
|
+
processCount: Math.max(held.processCount, next.processCount),
|
|
214392
|
+
processCountMin: Math.min(held.processCountMin, next.processCountMin)
|
|
214393
|
+
};
|
|
214394
|
+
}
|
|
214395
|
+
function resolveBucketMs(spanMs, cadenceMs, maxPoints) {
|
|
214396
|
+
if (maxPoints <= 0 || cadenceMs <= 0 || spanMs <= 0) return Math.max(cadenceMs, 1);
|
|
214397
|
+
const wanted = spanMs / maxPoints;
|
|
214398
|
+
if (wanted <= cadenceMs) return cadenceMs;
|
|
214399
|
+
return Math.ceil(wanted / cadenceMs) * cadenceMs;
|
|
214400
|
+
}
|
|
214401
|
+
function reducePoints(points, bucketMs, origin) {
|
|
214402
|
+
if (bucketMs <= 0 || points.length === 0) return points;
|
|
214403
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
214404
|
+
for (const point of points) {
|
|
214405
|
+
const start = origin + Math.floor((point.atMs - origin) / bucketMs) * bucketMs;
|
|
214406
|
+
const held = buckets.get(start);
|
|
214407
|
+
buckets.set(start, held === void 0 ? {
|
|
214408
|
+
...point,
|
|
214409
|
+
atMs: start
|
|
214410
|
+
} : mergePoints(held, point, start));
|
|
214411
|
+
}
|
|
214412
|
+
return [...buckets.values()].toSorted((a, b) => a.atMs - b.atMs);
|
|
214413
|
+
}
|
|
214414
|
+
function pickPreferredRtspEntry(entries2, pref, deviceId, options = {}) {
|
|
214415
|
+
if (entries2.length === 0) return null;
|
|
214416
|
+
const prefix = `${deviceId}/`;
|
|
214417
|
+
const toPicked = (e) => ({
|
|
214418
|
+
brokerId: e.brokerId,
|
|
214419
|
+
profileId: e.brokerId.startsWith(prefix) ? e.brokerId.slice(prefix.length) : e.brokerId,
|
|
214420
|
+
url: e.url,
|
|
214421
|
+
mutedUrl: e.mutedUrl,
|
|
214422
|
+
enabled: e.enabled,
|
|
214423
|
+
...e.codec !== void 0 ? { codec: e.codec } : {},
|
|
214424
|
+
...e.resolution !== void 0 ? { resolution: e.resolution } : {}
|
|
214425
|
+
});
|
|
214426
|
+
if (pref !== "auto") {
|
|
214427
|
+
const match = entries2.find((e) => e.enabled && e.url.length > 0 && (e.profile === pref || e.brokerId === `${prefix}${pref}`));
|
|
214428
|
+
if (match) return toPicked(match);
|
|
214429
|
+
}
|
|
214430
|
+
const eligible = entries2.filter((e) => e.enabled && e.url.length > 0);
|
|
214431
|
+
if (eligible.length === 0) return null;
|
|
214432
|
+
const target = options.targetResolution;
|
|
214433
|
+
if (target) {
|
|
214434
|
+
const withRes = eligible.filter((e) => e.resolution !== void 0);
|
|
214435
|
+
if (withRes.length > 0) {
|
|
214436
|
+
const picked = pickClosestResolution(withRes, target);
|
|
214437
|
+
if (picked) return toPicked(picked);
|
|
214438
|
+
}
|
|
214439
|
+
}
|
|
214440
|
+
return toPicked(eligible[0]);
|
|
214441
|
+
}
|
|
214442
|
+
function pickClosestResolution(entries2, target) {
|
|
214443
|
+
const targetPixels = target.width * target.height;
|
|
214444
|
+
let bestAbove;
|
|
214445
|
+
let bestBelow;
|
|
214446
|
+
for (const e of entries2) {
|
|
214447
|
+
if (e.resolution === void 0) continue;
|
|
214448
|
+
const pixels = e.resolution.width * e.resolution.height;
|
|
214449
|
+
if (pixels >= targetPixels) {
|
|
214450
|
+
if (!bestAbove || pixels < bestAbove.pixels) bestAbove = {
|
|
214451
|
+
entry: e,
|
|
214452
|
+
pixels
|
|
214453
|
+
};
|
|
214454
|
+
} else if (!bestBelow || pixels > bestBelow.pixels) bestBelow = {
|
|
214455
|
+
entry: e,
|
|
214456
|
+
pixels
|
|
214457
|
+
};
|
|
214458
|
+
}
|
|
214459
|
+
return bestAbove?.entry ?? bestBelow?.entry;
|
|
214460
|
+
}
|
|
214434
214461
|
var FORMAT_KEYS = [
|
|
214435
214462
|
"onnx",
|
|
214436
214463
|
"coreml",
|
|
@@ -229960,7 +229987,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
229960
229987
|
}), zod.z.instanceof(Uint8Array)),
|
|
229961
229988
|
endDownload: require_sleep.method(zod.z.object({ downloadId: zod.z.string() }), zod.z.void(), { kind: "mutation" }),
|
|
229962
229989
|
listLocations: require_sleep.method(zod.z.object({ type: StorageLocationTypeSchema.optional() }), zod.z.array(StorageLocationSchema).readonly()),
|
|
229963
|
-
getDefaultLocation: require_sleep.method(zod.z.object({ type: StorageLocationTypeSchema }), StorageLocationSchema.nullable()),
|
|
229964
229990
|
listLocationDeclarations: require_sleep.method(zod.z.void(), zod.z.array(StorageLocationDeclarationSchema).readonly()),
|
|
229965
229991
|
upsertLocation: require_sleep.method(StorageLocationSchema.omit({
|
|
229966
229992
|
createdAt: true,
|
|
@@ -240233,62 +240259,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
240233
240259
|
"ffmpeg.threadCount": 0,
|
|
240234
240260
|
"auth.tokenExpiry": "30d"
|
|
240235
240261
|
};
|
|
240236
|
-
var CONTAINER_CHILD_PRIORITY = [
|
|
240237
|
-
"media-player",
|
|
240238
|
-
"alarm-panel",
|
|
240239
|
-
"thermostat",
|
|
240240
|
-
"climate",
|
|
240241
|
-
"humidifier",
|
|
240242
|
-
"water-heater",
|
|
240243
|
-
"lock",
|
|
240244
|
-
"cover",
|
|
240245
|
-
"valve",
|
|
240246
|
-
"fan",
|
|
240247
|
-
"vacuum",
|
|
240248
|
-
"lawn-mower",
|
|
240249
|
-
"light",
|
|
240250
|
-
"switch",
|
|
240251
|
-
"siren",
|
|
240252
|
-
"button",
|
|
240253
|
-
"control",
|
|
240254
|
-
"notifier",
|
|
240255
|
-
"script",
|
|
240256
|
-
"automation",
|
|
240257
|
-
"update",
|
|
240258
|
-
"presence",
|
|
240259
|
-
"weather",
|
|
240260
|
-
"image",
|
|
240261
|
-
"sensor"
|
|
240262
|
-
];
|
|
240263
|
-
var CONTAINER_CHILD_ROLE_PRIORITY = [
|
|
240264
|
-
"contact-sensor",
|
|
240265
|
-
"motion-sensor",
|
|
240266
|
-
"occupancy-sensor",
|
|
240267
|
-
"smoke-sensor",
|
|
240268
|
-
"co-sensor",
|
|
240269
|
-
"gas-sensor",
|
|
240270
|
-
"leak-sensor",
|
|
240271
|
-
"vibration-sensor",
|
|
240272
|
-
"tamper-sensor",
|
|
240273
|
-
"sound-sensor"
|
|
240274
|
-
];
|
|
240275
|
-
function roleRank(role) {
|
|
240276
|
-
if (role === void 0) return CONTAINER_CHILD_ROLE_PRIORITY.length;
|
|
240277
|
-
const i = CONTAINER_CHILD_ROLE_PRIORITY.indexOf(role);
|
|
240278
|
-
return i === -1 ? CONTAINER_CHILD_ROLE_PRIORITY.length : i;
|
|
240279
|
-
}
|
|
240280
|
-
function rank(type) {
|
|
240281
|
-
const i = CONTAINER_CHILD_PRIORITY.indexOf(type);
|
|
240282
|
-
return i === -1 ? CONTAINER_CHILD_PRIORITY.length : i;
|
|
240283
|
-
}
|
|
240284
|
-
function resolveContainerPrimaryChild(children, overrideEntityId, containerName) {
|
|
240285
|
-
if (overrideEntityId !== void 0 && overrideEntityId !== null) {
|
|
240286
|
-
const picked = children.find((c) => c.stableId === overrideEntityId) ?? children.find((c) => c.entityId !== void 0 && c.entityId === overrideEntityId);
|
|
240287
|
-
if (picked !== void 0) return picked;
|
|
240288
|
-
}
|
|
240289
|
-
const namesake = (c) => containerName !== void 0 && containerName.length > 0 && c.name !== void 0 && c.name.toLowerCase() === containerName.toLowerCase() ? 0 : 1;
|
|
240290
|
-
return [...children].toSorted((a, b) => roleRank(a.role) - roleRank(b.role) || namesake(a) - namesake(b) || rank(a.type) - rank(b.type))[0] ?? null;
|
|
240291
|
-
}
|
|
240292
240262
|
var AccessoryKind = {
|
|
240293
240263
|
Siren: require_sleep.DeviceRole.Siren,
|
|
240294
240264
|
Floodlight: require_sleep.DeviceRole.Floodlight,
|
|
@@ -241739,6 +241709,62 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
241739
241709
|
function isBatteryPresenceFault(presence) {
|
|
241740
241710
|
return presence === "unreachable";
|
|
241741
241711
|
}
|
|
241712
|
+
var CONTAINER_CHILD_PRIORITY = [
|
|
241713
|
+
"media-player",
|
|
241714
|
+
"alarm-panel",
|
|
241715
|
+
"thermostat",
|
|
241716
|
+
"climate",
|
|
241717
|
+
"humidifier",
|
|
241718
|
+
"water-heater",
|
|
241719
|
+
"lock",
|
|
241720
|
+
"cover",
|
|
241721
|
+
"valve",
|
|
241722
|
+
"fan",
|
|
241723
|
+
"vacuum",
|
|
241724
|
+
"lawn-mower",
|
|
241725
|
+
"light",
|
|
241726
|
+
"switch",
|
|
241727
|
+
"siren",
|
|
241728
|
+
"button",
|
|
241729
|
+
"control",
|
|
241730
|
+
"notifier",
|
|
241731
|
+
"script",
|
|
241732
|
+
"automation",
|
|
241733
|
+
"update",
|
|
241734
|
+
"presence",
|
|
241735
|
+
"weather",
|
|
241736
|
+
"image",
|
|
241737
|
+
"sensor"
|
|
241738
|
+
];
|
|
241739
|
+
var CONTAINER_CHILD_ROLE_PRIORITY = [
|
|
241740
|
+
"contact-sensor",
|
|
241741
|
+
"motion-sensor",
|
|
241742
|
+
"occupancy-sensor",
|
|
241743
|
+
"smoke-sensor",
|
|
241744
|
+
"co-sensor",
|
|
241745
|
+
"gas-sensor",
|
|
241746
|
+
"leak-sensor",
|
|
241747
|
+
"vibration-sensor",
|
|
241748
|
+
"tamper-sensor",
|
|
241749
|
+
"sound-sensor"
|
|
241750
|
+
];
|
|
241751
|
+
function roleRank(role) {
|
|
241752
|
+
if (role === void 0) return CONTAINER_CHILD_ROLE_PRIORITY.length;
|
|
241753
|
+
const i = CONTAINER_CHILD_ROLE_PRIORITY.indexOf(role);
|
|
241754
|
+
return i === -1 ? CONTAINER_CHILD_ROLE_PRIORITY.length : i;
|
|
241755
|
+
}
|
|
241756
|
+
function rank(type) {
|
|
241757
|
+
const i = CONTAINER_CHILD_PRIORITY.indexOf(type);
|
|
241758
|
+
return i === -1 ? CONTAINER_CHILD_PRIORITY.length : i;
|
|
241759
|
+
}
|
|
241760
|
+
function resolveContainerPrimaryChild(children, overrideEntityId, containerName) {
|
|
241761
|
+
if (overrideEntityId !== void 0 && overrideEntityId !== null) {
|
|
241762
|
+
const picked = children.find((c) => c.stableId === overrideEntityId) ?? children.find((c) => c.entityId !== void 0 && c.entityId === overrideEntityId);
|
|
241763
|
+
if (picked !== void 0) return picked;
|
|
241764
|
+
}
|
|
241765
|
+
const namesake = (c) => containerName !== void 0 && containerName.length > 0 && c.name !== void 0 && c.name.toLowerCase() === containerName.toLowerCase() ? 0 : 1;
|
|
241766
|
+
return [...children].toSorted((a, b) => roleRank(a.role) - roleRank(b.role) || namesake(a) - namesake(b) || rank(a.type) - rank(b.type))[0] ?? null;
|
|
241767
|
+
}
|
|
241742
241768
|
var DECLARED_INTEGRATION_FIXED_KEY = "fixed";
|
|
241743
241769
|
function declarationOwnerNodeId(localNodeId) {
|
|
241744
241770
|
const raw = localNodeId ?? "hub";
|
|
@@ -249022,12 +249048,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
249022
249048
|
addonId: null,
|
|
249023
249049
|
access: "view"
|
|
249024
249050
|
},
|
|
249025
|
-
"storage.getDefaultLocation": {
|
|
249026
|
-
capName: "storage",
|
|
249027
|
-
capScope: "system",
|
|
249028
|
-
addonId: null,
|
|
249029
|
-
access: "view"
|
|
249030
|
-
},
|
|
249031
249051
|
"storage.list": {
|
|
249032
249052
|
capName: "storage",
|
|
249033
249053
|
capScope: "system",
|
|
@@ -253403,7 +253423,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
253403
253423
|
readChunk: (input) => dispatch("storage", "readChunk", "query", input),
|
|
253404
253424
|
endDownload: (input) => dispatch("storage", "endDownload", "mutation", input),
|
|
253405
253425
|
listLocations: (input) => dispatch("storage", "listLocations", "query", input),
|
|
253406
|
-
getDefaultLocation: (input) => dispatch("storage", "getDefaultLocation", "query", input),
|
|
253407
253426
|
listLocationDeclarations: (input) => dispatch("storage", "listLocationDeclarations", "query", input),
|
|
253408
253427
|
upsertLocation: (input) => dispatch("storage", "upsertLocation", "mutation", input),
|
|
253409
253428
|
deleteLocation: (input) => dispatch("storage", "deleteLocation", "mutation", input),
|
|
@@ -256581,6 +256600,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
256581
256600
|
exports.LedgerWalkReportSchema = LedgerWalkReportSchema;
|
|
256582
256601
|
exports.LedgerWalkSkipCountsSchema = LedgerWalkSkipCountsSchema;
|
|
256583
256602
|
exports.LedgerWalkSkipReasonSchema = LedgerWalkSkipReasonSchema;
|
|
256603
|
+
exports.LegacyStorageLocationDefaultSchema = LegacyStorageLocationDefaultSchema;
|
|
256584
256604
|
exports.LinkedDeviceSchema = LinkedDeviceSchema;
|
|
256585
256605
|
exports.LinkedDevicesModeSchema = LinkedDevicesModeSchema;
|
|
256586
256606
|
exports.LlmDefaultSchema = LlmDefaultSchema;
|
|
@@ -257433,6 +257453,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
257433
257453
|
exports.isEvent = require_sleep.isEvent;
|
|
257434
257454
|
exports.isFirstLevelMacroClass = isFirstLevelMacroClass;
|
|
257435
257455
|
exports.isIsolatedBuiltin = isIsolatedBuiltin;
|
|
257456
|
+
exports.isLocationEnabled = isLocationEnabled;
|
|
257436
257457
|
exports.isNode = isNode;
|
|
257437
257458
|
exports.isObjectInput = isObjectInput;
|
|
257438
257459
|
exports.isOccupancyRule = isOccupancyRule;
|