camstack 1.2.110 → 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_KQ5D2wqr = __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_KQ5D2wqr = __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_KQ5D2wqr = __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_KQ5D2wqr = __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) {
|
|
@@ -28498,12 +28501,30 @@ var require_dist_KQ5D2wqr = __commonJS({
|
|
|
28498
28501
|
lastSuccessAt: zod.z.number().optional(),
|
|
28499
28502
|
/** Newest-archive size from `manifests.json`, or undefined. */
|
|
28500
28503
|
lastSuccessSizeBytes: zod.z.number().optional(),
|
|
28501
|
-
/**
|
|
28504
|
+
/**
|
|
28505
|
+
* Cron cadence(s) of the ENABLED schedules that fan out to this
|
|
28506
|
+
* destination, comma-joined. Absent when no enabled schedule targets it
|
|
28507
|
+
* — a destination nothing is scheduled to write to must not advertise a
|
|
28508
|
+
* cadence (D384). This is never the `backup_destination_policies.cron`
|
|
28509
|
+
* column: that per-location cron has scheduled nothing since 2026-07-28
|
|
28510
|
+
* and reading it made a destination with a DISABLED schedule claim a
|
|
28511
|
+
* nightly run.
|
|
28512
|
+
*/
|
|
28502
28513
|
cron: zod.z.string().optional(),
|
|
28503
|
-
/** ms-epoch of next
|
|
28514
|
+
/** ms-epoch of the next firing across those schedules (earliest), if any. */
|
|
28504
28515
|
nextRunAt: zod.z.number().optional(),
|
|
28505
|
-
/**
|
|
28506
|
-
|
|
28516
|
+
/**
|
|
28517
|
+
* ms-epoch of the last time a run ATTEMPTED to write here — success or
|
|
28518
|
+
* failure. Never a success stamp: pair it with `lastSuccessAt` (the
|
|
28519
|
+
* newest archive that actually landed) and `lastError`.
|
|
28520
|
+
*/
|
|
28521
|
+
lastAttemptAt: zod.z.number().optional(),
|
|
28522
|
+
/**
|
|
28523
|
+
* Why the last attempt failed, verbatim. Absent when the last attempt
|
|
28524
|
+
* landed the archive. A destination that has never been written to has
|
|
28525
|
+
* neither this nor `lastAttemptAt`.
|
|
28526
|
+
*/
|
|
28527
|
+
lastError: zod.z.string().optional()
|
|
28507
28528
|
});
|
|
28508
28529
|
var BackupArchiveEntrySchema = zod.z.object({
|
|
28509
28530
|
id: zod.z.string(),
|
|
@@ -28625,8 +28646,16 @@ var require_dist_KQ5D2wqr = __commonJS({
|
|
|
28625
28646
|
retentionCount: zod.z.number().int().min(1).max(1e3),
|
|
28626
28647
|
/** Optional subset of source locations to include; omitted = all. */
|
|
28627
28648
|
dataSources: zod.z.array(zod.z.string()).readonly().optional(),
|
|
28628
|
-
/**
|
|
28629
|
-
|
|
28649
|
+
/**
|
|
28650
|
+
* ms-epoch of the last tick that FIRED this schedule. Stamped before the
|
|
28651
|
+
* archive runs (it is the dedupe anchor), so it says "attempted", never
|
|
28652
|
+
* "succeeded" — a run refused by every destination stamps it too.
|
|
28653
|
+
*/
|
|
28654
|
+
lastAttemptAt: zod.z.number().optional(),
|
|
28655
|
+
/** ms-epoch of the last run of this schedule that landed at ≥1 destination. */
|
|
28656
|
+
lastSuccessAt: zod.z.number().optional(),
|
|
28657
|
+
/** Why the last fired run failed, verbatim. Absent when it succeeded. */
|
|
28658
|
+
lastError: zod.z.string().optional(),
|
|
28630
28659
|
/** ms-epoch of next computed firing (read-only, filled on list). */
|
|
28631
28660
|
nextRunAt: zod.z.number().optional()
|
|
28632
28661
|
});
|
|
@@ -28749,14 +28778,7 @@ var require_dist_KQ5D2wqr = __commonJS({
|
|
|
28749
28778
|
locationId: zod.z.string(),
|
|
28750
28779
|
enabled: zod.z.boolean(),
|
|
28751
28780
|
retentionCount: zod.z.number().int().min(1).max(1e3),
|
|
28752
|
-
label: zod.z.string().optional()
|
|
28753
|
-
/**
|
|
28754
|
-
* Per-destination cron expression. Empty string clears the
|
|
28755
|
-
* schedule (manual-only). Validated server-side via croner;
|
|
28756
|
-
* malformed expressions reject the upsert with an actionable
|
|
28757
|
-
* message.
|
|
28758
|
-
*/
|
|
28759
|
-
cron: zod.z.string().optional()
|
|
28781
|
+
label: zod.z.string().optional()
|
|
28760
28782
|
}), zod.z.void(), {
|
|
28761
28783
|
kind: "mutation",
|
|
28762
28784
|
auth: "admin"
|
|
@@ -40651,7 +40673,6 @@ var require_dist_KQ5D2wqr = __commonJS({
|
|
|
40651
40673
|
}), zod.z.instanceof(Uint8Array)),
|
|
40652
40674
|
endDownload: method(zod.z.object({ downloadId: zod.z.string() }), zod.z.void(), { kind: "mutation" }),
|
|
40653
40675
|
listLocations: method(zod.z.object({ type: StorageLocationTypeSchema.optional() }), zod.z.array(StorageLocationSchema).readonly()),
|
|
40654
|
-
getDefaultLocation: method(zod.z.object({ type: StorageLocationTypeSchema }), StorageLocationSchema.nullable()),
|
|
40655
40676
|
listLocationDeclarations: method(zod.z.void(), zod.z.array(StorageLocationDeclarationSchema).readonly()),
|
|
40656
40677
|
upsertLocation: method(StorageLocationSchema.omit({
|
|
40657
40678
|
createdAt: true,
|
|
@@ -50687,6 +50708,26 @@ var require_dist_KQ5D2wqr = __commonJS({
|
|
|
50687
50708
|
"ffmpeg.threadCount": 0,
|
|
50688
50709
|
"auth.tokenExpiry": "30d"
|
|
50689
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
|
+
}
|
|
50690
50731
|
var CONTAINER_CHILD_PRIORITY = [
|
|
50691
50732
|
"media-player",
|
|
50692
50733
|
"alarm-panel",
|
|
@@ -50743,26 +50784,6 @@ var require_dist_KQ5D2wqr = __commonJS({
|
|
|
50743
50784
|
const namesake = (c) => containerName !== void 0 && containerName.length > 0 && c.name !== void 0 && c.name.toLowerCase() === containerName.toLowerCase() ? 0 : 1;
|
|
50744
50785
|
return [...children].toSorted((a, b) => roleRank(a.role) - roleRank(b.role) || namesake(a) - namesake(b) || rank(a.type) - rank(b.type))[0] ?? null;
|
|
50745
50786
|
}
|
|
50746
|
-
var AccessoryKind = {
|
|
50747
|
-
Siren: DeviceRole.Siren,
|
|
50748
|
-
Floodlight: DeviceRole.Floodlight,
|
|
50749
|
-
Spotlight: DeviceRole.Spotlight,
|
|
50750
|
-
PirSensor: DeviceRole.PirSensor,
|
|
50751
|
-
Chime: DeviceRole.Chime,
|
|
50752
|
-
Autotrack: DeviceRole.Autotrack,
|
|
50753
|
-
Nightvision: DeviceRole.Nightvision,
|
|
50754
|
-
PrivacyMask: DeviceRole.PrivacyMask
|
|
50755
|
-
};
|
|
50756
|
-
AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
|
|
50757
|
-
function deriveBatteryPresence(input) {
|
|
50758
|
-
const status = input.status;
|
|
50759
|
-
if (!status) return "sleeping";
|
|
50760
|
-
if (status.sleeping !== true) return "awake";
|
|
50761
|
-
const lastContactAt = status.lastContactAt;
|
|
50762
|
-
if (typeof lastContactAt !== "number" || lastContactAt <= 0) return "sleeping";
|
|
50763
|
-
const budget = input.unreachableAfterMs ?? 216e5;
|
|
50764
|
-
return input.nowMs - lastContactAt > budget ? "unreachable" : "sleeping";
|
|
50765
|
-
}
|
|
50766
50787
|
var DECLARED_INTEGRATION_FIXED_KEY = "fixed";
|
|
50767
50788
|
function declarationOwnerNodeId(localNodeId) {
|
|
50768
50789
|
const raw = localNodeId ?? "hub";
|
|
@@ -56202,12 +56223,6 @@ var require_dist_KQ5D2wqr = __commonJS({
|
|
|
56202
56223
|
addonId: null,
|
|
56203
56224
|
access: "view"
|
|
56204
56225
|
},
|
|
56205
|
-
"storage.getDefaultLocation": {
|
|
56206
|
-
capName: "storage",
|
|
56207
|
-
capScope: "system",
|
|
56208
|
-
addonId: null,
|
|
56209
|
-
access: "view"
|
|
56210
|
-
},
|
|
56211
56226
|
"storage.list": {
|
|
56212
56227
|
capName: "storage",
|
|
56213
56228
|
capScope: "system",
|
|
@@ -60093,6 +60108,12 @@ var require_dist_KQ5D2wqr = __commonJS({
|
|
|
60093
60108
|
return LOG_LEVEL_RANK;
|
|
60094
60109
|
}
|
|
60095
60110
|
});
|
|
60111
|
+
Object.defineProperty(exports, "LegacyStorageLocationDefaultSchema", {
|
|
60112
|
+
enumerable: true,
|
|
60113
|
+
get: function() {
|
|
60114
|
+
return LegacyStorageLocationDefaultSchema;
|
|
60115
|
+
}
|
|
60116
|
+
});
|
|
60096
60117
|
Object.defineProperty(exports, "METHOD_ACCESS_MAP", {
|
|
60097
60118
|
enumerable: true,
|
|
60098
60119
|
get: function() {
|
|
@@ -60423,6 +60444,12 @@ var require_dist_KQ5D2wqr = __commonJS({
|
|
|
60423
60444
|
return isDeviceConfigCap;
|
|
60424
60445
|
}
|
|
60425
60446
|
});
|
|
60447
|
+
Object.defineProperty(exports, "isLocationEnabled", {
|
|
60448
|
+
enumerable: true,
|
|
60449
|
+
get: function() {
|
|
60450
|
+
return isLocationEnabled;
|
|
60451
|
+
}
|
|
60452
|
+
});
|
|
60426
60453
|
Object.defineProperty(exports, "isObjectInput", {
|
|
60427
60454
|
enumerable: true,
|
|
60428
60455
|
get: function() {
|
|
@@ -60675,7 +60702,7 @@ var require_alerts_addon = __commonJS({
|
|
|
60675
60702
|
[Symbol.toStringTag]: { value: "Module" }
|
|
60676
60703
|
});
|
|
60677
60704
|
require_chunk_Cek0wNdY();
|
|
60678
|
-
var require_dist10 =
|
|
60705
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
60679
60706
|
function selectExpired(alerts, cutoffMs) {
|
|
60680
60707
|
return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
|
|
60681
60708
|
}
|
|
@@ -61494,7 +61521,7 @@ var require_console_logging = __commonJS({
|
|
|
61494
61521
|
[Symbol.toStringTag]: { value: "Module" }
|
|
61495
61522
|
});
|
|
61496
61523
|
require_chunk_Cek0wNdY();
|
|
61497
|
-
var require_dist10 =
|
|
61524
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
61498
61525
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
61499
61526
|
var LEVEL_RANK = {
|
|
61500
61527
|
debug: 0,
|
|
@@ -61588,7 +61615,7 @@ var require_core_blocks_addon = __commonJS({
|
|
|
61588
61615
|
"use strict";
|
|
61589
61616
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
61590
61617
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
61591
|
-
var require_dist10 =
|
|
61618
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
61592
61619
|
var node_crypto = __require("crypto");
|
|
61593
61620
|
var node_fs_promises = __require("fs/promises");
|
|
61594
61621
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -62485,11 +62512,11 @@ var require_core_blocks = __commonJS({
|
|
|
62485
62512
|
}
|
|
62486
62513
|
});
|
|
62487
62514
|
|
|
62488
|
-
// ../system/dist/retired-settings-keys-
|
|
62489
|
-
var
|
|
62490
|
-
"../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) {
|
|
62491
62518
|
"use strict";
|
|
62492
|
-
var require_dist10 =
|
|
62519
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
62493
62520
|
function settingsStoreIsAuthoritativeHere(env) {
|
|
62494
62521
|
const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
|
|
62495
62522
|
return raw === "" || raw === "hub";
|
|
@@ -64703,8 +64730,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
64703
64730
|
[Symbol.toStringTag]: { value: "Module" }
|
|
64704
64731
|
});
|
|
64705
64732
|
require_chunk_Cek0wNdY();
|
|
64706
|
-
var require_dist10 =
|
|
64707
|
-
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();
|
|
64708
64735
|
var node_crypto = __require("crypto");
|
|
64709
64736
|
var _camstack_types_node = require_node();
|
|
64710
64737
|
var JOB_HISTORY = 20;
|
|
@@ -70329,7 +70356,7 @@ var require_hub_forwarder = __commonJS({
|
|
|
70329
70356
|
[Symbol.toStringTag]: { value: "Module" }
|
|
70330
70357
|
});
|
|
70331
70358
|
require_chunk_Cek0wNdY();
|
|
70332
|
-
var require_dist10 =
|
|
70359
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
70333
70360
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
70334
70361
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
70335
70362
|
var HubForwarderDestination = class {
|
|
@@ -70466,7 +70493,7 @@ var require_liveness_monitor_addon = __commonJS({
|
|
|
70466
70493
|
"use strict";
|
|
70467
70494
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
70468
70495
|
require_chunk_Cek0wNdY();
|
|
70469
|
-
var require_dist10 =
|
|
70496
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
70470
70497
|
var NO_DEVICES = "liveness:no-devices";
|
|
70471
70498
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
70472
70499
|
var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
|
|
@@ -70662,7 +70689,7 @@ var require_local_auth_addon = __commonJS({
|
|
|
70662
70689
|
[Symbol.toStringTag]: { value: "Module" }
|
|
70663
70690
|
});
|
|
70664
70691
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
70665
|
-
var require_dist10 =
|
|
70692
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
70666
70693
|
var node_crypto = __require("crypto");
|
|
70667
70694
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
70668
70695
|
var crypto$1 = __require("crypto");
|
|
@@ -78475,7 +78502,7 @@ var require_loki_logging = __commonJS({
|
|
|
78475
78502
|
[Symbol.toStringTag]: { value: "Module" }
|
|
78476
78503
|
});
|
|
78477
78504
|
require_chunk_Cek0wNdY();
|
|
78478
|
-
var require_dist10 =
|
|
78505
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
78479
78506
|
function sanitizeLabelName(raw) {
|
|
78480
78507
|
const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
|
|
78481
78508
|
return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
|
|
@@ -79040,7 +79067,7 @@ var require_native_metrics_addon = __commonJS({
|
|
|
79040
79067
|
[Symbol.toStringTag]: { value: "Module" }
|
|
79041
79068
|
});
|
|
79042
79069
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
79043
|
-
var require_dist10 =
|
|
79070
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
79044
79071
|
var node_fs_promises = __require("fs/promises");
|
|
79045
79072
|
var node_child_process = __require("child_process");
|
|
79046
79073
|
var node_util = __require("util");
|
|
@@ -81624,7 +81651,7 @@ var require_filesystem_storage_addon = __commonJS({
|
|
|
81624
81651
|
[Symbol.toStringTag]: { value: "Module" }
|
|
81625
81652
|
});
|
|
81626
81653
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
81627
|
-
var require_dist10 =
|
|
81654
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
81628
81655
|
var node_crypto = __require("crypto");
|
|
81629
81656
|
var node_fs_promises = __require("fs/promises");
|
|
81630
81657
|
var node_path = __require("path");
|
|
@@ -82740,8 +82767,8 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
82740
82767
|
[Symbol.toStringTag]: { value: "Module" }
|
|
82741
82768
|
});
|
|
82742
82769
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
82743
|
-
var require_dist10 =
|
|
82744
|
-
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();
|
|
82745
82772
|
var node_crypto = __require("crypto");
|
|
82746
82773
|
var node_fs = __require("fs");
|
|
82747
82774
|
var node_module = __require("module");
|
|
@@ -85231,7 +85258,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85231
85258
|
[Symbol.toStringTag]: { value: "Module" }
|
|
85232
85259
|
});
|
|
85233
85260
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
85234
|
-
var require_dist10 =
|
|
85261
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
85235
85262
|
var require_hub_cap_forward = require_hub_cap_forward_DmHNjbB0();
|
|
85236
85263
|
var zod = require_zod();
|
|
85237
85264
|
var node_crypto = __require("crypto");
|
|
@@ -85378,7 +85405,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85378
85405
|
display_name: loc.displayName,
|
|
85379
85406
|
provider_id: loc.providerId,
|
|
85380
85407
|
config: storedConfig,
|
|
85381
|
-
is_default:
|
|
85408
|
+
is_default: 0,
|
|
85382
85409
|
is_system: loc.isSystem ? 1 : 0,
|
|
85383
85410
|
created_at: loc.createdAt,
|
|
85384
85411
|
updated_at: loc.updatedAt
|
|
@@ -85407,7 +85434,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85407
85434
|
config,
|
|
85408
85435
|
...nodeId !== void 0 ? { nodeId } : {},
|
|
85409
85436
|
...enabled !== void 0 ? { enabled } : {},
|
|
85410
|
-
isDefault: row["is_default"] === 1 || row["is_default"] === true,
|
|
85437
|
+
...require_dist10.LegacyStorageLocationDefaultSchema.parse({ isDefault: row["is_default"] === 1 || row["is_default"] === true }),
|
|
85411
85438
|
isSystem: row["is_system"] === 1 || row["is_system"] === true,
|
|
85412
85439
|
createdAt: Number(row["created_at"] ?? 0),
|
|
85413
85440
|
updatedAt: Number(row["updated_at"] ?? 0)
|
|
@@ -85456,16 +85483,18 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85456
85483
|
const targetId = input.destinations[storageClass];
|
|
85457
85484
|
if (targetId === void 0) continue;
|
|
85458
85485
|
const namedSourceId = input.sources?.[storageClass];
|
|
85459
|
-
const
|
|
85460
|
-
const
|
|
85461
|
-
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.`);
|
|
85462
85491
|
if (source.type !== storageClass) throw new Error(`Storage location "${source.id}" is type "${source.type}", expected "${storageClass}"`);
|
|
85463
85492
|
const target = this.deps.locations.getLocationById(targetId);
|
|
85464
85493
|
if (!target) throw new Error(`Storage location "${targetId}" not found`);
|
|
85465
85494
|
if (target.type !== storageClass) throw new Error(`Storage location "${targetId}" is type "${target.type}", expected "${storageClass}"`);
|
|
85466
|
-
if (source.id === target.id) throw new Error(namedSourceId ? `Source and destination are the same location ("${targetId}")` : `Storage location "${targetId}" is already the "${storageClass}"
|
|
85467
|
-
const freezeSource =
|
|
85468
|
-
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.`);
|
|
85469
85498
|
moves.push({
|
|
85470
85499
|
storageClass,
|
|
85471
85500
|
fromLocationId: source.id,
|
|
@@ -85482,7 +85511,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85482
85511
|
if (moves.some((move) => move.storageClass === "eventMedia")) {
|
|
85483
85512
|
const unstamped = await this.deps.participants.analytics.countUnstamped();
|
|
85484
85513
|
if (unstamped === null) {
|
|
85485
|
-
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.");
|
|
85486
85515
|
findings.push({
|
|
85487
85516
|
code: "unstampedEventMediaRows",
|
|
85488
85517
|
storageClass: "eventMedia",
|
|
@@ -85491,7 +85520,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85491
85520
|
} else if (unstamped.anyPresent) {
|
|
85492
85521
|
const scale = unstamped.total === null ? "an unknown number of" : unstamped.total.toString();
|
|
85493
85522
|
const perLane = `${describeUnstamped(unstamped.media)} media, ${describeUnstamped(unstamped.retrainFrames)} retrain frames`;
|
|
85494
|
-
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"}" }).`);
|
|
85495
85524
|
findings.push({
|
|
85496
85525
|
code: "unstampedEventMediaRows",
|
|
85497
85526
|
storageClass: "eventMedia",
|
|
@@ -85630,7 +85659,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85630
85659
|
* remaining" action.
|
|
85631
85660
|
*
|
|
85632
85661
|
* A class appears here only when something is (or might be) left on a
|
|
85633
|
-
* location
|
|
85662
|
+
* location outside its write set. An empty result therefore means exactly
|
|
85634
85663
|
* "there is nothing to drain", which is what lets the UI offer the action
|
|
85635
85664
|
* only when it is true, and what lets {@link drain} refuse rather than start
|
|
85636
85665
|
* a job that would move nothing and report `done` — the failure mode D295
|
|
@@ -85644,11 +85673,13 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85644
85673
|
const out = [];
|
|
85645
85674
|
for (const storageClass of STORAGE_CLASSES) {
|
|
85646
85675
|
if (!MOVER_CLASSES.includes(storageClass)) continue;
|
|
85647
|
-
const
|
|
85676
|
+
const writeSet = this.deps.locations.writeTargets(storageClass);
|
|
85677
|
+
const target = writeSet[0];
|
|
85648
85678
|
if (!target) continue;
|
|
85679
|
+
const inWriteSet = new Set(writeSet.map((loc) => loc.id));
|
|
85649
85680
|
if (laneOf(storageClass) === "media") {
|
|
85650
85681
|
for (const source of this.deps.locations.listLocations({ type: storageClass })) {
|
|
85651
|
-
if (source.id
|
|
85682
|
+
if (inWriteSet.has(source.id)) continue;
|
|
85652
85683
|
const count = await unanswerable(this.deps.participants.analytics.residue({
|
|
85653
85684
|
toLocationId: target.id,
|
|
85654
85685
|
fromLocationId: source.id,
|
|
@@ -85666,7 +85697,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85666
85697
|
continue;
|
|
85667
85698
|
}
|
|
85668
85699
|
for (const source of this.deps.locations.listLocations({ type: storageClass })) {
|
|
85669
|
-
if (source.id
|
|
85700
|
+
if (inWriteSet.has(source.id)) continue;
|
|
85670
85701
|
const census = await unanswerable(this.deps.participants.recorder.residue({
|
|
85671
85702
|
fromLocationId: source.id,
|
|
85672
85703
|
footageClass: storageClass === "recordingsLow" ? "recordingsLow" : "recordings"
|
|
@@ -85684,12 +85715,12 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85684
85715
|
return out;
|
|
85685
85716
|
}
|
|
85686
85717
|
/**
|
|
85687
|
-
* Run the DRAIN half alone, against classes whose
|
|
85718
|
+
* Run the DRAIN half alone, against classes whose write set has already moved.
|
|
85688
85719
|
*
|
|
85689
85720
|
* ## Why this is a second verb rather than a looser `start`
|
|
85690
85721
|
*
|
|
85691
|
-
* `start` refuses a destination that is already the class's
|
|
85692
|
-
* (`"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
|
|
85693
85724
|
* is correct and it is load-bearing: there is genuinely nothing left to
|
|
85694
85725
|
* repoint, and an operator must never be able to re-repoint a migrated class
|
|
85695
85726
|
* by accident. Making `start` idempotent — "an already-repointed class
|
|
@@ -85698,7 +85729,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85698
85729
|
* operator reads ("pauses the writers…") would be a lie half the time.
|
|
85699
85730
|
*
|
|
85700
85731
|
* `drain` instead cannot repoint AT ALL: it never touches
|
|
85701
|
-
* `
|
|
85732
|
+
* `setWriteTargets`, and its job starts in `draining` with `repointed`
|
|
85702
85733
|
* already true, so the `repointing` / `refreshing` / `resuming` blocks of
|
|
85703
85734
|
* {@link run} are behind it and unreachable. The two verbs are disjoint, and
|
|
85704
85735
|
* `start`'s refusal keeps meaning exactly what it meant.
|
|
@@ -85708,7 +85739,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85708
85739
|
* The finished job is the audit of what happened; re-opening it destroys
|
|
85709
85740
|
* that. And a drain is needed in cases where no migration job ever existed
|
|
85710
85741
|
* (a mover armed by hand, footage stranded on a location an operator added
|
|
85711
|
-
* and then
|
|
85742
|
+
* and then disabled again). One job = one operation, and the work list comes
|
|
85712
85743
|
* from {@link residue} — the archive — not from what a previous job believed.
|
|
85713
85744
|
*/
|
|
85714
85745
|
async drain(input) {
|
|
@@ -85731,9 +85762,9 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85731
85762
|
const destinations = {};
|
|
85732
85763
|
for (const storageClass of input.classes) {
|
|
85733
85764
|
const remaining = residue.filter((entry) => entry.storageClass === storageClass);
|
|
85734
|
-
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.`);
|
|
85735
85766
|
for (const entry of remaining) {
|
|
85736
|
-
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.`);
|
|
85737
85768
|
moves.push({
|
|
85738
85769
|
storageClass,
|
|
85739
85770
|
fromLocationId: entry.fromLocationId,
|
|
@@ -85851,8 +85882,13 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
85851
85882
|
await this.setPhase(job, "repointing");
|
|
85852
85883
|
}
|
|
85853
85884
|
if (job.phase === "repointing") {
|
|
85854
|
-
const targets =
|
|
85855
|
-
|
|
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);
|
|
85856
85892
|
job.repointed = true;
|
|
85857
85893
|
await this.setPhase(job, "refreshing");
|
|
85858
85894
|
}
|
|
@@ -86052,9 +86088,9 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86052
86088
|
for (const move of job.moves) if (move.state !== "done") throw new Error(`${move.storageClass} move did not complete verification`);
|
|
86053
86089
|
}
|
|
86054
86090
|
/**
|
|
86055
|
-
* After a from→to copy whose source was NOT
|
|
86056
|
-
*
|
|
86057
|
-
* 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
|
|
86058
86094
|
* left alone.
|
|
86059
86095
|
*/
|
|
86060
86096
|
freezeDrainedSources(job) {
|
|
@@ -86652,33 +86688,77 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86652
86688
|
backfilled: backfill.length,
|
|
86653
86689
|
total: this.locations.size
|
|
86654
86690
|
} });
|
|
86655
|
-
this.
|
|
86691
|
+
this.migrateOntoTheWriteSet(rows);
|
|
86656
86692
|
}
|
|
86657
86693
|
/**
|
|
86658
|
-
*
|
|
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.
|
|
86659
86697
|
*
|
|
86660
|
-
*
|
|
86661
|
-
*
|
|
86662
|
-
* hand-edited store or a row written by another build can too. It matters
|
|
86663
|
-
* because every bare-type ref still resolves through {@link getDefaultLocation}
|
|
86664
|
-
* until Stage 5 of the enabled-write-set design: on such a row
|
|
86665
|
-
* `storage.write({ location: 'eventMedia' })` and the migration coordinator's
|
|
86666
|
-
* 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:
|
|
86667
86700
|
*
|
|
86668
|
-
*
|
|
86669
|
-
*
|
|
86670
|
-
*
|
|
86671
|
-
*
|
|
86672
|
-
*
|
|
86673
|
-
|
|
86674
|
-
|
|
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 = [];
|
|
86675
86720
|
for (const loc of this.locations.values()) {
|
|
86676
|
-
if (
|
|
86677
|
-
this.
|
|
86678
|
-
|
|
86679
|
-
|
|
86680
|
-
}
|
|
86721
|
+
if (loc.enabled !== void 0) continue;
|
|
86722
|
+
this.persistMigrated({
|
|
86723
|
+
...loc,
|
|
86724
|
+
enabled: true
|
|
86725
|
+
});
|
|
86726
|
+
stamped.push(loc.id);
|
|
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);
|
|
86681
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
|
+
});
|
|
86682
86762
|
}
|
|
86683
86763
|
/**
|
|
86684
86764
|
* Inject the declaration-driven cardinality source. Called once by the
|
|
@@ -86733,7 +86813,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86733
86813
|
const rows = await this.locationStore.loadAll();
|
|
86734
86814
|
for (const loc of rows) this.locations.set(loc.id, loc);
|
|
86735
86815
|
this.logger.info("storage-orchestrator: hydrated locations from store", { meta: { loaded: this.locations.size } });
|
|
86736
|
-
this.
|
|
86816
|
+
this.migrateOntoTheWriteSet(rows);
|
|
86737
86817
|
}
|
|
86738
86818
|
/**
|
|
86739
86819
|
* Boot backfill (SP1): stamp `nodeId` on every persisted node-local
|
|
@@ -86782,40 +86862,51 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86782
86862
|
const all = [...this.locations.values()];
|
|
86783
86863
|
return filter?.type ? all.filter((l) => l.type === filter.type) : all;
|
|
86784
86864
|
}
|
|
86785
|
-
getDefaultLocation(type) {
|
|
86786
|
-
for (const loc of this.locations.values()) if (loc.type === type && loc.isDefault) return loc;
|
|
86787
|
-
return null;
|
|
86788
|
-
}
|
|
86789
86865
|
/**
|
|
86790
|
-
*
|
|
86791
|
-
*
|
|
86792
|
-
* a
|
|
86793
|
-
* 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).
|
|
86794
86871
|
*/
|
|
86795
|
-
|
|
86796
|
-
|
|
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;
|
|
86797
86893
|
const now = Date.now();
|
|
86798
86894
|
const nextById = new Map(this.locations);
|
|
86799
86895
|
const touched = /* @__PURE__ */ new Map();
|
|
86800
|
-
for (const [
|
|
86801
|
-
const
|
|
86802
|
-
if (!
|
|
86803
|
-
if (
|
|
86804
|
-
|
|
86805
|
-
|
|
86806
|
-
|
|
86807
|
-
|
|
86808
|
-
|
|
86809
|
-
|
|
86810
|
-
|
|
86811
|
-
isDefault,
|
|
86812
|
-
...enable ? { enabled: true } : {},
|
|
86813
|
-
updatedAt: now
|
|
86814
|
-
};
|
|
86815
|
-
nextById.set(id, updated);
|
|
86816
|
-
touched.set(id, updated);
|
|
86817
|
-
}
|
|
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);
|
|
86818
86907
|
}
|
|
86908
|
+
if (touched.size === 0) return;
|
|
86909
|
+
refuseTypeWithoutWriteTarget(touched.values(), nextById.values());
|
|
86819
86910
|
if (this.locationStore) {
|
|
86820
86911
|
const previous = /* @__PURE__ */ new Map();
|
|
86821
86912
|
for (const id of touched.keys()) {
|
|
@@ -86836,13 +86927,10 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86836
86927
|
for (const [id, location] of nextById) this.locations.set(id, location);
|
|
86837
86928
|
}
|
|
86838
86929
|
/**
|
|
86839
|
-
* Insert or update a location.
|
|
86840
|
-
* demotes any other default for the same type — operators always see
|
|
86841
|
-
* exactly one default per type.
|
|
86930
|
+
* Insert or update a location.
|
|
86842
86931
|
*
|
|
86843
|
-
* When a persistence store is wired (Task 6), the
|
|
86844
|
-
*
|
|
86845
|
-
* 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.
|
|
86846
86934
|
*/
|
|
86847
86935
|
/**
|
|
86848
86936
|
* Refuse a `(location kind, provider)` pair the kind cannot use.
|
|
@@ -86928,30 +87016,17 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86928
87016
|
updatedAt: now
|
|
86929
87017
|
};
|
|
86930
87018
|
refuseLastWriteTargetDisabled(existing, next, this.locations.values());
|
|
86931
|
-
const demoted = [];
|
|
86932
|
-
if (next.isDefault) {
|
|
86933
|
-
for (const [otherId, otherLoc] of this.locations) if (otherLoc.type === next.type && otherLoc.id !== next.id && otherLoc.isDefault) {
|
|
86934
|
-
const updated = {
|
|
86935
|
-
...otherLoc,
|
|
86936
|
-
isDefault: false,
|
|
86937
|
-
updatedAt: now
|
|
86938
|
-
};
|
|
86939
|
-
this.locations.set(otherId, updated);
|
|
86940
|
-
demoted.push(updated);
|
|
86941
|
-
}
|
|
86942
|
-
}
|
|
86943
87019
|
this.locations.set(next.id, next);
|
|
86944
87020
|
this.logger.debug("storage-orchestrator: upsertLocation", { meta: {
|
|
86945
87021
|
id: next.id,
|
|
86946
87022
|
type: next.type,
|
|
86947
87023
|
providerId: next.providerId,
|
|
86948
|
-
|
|
87024
|
+
enabled: next.enabled
|
|
86949
87025
|
} });
|
|
86950
87026
|
if (this.locationStore) {
|
|
86951
87027
|
const store = this.locationStore;
|
|
86952
87028
|
(async () => {
|
|
86953
87029
|
try {
|
|
86954
|
-
for (const d of demoted) await store.upsert(d);
|
|
86955
87030
|
await store.upsert(next);
|
|
86956
87031
|
} catch (err) {
|
|
86957
87032
|
this.logger.error("storage-orchestrator: upsert persistence failed", { meta: {
|
|
@@ -86966,8 +87041,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86966
87041
|
/**
|
|
86967
87042
|
* Remove a location. Refuses the last location of a type and the last
|
|
86968
87043
|
* enabled location of a type. `isSystem` is informational — a frozen
|
|
86969
|
-
* recordings `:default` is deletable once a sibling is live.
|
|
86970
|
-
* the flagged default promotes an enabled sibling. Occupancy still
|
|
87044
|
+
* recordings `:default` is deletable once a sibling is live. Occupancy still
|
|
86971
87045
|
* refuses unless `force` is set; force never overrides uniqueness /
|
|
86972
87046
|
* last-enabled.
|
|
86973
87047
|
*/
|
|
@@ -86978,8 +87052,6 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86978
87052
|
if (ofType.length <= 1) throw new Error(`Cannot delete "${id}" \u2014 it is the only location for type "${loc.type}"`);
|
|
86979
87053
|
const enabledOfType = ofType.filter((l) => l.enabled !== false);
|
|
86980
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}"`);
|
|
86981
|
-
const successor = loc.isDefault === true ? ofType.find((l) => l.id !== id && l.enabled !== false) : void 0;
|
|
86982
|
-
if (loc.isDefault === true && successor === void 0) throw new Error(`Cannot delete "${id}" \u2014 it is the only enabled location for type "${loc.type}"`);
|
|
86983
87055
|
const occupancy = await this.probeOccupancy(loc);
|
|
86984
87056
|
if (occupancy !== "empty") {
|
|
86985
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`);
|
|
@@ -86989,21 +87061,6 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
86989
87061
|
occupancy
|
|
86990
87062
|
} });
|
|
86991
87063
|
}
|
|
86992
|
-
if (successor !== void 0 && successor.isDefault !== true) {
|
|
86993
|
-
const now = Date.now();
|
|
86994
|
-
const promoted = {
|
|
86995
|
-
...successor,
|
|
86996
|
-
isDefault: true,
|
|
86997
|
-
updatedAt: now
|
|
86998
|
-
};
|
|
86999
|
-
this.locations.set(successor.id, promoted);
|
|
87000
|
-
if (this.locationStore) this.locationStore.upsert(promoted).catch((err) => {
|
|
87001
|
-
this.logger.error("storage-orchestrator: default promotion persist failed", { meta: {
|
|
87002
|
-
id: successor.id,
|
|
87003
|
-
error: err instanceof Error ? err.message : String(err)
|
|
87004
|
-
} });
|
|
87005
|
-
});
|
|
87006
|
-
}
|
|
87007
87064
|
this.locations.delete(id);
|
|
87008
87065
|
if (this.locationStore) this.locationStore.delete(id).catch((err) => {
|
|
87009
87066
|
this.logger.error("storage-orchestrator: delete persistence failed", { meta: {
|
|
@@ -87082,9 +87139,17 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
87082
87139
|
if (!loc) throw new Error(`Storage location "${ref}" not found`);
|
|
87083
87140
|
return loc;
|
|
87084
87141
|
}
|
|
87085
|
-
const
|
|
87086
|
-
if (
|
|
87087
|
-
|
|
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;
|
|
87088
87153
|
}
|
|
87089
87154
|
/**
|
|
87090
87155
|
* Find the storage-provider that backs a given location — the entry point
|
|
@@ -87188,9 +87253,11 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
87188
87253
|
* declarations on a populated map is a no-op (each declared `id`
|
|
87189
87254
|
* already resolves to an existing `<id>:default`).
|
|
87190
87255
|
*
|
|
87191
|
-
*
|
|
87192
|
-
* `
|
|
87193
|
-
*
|
|
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
|
|
87194
87261
|
* `^[a-z][a-z0-9-]*:[a-z0-9-]+$` regex on `StorageLocationSchema.id`.
|
|
87195
87262
|
*
|
|
87196
87263
|
* `basePath` is the storage root (e.g. `<CAMSTACK_DATA>`). Each
|
|
@@ -87215,7 +87282,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
87215
87282
|
displayName: d.displayName,
|
|
87216
87283
|
providerId: input.providerId,
|
|
87217
87284
|
config: { basePath: base },
|
|
87218
|
-
|
|
87285
|
+
enabled: true,
|
|
87219
87286
|
isSystem: false
|
|
87220
87287
|
});
|
|
87221
87288
|
added++;
|
|
@@ -87235,7 +87302,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
87235
87302
|
const requested = input.enabled ?? existing?.enabled;
|
|
87236
87303
|
if (requested === void 0) {
|
|
87237
87304
|
if (existing) return {};
|
|
87238
|
-
return { enabled:
|
|
87305
|
+
return { enabled: !typeAlreadyHasLocation };
|
|
87239
87306
|
}
|
|
87240
87307
|
return { enabled: requested };
|
|
87241
87308
|
}
|
|
@@ -87251,6 +87318,16 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
87251
87318
|
}
|
|
87252
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.`);
|
|
87253
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
|
+
}
|
|
87254
87331
|
var DEFAULT_MAX_ROUNDS = 8;
|
|
87255
87332
|
var DEFAULT_HYSTERESIS_PERCENT = 1;
|
|
87256
87333
|
var StoragePressureManager = class {
|
|
@@ -87455,13 +87532,9 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
87455
87532
|
const rows = type !== void 0 ? service.listLocations({ type }) : service.listLocations();
|
|
87456
87533
|
return Promise.all(rows.map(async (loc) => ({
|
|
87457
87534
|
...this.redacted(loc),
|
|
87458
|
-
capacity: loc
|
|
87535
|
+
capacity: require_dist10.isLocationEnabled(loc) ? await this.localCapacityOf(loc) : null
|
|
87459
87536
|
})));
|
|
87460
87537
|
},
|
|
87461
|
-
getDefaultLocation: async ({ type }) => {
|
|
87462
|
-
const loc = service.getDefaultLocation(type);
|
|
87463
|
-
return loc === null ? null : this.redacted(loc);
|
|
87464
|
-
},
|
|
87465
87538
|
listLocationDeclarations: async () => service.listDeclarations(),
|
|
87466
87539
|
upsertLocation: async (input) => {
|
|
87467
87540
|
const stored = service.getLocationById(input.id);
|
|
@@ -88182,7 +88255,7 @@ var require_system_config_addon = __commonJS({
|
|
|
88182
88255
|
[Symbol.toStringTag]: { value: "Module" }
|
|
88183
88256
|
});
|
|
88184
88257
|
require_chunk_Cek0wNdY();
|
|
88185
|
-
var require_dist10 =
|
|
88258
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
88186
88259
|
var SECTION_TITLES = {
|
|
88187
88260
|
server: "Server",
|
|
88188
88261
|
auth: "Authentication"
|
|
@@ -106243,7 +106316,7 @@ var require_winston_logging = __commonJS({
|
|
|
106243
106316
|
[Symbol.toStringTag]: { value: "Module" }
|
|
106244
106317
|
});
|
|
106245
106318
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
106246
|
-
var require_dist10 =
|
|
106319
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
106247
106320
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
106248
106321
|
var node_path = __require("path");
|
|
106249
106322
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -112075,47 +112148,17 @@ var require_addon = __commonJS({
|
|
|
112075
112148
|
},
|
|
112076
112149
|
"setDeviceConfig": { "config": { "__nested__": { "bands": [] } } }
|
|
112077
112150
|
},
|
|
112078
|
-
"storage": { "upsertLocation": {
|
|
112079
|
-
"isDefault": false,
|
|
112080
|
-
"isSystem": false
|
|
112081
|
-
} },
|
|
112151
|
+
"storage": { "upsertLocation": { "isSystem": false } },
|
|
112082
112152
|
"storage-provider": {
|
|
112083
|
-
"beginDownload": { "location": { "__nested__": {
|
|
112084
|
-
|
|
112085
|
-
|
|
112086
|
-
} } },
|
|
112087
|
-
"
|
|
112088
|
-
|
|
112089
|
-
|
|
112090
|
-
} } },
|
|
112091
|
-
"
|
|
112092
|
-
"isDefault": false,
|
|
112093
|
-
"isSystem": false
|
|
112094
|
-
} } },
|
|
112095
|
-
"exists": { "location": { "__nested__": {
|
|
112096
|
-
"isDefault": false,
|
|
112097
|
-
"isSystem": false
|
|
112098
|
-
} } },
|
|
112099
|
-
"getAvailableSpace": { "location": { "__nested__": {
|
|
112100
|
-
"isDefault": false,
|
|
112101
|
-
"isSystem": false
|
|
112102
|
-
} } },
|
|
112103
|
-
"list": { "location": { "__nested__": {
|
|
112104
|
-
"isDefault": false,
|
|
112105
|
-
"isSystem": false
|
|
112106
|
-
} } },
|
|
112107
|
-
"read": { "location": { "__nested__": {
|
|
112108
|
-
"isDefault": false,
|
|
112109
|
-
"isSystem": false
|
|
112110
|
-
} } },
|
|
112111
|
-
"resolve": { "location": { "__nested__": {
|
|
112112
|
-
"isDefault": false,
|
|
112113
|
-
"isSystem": false
|
|
112114
|
-
} } },
|
|
112115
|
-
"write": { "location": { "__nested__": {
|
|
112116
|
-
"isDefault": false,
|
|
112117
|
-
"isSystem": false
|
|
112118
|
-
} } }
|
|
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 } } }
|
|
112119
112162
|
},
|
|
112120
112163
|
"stream-broker": {
|
|
112121
112164
|
"produceEventMedia": {
|
|
@@ -130881,7 +130924,7 @@ var require_dist3 = __commonJS({
|
|
|
130881
130924
|
"use strict";
|
|
130882
130925
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
130883
130926
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
130884
|
-
var require_dist10 =
|
|
130927
|
+
var require_dist10 = require_dist_jZb_HKI5();
|
|
130885
130928
|
var require_builtins_alerts_alerts_addon = require_alerts_addon();
|
|
130886
130929
|
require_alerts();
|
|
130887
130930
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
@@ -212888,253 +212931,6 @@ var require_dist4 = __commonJS({
|
|
|
212888
212931
|
function logLevelAtMost(level, threshold) {
|
|
212889
212932
|
return LOG_LEVEL_RANK[level] <= LOG_LEVEL_RANK[threshold];
|
|
212890
212933
|
}
|
|
212891
|
-
var LogChannelLevelSchema = zod.z.enum([
|
|
212892
|
-
"info",
|
|
212893
|
-
"warn",
|
|
212894
|
-
"error"
|
|
212895
|
-
]);
|
|
212896
|
-
var LogChannelDescriptorSchema = zod.z.object({
|
|
212897
|
-
/**
|
|
212898
|
-
* Dotted `area.thing`, unique across the workspace. `area` is conventionally
|
|
212899
|
-
* the addon's short name so an operator reading a channel list can tell who
|
|
212900
|
-
* owns it without a second lookup.
|
|
212901
|
-
*/
|
|
212902
|
-
name: zod.z.string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
|
|
212903
|
-
/** One sentence: what the operator will SEE after arming it. */
|
|
212904
|
-
description: zod.z.string().min(1),
|
|
212905
|
-
/** The level its lines are emitted at. Never below `info`. */
|
|
212906
|
-
defaultLevel: LogChannelLevelSchema,
|
|
212907
|
-
/**
|
|
212908
|
-
* Whether this channel can be narrowed to a camera.
|
|
212909
|
-
*
|
|
212910
|
-
* `true` is a PROMISE with two halves, and both must hold: the gate is
|
|
212911
|
-
* consulted with the numeric device id, AND every line the channel admits
|
|
212912
|
-
* carries `tags: { deviceId }` with that same numeric id. The second half is
|
|
212913
|
-
* what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
|
|
212914
|
-
* keeps `deviceId` out of the stream labels for cardinality, so the tag in
|
|
212915
|
-
* the body is the only way to filter.
|
|
212916
|
-
*
|
|
212917
|
-
* A channel whose lines carry the device only in `meta` (or not at all) is
|
|
212918
|
-
* declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
|
|
212919
|
-
* the operator narrows to one camera, sees nothing, and concludes the code
|
|
212920
|
-
* path was never taken.
|
|
212921
|
-
*/
|
|
212922
|
-
perDevice: zod.z.boolean()
|
|
212923
|
-
});
|
|
212924
|
-
var LogChannelWindowSchema = zod.z.object({
|
|
212925
|
-
channel: zod.z.string().min(1),
|
|
212926
|
-
/** Epoch ms the window closes at. */
|
|
212927
|
-
armedUntilMs: zod.z.number(),
|
|
212928
|
-
/** `null` = every camera. A non-empty list narrows to those numeric ids. */
|
|
212929
|
-
deviceIds: zod.z.array(zod.z.number().int()).readonly().nullable()
|
|
212930
|
-
});
|
|
212931
|
-
var LogChannelGate = class {
|
|
212932
|
-
descriptor;
|
|
212933
|
-
/**
|
|
212934
|
-
* HOT PATH GUARD. A plain data FIELD, and it must stay one.
|
|
212935
|
-
*
|
|
212936
|
-
* `log-channel.spec.ts` asserts the property descriptor has no getter and
|
|
212937
|
-
* booby-traps the device set, so turning this into an accessor — or reading
|
|
212938
|
-
* anything before it — fails the spec instead of taxing every line the
|
|
212939
|
-
* process emits.
|
|
212940
|
-
*/
|
|
212941
|
-
on = false;
|
|
212942
|
-
/** `null` while armed for every camera. Never read while `on` is false. */
|
|
212943
|
-
devices = null;
|
|
212944
|
-
level;
|
|
212945
|
-
closesAtMs = 0;
|
|
212946
|
-
constructor(descriptor) {
|
|
212947
|
-
this.descriptor = descriptor;
|
|
212948
|
-
this.level = descriptor.defaultLevel;
|
|
212949
|
-
}
|
|
212950
|
-
/** Epoch ms this channel disarms itself at. 0 when disarmed. */
|
|
212951
|
-
get armedUntilMs() {
|
|
212952
|
-
return this.on ? this.closesAtMs : 0;
|
|
212953
|
-
}
|
|
212954
|
-
/**
|
|
212955
|
-
* Does this channel want a line about `deviceId`?
|
|
212956
|
-
*
|
|
212957
|
-
* Call it only behind `gate.on &&`. On its own it is still correct — the
|
|
212958
|
-
* guard is repeated inside — but the point of the prefix is that a disarmed
|
|
212959
|
-
* channel must not pay the call at all.
|
|
212960
|
-
*/
|
|
212961
|
-
wants(deviceId) {
|
|
212962
|
-
if (!this.on) return false;
|
|
212963
|
-
return this.devices === null || this.devices.has(deviceId);
|
|
212964
|
-
}
|
|
212965
|
-
/**
|
|
212966
|
-
* Emit one line on this channel, at the channel's declared level.
|
|
212967
|
-
*
|
|
212968
|
-
* The channel name is added as `tags.logChannel` so LogQL can select the
|
|
212969
|
-
* channel without matching on the message text, and whatever `tags` the
|
|
212970
|
-
* caller passed — `deviceId` above all — is preserved.
|
|
212971
|
-
*/
|
|
212972
|
-
log(logger, message, extras) {
|
|
212973
|
-
if (!this.on) return;
|
|
212974
|
-
const tags = {
|
|
212975
|
-
...extras.tags,
|
|
212976
|
-
logChannel: this.descriptor.name
|
|
212977
|
-
};
|
|
212978
|
-
const line = {
|
|
212979
|
-
...extras,
|
|
212980
|
-
tags
|
|
212981
|
-
};
|
|
212982
|
-
if (this.level === "error") logger.error(message, line);
|
|
212983
|
-
else if (this.level === "warn") logger.warn(message, line);
|
|
212984
|
-
else logger.info(message, line);
|
|
212985
|
-
}
|
|
212986
|
-
/**
|
|
212987
|
-
* Arm (or RE-arm, restarting) this channel. Off the hot path only.
|
|
212988
|
-
*
|
|
212989
|
-
* An empty `deviceIds` list is treated as "every camera" rather than "no
|
|
212990
|
-
* camera": a window that matches nothing is indistinguishable from a
|
|
212991
|
-
* disarmed one, and the operator who asked for it would wait for lines that
|
|
212992
|
-
* can never come.
|
|
212993
|
-
*/
|
|
212994
|
-
arm(window2) {
|
|
212995
|
-
const ids = window2.deviceIds;
|
|
212996
|
-
this.devices = ids === null || ids.length === 0 ? null : new Set(ids);
|
|
212997
|
-
this.closesAtMs = window2.armedUntilMs;
|
|
212998
|
-
this.on = true;
|
|
212999
|
-
}
|
|
213000
|
-
/** Disarm. Off the hot path only. */
|
|
213001
|
-
disarm() {
|
|
213002
|
-
this.on = false;
|
|
213003
|
-
this.devices = null;
|
|
213004
|
-
this.closesAtMs = 0;
|
|
213005
|
-
}
|
|
213006
|
-
};
|
|
213007
|
-
var LogChannelRegistry = class {
|
|
213008
|
-
gates = /* @__PURE__ */ new Map();
|
|
213009
|
-
/**
|
|
213010
|
-
* Declare a channel and get its gate.
|
|
213011
|
-
*
|
|
213012
|
-
* A duplicate name throws. Two declarations of one name is a programming
|
|
213013
|
-
* error, not a merge: the operator would arm one and the other would stay
|
|
213014
|
-
* dark, which is the dead-knob shape (D62) with an extra step.
|
|
213015
|
-
*/
|
|
213016
|
-
declare(descriptor) {
|
|
213017
|
-
const parsed = LogChannelDescriptorSchema.parse(descriptor);
|
|
213018
|
-
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`);
|
|
213019
|
-
const gate = new LogChannelGate(parsed);
|
|
213020
|
-
this.gates.set(parsed.name, gate);
|
|
213021
|
-
return gate;
|
|
213022
|
-
}
|
|
213023
|
-
/** The declarations, sorted by name so a list is stable to read and diff. */
|
|
213024
|
-
list() {
|
|
213025
|
-
return [...this.gates.values()].map((gate) => gate.descriptor).sort((a, b) => a.name.localeCompare(b.name));
|
|
213026
|
-
}
|
|
213027
|
-
/** The gate for a declared channel, or `undefined`. */
|
|
213028
|
-
gate(name) {
|
|
213029
|
-
return this.gates.get(name);
|
|
213030
|
-
}
|
|
213031
|
-
/**
|
|
213032
|
-
* Apply the FULL set of armed windows. Off the hot path.
|
|
213033
|
-
*
|
|
213034
|
-
* Full, not incremental, and that is the whole design: the document is the
|
|
213035
|
-
* authority, so a channel the document does not name is disarmed here. An
|
|
213036
|
-
* incremental apply would let a disarm get lost in transit and leave a
|
|
213037
|
-
* channel running that nobody can see is running.
|
|
213038
|
-
*
|
|
213039
|
-
* A window already past its deadline is ignored rather than armed — a
|
|
213040
|
-
* restore that re-armed an expired window would make a forgotten diagnostic
|
|
213041
|
-
* immortal across restarts.
|
|
213042
|
-
*
|
|
213043
|
-
* Returns the names it could not place, so the caller can log them: a
|
|
213044
|
-
* channel named in the document that this process does not declare is
|
|
213045
|
-
* either a typo or an addon that has not booted yet, and both deserve a
|
|
213046
|
-
* line rather than silence.
|
|
213047
|
-
*/
|
|
213048
|
-
apply(windows, nowMs) {
|
|
213049
|
-
const wanted = /* @__PURE__ */ new Map();
|
|
213050
|
-
const unknown = [];
|
|
213051
|
-
for (const window2 of windows) {
|
|
213052
|
-
if (window2.armedUntilMs <= nowMs) continue;
|
|
213053
|
-
if (!this.gates.has(window2.channel)) {
|
|
213054
|
-
unknown.push(window2.channel);
|
|
213055
|
-
continue;
|
|
213056
|
-
}
|
|
213057
|
-
wanted.set(window2.channel, window2);
|
|
213058
|
-
}
|
|
213059
|
-
for (const [name, gate] of this.gates) {
|
|
213060
|
-
const window2 = wanted.get(name);
|
|
213061
|
-
if (window2 === void 0) gate.disarm();
|
|
213062
|
-
else gate.arm(window2);
|
|
213063
|
-
}
|
|
213064
|
-
return unknown;
|
|
213065
|
-
}
|
|
213066
|
-
/**
|
|
213067
|
-
* Disarm whatever has run out. Called on a timer, NEVER from a log path — a
|
|
213068
|
-
* diagnostic that adds a `Date.now()` to the path it is measuring measures
|
|
213069
|
-
* itself.
|
|
213070
|
-
*
|
|
213071
|
-
* Returns the names it closed, so the caller can write the one line that
|
|
213072
|
-
* says a window ended and stops "it went quiet" from reading as "the branch
|
|
213073
|
-
* was not taken".
|
|
213074
|
-
*/
|
|
213075
|
-
tick(nowMs) {
|
|
213076
|
-
const closed = [];
|
|
213077
|
-
for (const [name, gate] of this.gates) if (gate.on && gate.armedUntilMs <= nowMs) {
|
|
213078
|
-
gate.disarm();
|
|
213079
|
-
closed.push(name);
|
|
213080
|
-
}
|
|
213081
|
-
return closed;
|
|
213082
|
-
}
|
|
213083
|
-
/** The channels armed right now, as the document would describe them. */
|
|
213084
|
-
armed() {
|
|
213085
|
-
const out = [];
|
|
213086
|
-
for (const [name, gate] of this.gates) if (gate.on) out.push({
|
|
213087
|
-
channel: name,
|
|
213088
|
-
armedUntilMs: gate.armedUntilMs,
|
|
213089
|
-
deviceIds: null
|
|
213090
|
-
});
|
|
213091
|
-
return out;
|
|
213092
|
-
}
|
|
213093
|
-
};
|
|
213094
|
-
var instance = null;
|
|
213095
|
-
function getLogChannelRegistry() {
|
|
213096
|
-
instance ??= new LogChannelRegistry();
|
|
213097
|
-
return instance;
|
|
213098
|
-
}
|
|
213099
|
-
function declareLogChannel(descriptor) {
|
|
213100
|
-
return getLogChannelRegistry().declare(descriptor);
|
|
213101
|
-
}
|
|
213102
|
-
function __resetLogChannelRegistryForTests() {
|
|
213103
|
-
instance = null;
|
|
213104
|
-
}
|
|
213105
|
-
var LOG_CHANNEL_TICK_MS = 5e3;
|
|
213106
|
-
function createLogChannelsProvider(logger, options = {}) {
|
|
213107
|
-
const registry = getLogChannelRegistry();
|
|
213108
|
-
const now = options.now ?? Date.now;
|
|
213109
|
-
const tickMs = options.tickMs ?? 5e3;
|
|
213110
|
-
const timer = setInterval(() => {
|
|
213111
|
-
const closed = registry.tick(now());
|
|
213112
|
-
for (const name of closed) logger.info("log channel window closed", {
|
|
213113
|
-
tags: { logChannel: name },
|
|
213114
|
-
meta: { channel: name }
|
|
213115
|
-
});
|
|
213116
|
-
}, tickMs);
|
|
213117
|
-
timer.unref?.();
|
|
213118
|
-
return {
|
|
213119
|
-
list: () => registry.list(),
|
|
213120
|
-
apply: (input) => {
|
|
213121
|
-
const unknown = registry.apply(input.windows, now());
|
|
213122
|
-
const armed = registry.armed();
|
|
213123
|
-
logger.info("log channels applied", { meta: {
|
|
213124
|
-
armed: armed.map((window2) => window2.channel),
|
|
213125
|
-
unknown,
|
|
213126
|
-
declared: registry.list().length
|
|
213127
|
-
} });
|
|
213128
|
-
return {
|
|
213129
|
-
armed: armed.length,
|
|
213130
|
-
unknown
|
|
213131
|
-
};
|
|
213132
|
-
},
|
|
213133
|
-
stop: () => {
|
|
213134
|
-
clearInterval(timer);
|
|
213135
|
-
}
|
|
213136
|
-
};
|
|
213137
|
-
}
|
|
213138
212934
|
var OpsLogDomainSchema = zod.z.enum(["recording", "events"]);
|
|
213139
212935
|
var OpsLogOpSchema = zod.z.enum([
|
|
213140
212936
|
"prune",
|
|
@@ -213840,21 +213636,20 @@ var require_dist4 = __commonJS({
|
|
|
213840
213636
|
* flag at upsert time, not here (the schema is provider-agnostic).
|
|
213841
213637
|
*/
|
|
213842
213638
|
nodeId: zod.z.string().optional(),
|
|
213843
|
-
isDefault: zod.z.boolean().default(false),
|
|
213844
213639
|
isSystem: zod.z.boolean().default(false),
|
|
213845
213640
|
/**
|
|
213846
|
-
*
|
|
213847
|
-
*
|
|
213848
|
-
*
|
|
213849
|
-
*
|
|
213850
|
-
*
|
|
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.
|
|
213851
213646
|
*
|
|
213852
|
-
* OPTIONAL
|
|
213853
|
-
*
|
|
213854
|
-
*
|
|
213855
|
-
*
|
|
213856
|
-
*
|
|
213857
|
-
*
|
|
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.
|
|
213858
213653
|
*/
|
|
213859
213654
|
enabled: zod.z.boolean().optional(),
|
|
213860
213655
|
/** COMPUTED at read time by the orchestrator (statfs of the backing volume
|
|
@@ -213867,6 +213662,10 @@ var require_dist4 = __commonJS({
|
|
|
213867
213662
|
createdAt: zod.z.number(),
|
|
213868
213663
|
updatedAt: zod.z.number()
|
|
213869
213664
|
});
|
|
213665
|
+
var LegacyStorageLocationDefaultSchema = zod.z.object({ isDefault: zod.z.boolean().optional() });
|
|
213666
|
+
function isLocationEnabled(location) {
|
|
213667
|
+
return location.enabled !== false;
|
|
213668
|
+
}
|
|
213870
213669
|
var StorageLocationRefSchema = zod.z.union([StorageLocationTypeSchema, zod.z.string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/)]);
|
|
213871
213670
|
var StorageAccessSchema = zod.z.enum(["local-path", "cap-mediated"]);
|
|
213872
213671
|
var STORAGE_ACCESS_FALLBACK = "local-path";
|
|
@@ -214164,52 +213963,343 @@ var require_dist4 = __commonJS({
|
|
|
214164
213963
|
function findTimezone(id) {
|
|
214165
213964
|
return TIMEZONES.find((tz) => tz.id === id);
|
|
214166
213965
|
}
|
|
214167
|
-
|
|
214168
|
-
|
|
214169
|
-
|
|
214170
|
-
|
|
214171
|
-
|
|
214172
|
-
|
|
214173
|
-
|
|
214174
|
-
|
|
214175
|
-
|
|
214176
|
-
|
|
214177
|
-
|
|
214178
|
-
|
|
214179
|
-
|
|
214180
|
-
|
|
214181
|
-
|
|
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;
|
|
214182
214024
|
}
|
|
214183
|
-
|
|
214184
|
-
|
|
214185
|
-
|
|
214186
|
-
|
|
214187
|
-
|
|
214188
|
-
|
|
214189
|
-
|
|
214190
|
-
|
|
214025
|
+
/** Epoch ms this channel disarms itself at. 0 when disarmed. */
|
|
214026
|
+
get armedUntilMs() {
|
|
214027
|
+
return this.on ? this.closesAtMs : 0;
|
|
214028
|
+
}
|
|
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);
|
|
214039
|
+
}
|
|
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
|
|
214052
|
+
};
|
|
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);
|
|
214060
|
+
}
|
|
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;
|
|
214131
|
+
}
|
|
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);
|
|
214191
214138
|
}
|
|
214139
|
+
return unknown;
|
|
214192
214140
|
}
|
|
214193
|
-
|
|
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;
|
|
214194
214173
|
}
|
|
214195
|
-
function
|
|
214196
|
-
|
|
214197
|
-
|
|
214198
|
-
|
|
214199
|
-
|
|
214200
|
-
|
|
214201
|
-
|
|
214202
|
-
|
|
214203
|
-
|
|
214204
|
-
|
|
214205
|
-
|
|
214174
|
+
function declareLogChannel(descriptor) {
|
|
214175
|
+
return getLogChannelRegistry().declare(descriptor);
|
|
214176
|
+
}
|
|
214177
|
+
function __resetLogChannelRegistryForTests() {
|
|
214178
|
+
instance = null;
|
|
214179
|
+
}
|
|
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?.();
|
|
214193
|
+
return {
|
|
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
214206
|
};
|
|
214207
|
-
}
|
|
214208
|
-
|
|
214209
|
-
|
|
214210
|
-
}
|
|
214207
|
+
},
|
|
214208
|
+
stop: () => {
|
|
214209
|
+
clearInterval(timer);
|
|
214210
|
+
}
|
|
214211
|
+
};
|
|
214212
|
+
}
|
|
214213
|
+
var MAX_REASONS_PER_KEY = 16;
|
|
214214
|
+
var MAX_KEYS = 1024;
|
|
214215
|
+
var OVERFLOW_REASON = "other";
|
|
214216
|
+
function counterKey(deviceId, family, variant) {
|
|
214217
|
+
return variant === void 0 ? `${deviceId}\0${family}` : `${deviceId}\0${family}\0${variant}`;
|
|
214218
|
+
}
|
|
214219
|
+
var FailureCounters = class {
|
|
214220
|
+
maxKeys;
|
|
214221
|
+
maxReasons;
|
|
214222
|
+
counters = /* @__PURE__ */ new Map();
|
|
214223
|
+
refused = 0;
|
|
214224
|
+
constructor(maxKeys = MAX_KEYS, maxReasons = 16) {
|
|
214225
|
+
this.maxKeys = maxKeys;
|
|
214226
|
+
this.maxReasons = maxReasons;
|
|
214211
214227
|
}
|
|
214212
|
-
|
|
214228
|
+
/**
|
|
214229
|
+
* Counters refused because {@link MAX_KEYS} was already held.
|
|
214230
|
+
*
|
|
214231
|
+
* Cumulative for the life of the instance: a bound that bit is a fact about
|
|
214232
|
+
* the deployment, and a surface that hid it would under-report a fleet
|
|
214233
|
+
* precisely when the fleet got large enough to matter.
|
|
214234
|
+
*/
|
|
214235
|
+
get keysRefused() {
|
|
214236
|
+
return this.refused;
|
|
214237
|
+
}
|
|
214238
|
+
/** Counters currently held. */
|
|
214239
|
+
get size() {
|
|
214240
|
+
return this.counters.size;
|
|
214241
|
+
}
|
|
214242
|
+
/**
|
|
214243
|
+
* Fold one observation in.
|
|
214244
|
+
*
|
|
214245
|
+
* A non-positive or non-integer `deviceId` is REFUSED rather than bucketed:
|
|
214246
|
+
* see the module docblock — an entry that cannot name its camera is worse
|
|
214247
|
+
* than no entry.
|
|
214248
|
+
*/
|
|
214249
|
+
note(observation, nowMs) {
|
|
214250
|
+
if (!Number.isInteger(observation.deviceId) || observation.deviceId <= 0) return;
|
|
214251
|
+
const key = counterKey(observation.deviceId, observation.family, observation.variant);
|
|
214252
|
+
let counter = this.counters.get(key);
|
|
214253
|
+
if (counter === void 0) {
|
|
214254
|
+
if (this.counters.size >= this.maxKeys) {
|
|
214255
|
+
this.refused += 1;
|
|
214256
|
+
return;
|
|
214257
|
+
}
|
|
214258
|
+
counter = {
|
|
214259
|
+
deviceId: observation.deviceId,
|
|
214260
|
+
family: observation.family,
|
|
214261
|
+
variant: observation.variant,
|
|
214262
|
+
sinceMs: nowMs,
|
|
214263
|
+
attempts: 0,
|
|
214264
|
+
succeeded: 0,
|
|
214265
|
+
reasons: /* @__PURE__ */ new Map()
|
|
214266
|
+
};
|
|
214267
|
+
this.counters.set(key, counter);
|
|
214268
|
+
}
|
|
214269
|
+
counter.attempts += 1;
|
|
214270
|
+
if (observation.reason === void 0) {
|
|
214271
|
+
counter.succeeded += 1;
|
|
214272
|
+
return;
|
|
214273
|
+
}
|
|
214274
|
+
const reason = counter.reasons.has(observation.reason) || counter.reasons.size < this.maxReasons ? observation.reason : OVERFLOW_REASON;
|
|
214275
|
+
counter.reasons.set(reason, (counter.reasons.get(reason) ?? 0) + 1);
|
|
214276
|
+
}
|
|
214277
|
+
/** Read every counter. Never mutates — see the module docblock. */
|
|
214278
|
+
snapshot(nowMs) {
|
|
214279
|
+
const out = [];
|
|
214280
|
+
for (const counter of this.counters.values()) out.push({
|
|
214281
|
+
deviceId: counter.deviceId,
|
|
214282
|
+
family: counter.family,
|
|
214283
|
+
...counter.variant !== void 0 ? { variant: counter.variant } : {},
|
|
214284
|
+
sinceMs: counter.sinceMs,
|
|
214285
|
+
atMs: nowMs,
|
|
214286
|
+
attempts: counter.attempts,
|
|
214287
|
+
succeeded: counter.succeeded,
|
|
214288
|
+
reasons: [...counter.reasons.entries()].map(([reason, count]) => ({
|
|
214289
|
+
reason,
|
|
214290
|
+
count
|
|
214291
|
+
})).toSorted((a, b) => b.count - a.count)
|
|
214292
|
+
});
|
|
214293
|
+
return out;
|
|
214294
|
+
}
|
|
214295
|
+
/** Drop everything (host disposal). */
|
|
214296
|
+
clear() {
|
|
214297
|
+
this.counters.clear();
|
|
214298
|
+
}
|
|
214299
|
+
};
|
|
214300
|
+
function failureRate(sample) {
|
|
214301
|
+
if (sample.attempts <= 0) return null;
|
|
214302
|
+
return (sample.attempts - sample.succeeded) / sample.attempts;
|
|
214213
214303
|
}
|
|
214214
214304
|
var UNATTRIBUTED_BUCKET_KEY = "__unattributed__";
|
|
214215
214305
|
var ROOT_BUCKET_KEY = "__root__";
|
|
@@ -214321,96 +214411,52 @@ var require_dist4 = __commonJS({
|
|
|
214321
214411
|
}
|
|
214322
214412
|
return [...buckets.values()].toSorted((a, b) => a.atMs - b.atMs);
|
|
214323
214413
|
}
|
|
214324
|
-
|
|
214325
|
-
|
|
214326
|
-
|
|
214327
|
-
|
|
214328
|
-
|
|
214329
|
-
|
|
214330
|
-
|
|
214331
|
-
|
|
214332
|
-
|
|
214333
|
-
|
|
214334
|
-
|
|
214335
|
-
|
|
214336
|
-
|
|
214337
|
-
|
|
214338
|
-
|
|
214339
|
-
/**
|
|
214340
|
-
* Counters refused because {@link MAX_KEYS} was already held.
|
|
214341
|
-
*
|
|
214342
|
-
* Cumulative for the life of the instance: a bound that bit is a fact about
|
|
214343
|
-
* the deployment, and a surface that hid it would under-report a fleet
|
|
214344
|
-
* precisely when the fleet got large enough to matter.
|
|
214345
|
-
*/
|
|
214346
|
-
get keysRefused() {
|
|
214347
|
-
return this.refused;
|
|
214348
|
-
}
|
|
214349
|
-
/** Counters currently held. */
|
|
214350
|
-
get size() {
|
|
214351
|
-
return this.counters.size;
|
|
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);
|
|
214352
214429
|
}
|
|
214353
|
-
|
|
214354
|
-
|
|
214355
|
-
|
|
214356
|
-
|
|
214357
|
-
|
|
214358
|
-
|
|
214359
|
-
|
|
214360
|
-
|
|
214361
|
-
if (!Number.isInteger(observation.deviceId) || observation.deviceId <= 0) return;
|
|
214362
|
-
const key = counterKey(observation.deviceId, observation.family, observation.variant);
|
|
214363
|
-
let counter = this.counters.get(key);
|
|
214364
|
-
if (counter === void 0) {
|
|
214365
|
-
if (this.counters.size >= this.maxKeys) {
|
|
214366
|
-
this.refused += 1;
|
|
214367
|
-
return;
|
|
214368
|
-
}
|
|
214369
|
-
counter = {
|
|
214370
|
-
deviceId: observation.deviceId,
|
|
214371
|
-
family: observation.family,
|
|
214372
|
-
variant: observation.variant,
|
|
214373
|
-
sinceMs: nowMs,
|
|
214374
|
-
attempts: 0,
|
|
214375
|
-
succeeded: 0,
|
|
214376
|
-
reasons: /* @__PURE__ */ new Map()
|
|
214377
|
-
};
|
|
214378
|
-
this.counters.set(key, counter);
|
|
214379
|
-
}
|
|
214380
|
-
counter.attempts += 1;
|
|
214381
|
-
if (observation.reason === void 0) {
|
|
214382
|
-
counter.succeeded += 1;
|
|
214383
|
-
return;
|
|
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);
|
|
214384
214438
|
}
|
|
214385
|
-
const reason = counter.reasons.has(observation.reason) || counter.reasons.size < this.maxReasons ? observation.reason : OVERFLOW_REASON;
|
|
214386
|
-
counter.reasons.set(reason, (counter.reasons.get(reason) ?? 0) + 1);
|
|
214387
|
-
}
|
|
214388
|
-
/** Read every counter. Never mutates — see the module docblock. */
|
|
214389
|
-
snapshot(nowMs) {
|
|
214390
|
-
const out = [];
|
|
214391
|
-
for (const counter of this.counters.values()) out.push({
|
|
214392
|
-
deviceId: counter.deviceId,
|
|
214393
|
-
family: counter.family,
|
|
214394
|
-
...counter.variant !== void 0 ? { variant: counter.variant } : {},
|
|
214395
|
-
sinceMs: counter.sinceMs,
|
|
214396
|
-
atMs: nowMs,
|
|
214397
|
-
attempts: counter.attempts,
|
|
214398
|
-
succeeded: counter.succeeded,
|
|
214399
|
-
reasons: [...counter.reasons.entries()].map(([reason, count]) => ({
|
|
214400
|
-
reason,
|
|
214401
|
-
count
|
|
214402
|
-
})).toSorted((a, b) => b.count - a.count)
|
|
214403
|
-
});
|
|
214404
|
-
return out;
|
|
214405
214439
|
}
|
|
214406
|
-
|
|
214407
|
-
|
|
214408
|
-
|
|
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
|
+
};
|
|
214409
214458
|
}
|
|
214410
|
-
|
|
214411
|
-
function failureRate(sample) {
|
|
214412
|
-
if (sample.attempts <= 0) return null;
|
|
214413
|
-
return (sample.attempts - sample.succeeded) / sample.attempts;
|
|
214459
|
+
return bestAbove?.entry ?? bestBelow?.entry;
|
|
214414
214460
|
}
|
|
214415
214461
|
var FORMAT_KEYS = [
|
|
214416
214462
|
"onnx",
|
|
@@ -216679,12 +216725,30 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
216679
216725
|
lastSuccessAt: zod.z.number().optional(),
|
|
216680
216726
|
/** Newest-archive size from `manifests.json`, or undefined. */
|
|
216681
216727
|
lastSuccessSizeBytes: zod.z.number().optional(),
|
|
216682
|
-
/**
|
|
216728
|
+
/**
|
|
216729
|
+
* Cron cadence(s) of the ENABLED schedules that fan out to this
|
|
216730
|
+
* destination, comma-joined. Absent when no enabled schedule targets it
|
|
216731
|
+
* — a destination nothing is scheduled to write to must not advertise a
|
|
216732
|
+
* cadence (D384). This is never the `backup_destination_policies.cron`
|
|
216733
|
+
* column: that per-location cron has scheduled nothing since 2026-07-28
|
|
216734
|
+
* and reading it made a destination with a DISABLED schedule claim a
|
|
216735
|
+
* nightly run.
|
|
216736
|
+
*/
|
|
216683
216737
|
cron: zod.z.string().optional(),
|
|
216684
|
-
/** ms-epoch of next
|
|
216738
|
+
/** ms-epoch of the next firing across those schedules (earliest), if any. */
|
|
216685
216739
|
nextRunAt: zod.z.number().optional(),
|
|
216686
|
-
/**
|
|
216687
|
-
|
|
216740
|
+
/**
|
|
216741
|
+
* ms-epoch of the last time a run ATTEMPTED to write here — success or
|
|
216742
|
+
* failure. Never a success stamp: pair it with `lastSuccessAt` (the
|
|
216743
|
+
* newest archive that actually landed) and `lastError`.
|
|
216744
|
+
*/
|
|
216745
|
+
lastAttemptAt: zod.z.number().optional(),
|
|
216746
|
+
/**
|
|
216747
|
+
* Why the last attempt failed, verbatim. Absent when the last attempt
|
|
216748
|
+
* landed the archive. A destination that has never been written to has
|
|
216749
|
+
* neither this nor `lastAttemptAt`.
|
|
216750
|
+
*/
|
|
216751
|
+
lastError: zod.z.string().optional()
|
|
216688
216752
|
});
|
|
216689
216753
|
var BackupArchiveEntrySchema = zod.z.object({
|
|
216690
216754
|
id: zod.z.string(),
|
|
@@ -216806,8 +216870,16 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
216806
216870
|
retentionCount: zod.z.number().int().min(1).max(1e3),
|
|
216807
216871
|
/** Optional subset of source locations to include; omitted = all. */
|
|
216808
216872
|
dataSources: zod.z.array(zod.z.string()).readonly().optional(),
|
|
216809
|
-
/**
|
|
216810
|
-
|
|
216873
|
+
/**
|
|
216874
|
+
* ms-epoch of the last tick that FIRED this schedule. Stamped before the
|
|
216875
|
+
* archive runs (it is the dedupe anchor), so it says "attempted", never
|
|
216876
|
+
* "succeeded" — a run refused by every destination stamps it too.
|
|
216877
|
+
*/
|
|
216878
|
+
lastAttemptAt: zod.z.number().optional(),
|
|
216879
|
+
/** ms-epoch of the last run of this schedule that landed at ≥1 destination. */
|
|
216880
|
+
lastSuccessAt: zod.z.number().optional(),
|
|
216881
|
+
/** Why the last fired run failed, verbatim. Absent when it succeeded. */
|
|
216882
|
+
lastError: zod.z.string().optional(),
|
|
216811
216883
|
/** ms-epoch of next computed firing (read-only, filled on list). */
|
|
216812
216884
|
nextRunAt: zod.z.number().optional()
|
|
216813
216885
|
});
|
|
@@ -216930,14 +217002,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
216930
217002
|
locationId: zod.z.string(),
|
|
216931
217003
|
enabled: zod.z.boolean(),
|
|
216932
217004
|
retentionCount: zod.z.number().int().min(1).max(1e3),
|
|
216933
|
-
label: zod.z.string().optional()
|
|
216934
|
-
/**
|
|
216935
|
-
* Per-destination cron expression. Empty string clears the
|
|
216936
|
-
* schedule (manual-only). Validated server-side via croner;
|
|
216937
|
-
* malformed expressions reject the upsert with an actionable
|
|
216938
|
-
* message.
|
|
216939
|
-
*/
|
|
216940
|
-
cron: zod.z.string().optional()
|
|
217005
|
+
label: zod.z.string().optional()
|
|
216941
217006
|
}), zod.z.void(), {
|
|
216942
217007
|
kind: "mutation",
|
|
216943
217008
|
auth: "admin"
|
|
@@ -229922,7 +229987,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
229922
229987
|
}), zod.z.instanceof(Uint8Array)),
|
|
229923
229988
|
endDownload: require_sleep.method(zod.z.object({ downloadId: zod.z.string() }), zod.z.void(), { kind: "mutation" }),
|
|
229924
229989
|
listLocations: require_sleep.method(zod.z.object({ type: StorageLocationTypeSchema.optional() }), zod.z.array(StorageLocationSchema).readonly()),
|
|
229925
|
-
getDefaultLocation: require_sleep.method(zod.z.object({ type: StorageLocationTypeSchema }), StorageLocationSchema.nullable()),
|
|
229926
229990
|
listLocationDeclarations: require_sleep.method(zod.z.void(), zod.z.array(StorageLocationDeclarationSchema).readonly()),
|
|
229927
229991
|
upsertLocation: require_sleep.method(StorageLocationSchema.omit({
|
|
229928
229992
|
createdAt: true,
|
|
@@ -240195,62 +240259,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
240195
240259
|
"ffmpeg.threadCount": 0,
|
|
240196
240260
|
"auth.tokenExpiry": "30d"
|
|
240197
240261
|
};
|
|
240198
|
-
var CONTAINER_CHILD_PRIORITY = [
|
|
240199
|
-
"media-player",
|
|
240200
|
-
"alarm-panel",
|
|
240201
|
-
"thermostat",
|
|
240202
|
-
"climate",
|
|
240203
|
-
"humidifier",
|
|
240204
|
-
"water-heater",
|
|
240205
|
-
"lock",
|
|
240206
|
-
"cover",
|
|
240207
|
-
"valve",
|
|
240208
|
-
"fan",
|
|
240209
|
-
"vacuum",
|
|
240210
|
-
"lawn-mower",
|
|
240211
|
-
"light",
|
|
240212
|
-
"switch",
|
|
240213
|
-
"siren",
|
|
240214
|
-
"button",
|
|
240215
|
-
"control",
|
|
240216
|
-
"notifier",
|
|
240217
|
-
"script",
|
|
240218
|
-
"automation",
|
|
240219
|
-
"update",
|
|
240220
|
-
"presence",
|
|
240221
|
-
"weather",
|
|
240222
|
-
"image",
|
|
240223
|
-
"sensor"
|
|
240224
|
-
];
|
|
240225
|
-
var CONTAINER_CHILD_ROLE_PRIORITY = [
|
|
240226
|
-
"contact-sensor",
|
|
240227
|
-
"motion-sensor",
|
|
240228
|
-
"occupancy-sensor",
|
|
240229
|
-
"smoke-sensor",
|
|
240230
|
-
"co-sensor",
|
|
240231
|
-
"gas-sensor",
|
|
240232
|
-
"leak-sensor",
|
|
240233
|
-
"vibration-sensor",
|
|
240234
|
-
"tamper-sensor",
|
|
240235
|
-
"sound-sensor"
|
|
240236
|
-
];
|
|
240237
|
-
function roleRank(role) {
|
|
240238
|
-
if (role === void 0) return CONTAINER_CHILD_ROLE_PRIORITY.length;
|
|
240239
|
-
const i = CONTAINER_CHILD_ROLE_PRIORITY.indexOf(role);
|
|
240240
|
-
return i === -1 ? CONTAINER_CHILD_ROLE_PRIORITY.length : i;
|
|
240241
|
-
}
|
|
240242
|
-
function rank(type) {
|
|
240243
|
-
const i = CONTAINER_CHILD_PRIORITY.indexOf(type);
|
|
240244
|
-
return i === -1 ? CONTAINER_CHILD_PRIORITY.length : i;
|
|
240245
|
-
}
|
|
240246
|
-
function resolveContainerPrimaryChild(children, overrideEntityId, containerName) {
|
|
240247
|
-
if (overrideEntityId !== void 0 && overrideEntityId !== null) {
|
|
240248
|
-
const picked = children.find((c) => c.stableId === overrideEntityId) ?? children.find((c) => c.entityId !== void 0 && c.entityId === overrideEntityId);
|
|
240249
|
-
if (picked !== void 0) return picked;
|
|
240250
|
-
}
|
|
240251
|
-
const namesake = (c) => containerName !== void 0 && containerName.length > 0 && c.name !== void 0 && c.name.toLowerCase() === containerName.toLowerCase() ? 0 : 1;
|
|
240252
|
-
return [...children].toSorted((a, b) => roleRank(a.role) - roleRank(b.role) || namesake(a) - namesake(b) || rank(a.type) - rank(b.type))[0] ?? null;
|
|
240253
|
-
}
|
|
240254
240262
|
var AccessoryKind = {
|
|
240255
240263
|
Siren: require_sleep.DeviceRole.Siren,
|
|
240256
240264
|
Floodlight: require_sleep.DeviceRole.Floodlight,
|
|
@@ -241701,6 +241709,62 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
241701
241709
|
function isBatteryPresenceFault(presence) {
|
|
241702
241710
|
return presence === "unreachable";
|
|
241703
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
|
+
}
|
|
241704
241768
|
var DECLARED_INTEGRATION_FIXED_KEY = "fixed";
|
|
241705
241769
|
function declarationOwnerNodeId(localNodeId) {
|
|
241706
241770
|
const raw = localNodeId ?? "hub";
|
|
@@ -248984,12 +249048,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
248984
249048
|
addonId: null,
|
|
248985
249049
|
access: "view"
|
|
248986
249050
|
},
|
|
248987
|
-
"storage.getDefaultLocation": {
|
|
248988
|
-
capName: "storage",
|
|
248989
|
-
capScope: "system",
|
|
248990
|
-
addonId: null,
|
|
248991
|
-
access: "view"
|
|
248992
|
-
},
|
|
248993
249051
|
"storage.list": {
|
|
248994
249052
|
capName: "storage",
|
|
248995
249053
|
capScope: "system",
|
|
@@ -253365,7 +253423,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
253365
253423
|
readChunk: (input) => dispatch("storage", "readChunk", "query", input),
|
|
253366
253424
|
endDownload: (input) => dispatch("storage", "endDownload", "mutation", input),
|
|
253367
253425
|
listLocations: (input) => dispatch("storage", "listLocations", "query", input),
|
|
253368
|
-
getDefaultLocation: (input) => dispatch("storage", "getDefaultLocation", "query", input),
|
|
253369
253426
|
listLocationDeclarations: (input) => dispatch("storage", "listLocationDeclarations", "query", input),
|
|
253370
253427
|
upsertLocation: (input) => dispatch("storage", "upsertLocation", "mutation", input),
|
|
253371
253428
|
deleteLocation: (input) => dispatch("storage", "deleteLocation", "mutation", input),
|
|
@@ -256543,6 +256600,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
256543
256600
|
exports.LedgerWalkReportSchema = LedgerWalkReportSchema;
|
|
256544
256601
|
exports.LedgerWalkSkipCountsSchema = LedgerWalkSkipCountsSchema;
|
|
256545
256602
|
exports.LedgerWalkSkipReasonSchema = LedgerWalkSkipReasonSchema;
|
|
256603
|
+
exports.LegacyStorageLocationDefaultSchema = LegacyStorageLocationDefaultSchema;
|
|
256546
256604
|
exports.LinkedDeviceSchema = LinkedDeviceSchema;
|
|
256547
256605
|
exports.LinkedDevicesModeSchema = LinkedDevicesModeSchema;
|
|
256548
256606
|
exports.LlmDefaultSchema = LlmDefaultSchema;
|
|
@@ -257395,6 +257453,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
257395
257453
|
exports.isEvent = require_sleep.isEvent;
|
|
257396
257454
|
exports.isFirstLevelMacroClass = isFirstLevelMacroClass;
|
|
257397
257455
|
exports.isIsolatedBuiltin = isIsolatedBuiltin;
|
|
257456
|
+
exports.isLocationEnabled = isLocationEnabled;
|
|
257398
257457
|
exports.isNode = isNode;
|
|
257399
257458
|
exports.isObjectInput = isObjectInput;
|
|
257400
257459
|
exports.isOccupancyRule = isOccupancyRule;
|