camstack 1.2.60 → 1.2.62
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-CDgIzo82.js
|
|
23637
|
+
var require_dist_CDgIzo82 = __commonJS({
|
|
23638
|
+
"../system/dist/dist-CDgIzo82.js"(exports) {
|
|
23639
23639
|
"use strict";
|
|
23640
23640
|
var zod = require_zod();
|
|
23641
23641
|
var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
|
|
@@ -26256,29 +26256,50 @@ var require_dist_BPlfW_CG = __commonJS({
|
|
|
26256
26256
|
});
|
|
26257
26257
|
var StorageMigrationLeaseInputSchema = zod.z.object({ leaseId: zod.z.string().min(1) });
|
|
26258
26258
|
var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: zod.z.string().min(1) });
|
|
26259
|
+
var MediaRelocateModeSchema = zod.z.enum([
|
|
26260
|
+
"move",
|
|
26261
|
+
"seal",
|
|
26262
|
+
"gallery"
|
|
26263
|
+
]);
|
|
26259
26264
|
var RelocateMediaInputSchema = zod.z.object({
|
|
26260
26265
|
toLocationId: zod.z.string(),
|
|
26261
|
-
throttleMbps: zod.z.number().min(1).max(1e3).optional()
|
|
26266
|
+
throttleMbps: zod.z.number().min(1).max(1e3).optional(),
|
|
26267
|
+
/** Omitted = `move`, the pre-existing behaviour. */
|
|
26268
|
+
mode: MediaRelocateModeSchema.optional()
|
|
26269
|
+
});
|
|
26270
|
+
var UnstampedEventMediaCountSchema = zod.z.object({
|
|
26271
|
+
media: zod.z.number().int().nonnegative(),
|
|
26272
|
+
retrainFrames: zod.z.number().int().nonnegative(),
|
|
26273
|
+
total: zod.z.number().int().nonnegative()
|
|
26262
26274
|
});
|
|
26263
26275
|
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: zod.z.string().min(1) });
|
|
26264
26276
|
var StorageMigrationClassSchema = zod.z.enum([
|
|
26265
26277
|
"recordings",
|
|
26266
26278
|
"recordingsLow",
|
|
26267
|
-
"eventMedia"
|
|
26279
|
+
"eventMedia",
|
|
26280
|
+
"backups",
|
|
26281
|
+
"galleryMedia"
|
|
26268
26282
|
]);
|
|
26269
26283
|
var StorageMigrationDestinationsSchema = zod.z.object({
|
|
26270
26284
|
recordings: zod.z.string().min(1).optional(),
|
|
26271
26285
|
recordingsLow: zod.z.string().min(1).optional(),
|
|
26272
|
-
eventMedia: zod.z.string().min(1).optional()
|
|
26286
|
+
eventMedia: zod.z.string().min(1).optional(),
|
|
26287
|
+
backups: zod.z.string().min(1).optional(),
|
|
26288
|
+
galleryMedia: zod.z.string().min(1).optional()
|
|
26273
26289
|
}).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
|
|
26290
|
+
var StorageMigrationModeSchema = zod.z.enum(["blocking", "nonBlocking"]);
|
|
26274
26291
|
var StorageMigrationInputSchema = zod.z.object({
|
|
26275
26292
|
destinations: StorageMigrationDestinationsSchema,
|
|
26276
|
-
throttleMbps: zod.z.number().min(1).max(1e3).optional()
|
|
26293
|
+
throttleMbps: zod.z.number().min(1).max(1e3).optional(),
|
|
26294
|
+
/** Omitted = `blocking`, which stays the default. */
|
|
26295
|
+
mode: StorageMigrationModeSchema.optional()
|
|
26277
26296
|
});
|
|
26278
26297
|
var StorageMigrationPhaseSchema = zod.z.enum([
|
|
26279
26298
|
"planning",
|
|
26299
|
+
"sealing",
|
|
26280
26300
|
"pausing",
|
|
26281
26301
|
"moving",
|
|
26302
|
+
"draining",
|
|
26282
26303
|
"verifying",
|
|
26283
26304
|
"repointing",
|
|
26284
26305
|
"refreshing",
|
|
@@ -26303,6 +26324,9 @@ var require_dist_BPlfW_CG = __commonJS({
|
|
|
26303
26324
|
var StorageMigrationJobSchema = zod.z.object({
|
|
26304
26325
|
jobId: zod.z.string(),
|
|
26305
26326
|
phase: StorageMigrationPhaseSchema,
|
|
26327
|
+
/** Which order this job is running. `status` is the only place an operator
|
|
26328
|
+
* can tell a seconds-long cutover from a thirty-hour one. */
|
|
26329
|
+
mode: StorageMigrationModeSchema,
|
|
26306
26330
|
destinations: StorageMigrationDestinationsSchema,
|
|
26307
26331
|
throttleMbps: zod.z.number(),
|
|
26308
26332
|
moves: zod.z.array(StorageMigrationMoveSchema),
|
|
@@ -26315,13 +26339,31 @@ var require_dist_BPlfW_CG = __commonJS({
|
|
|
26315
26339
|
finishedAt: zod.z.number().nullable(),
|
|
26316
26340
|
error: zod.z.string().nullable()
|
|
26317
26341
|
});
|
|
26342
|
+
var StorageMigrationFindingCodeSchema = zod.z.enum([
|
|
26343
|
+
"sharesDeviceWithSource",
|
|
26344
|
+
"deviceIdentityUnknown",
|
|
26345
|
+
"unstampedEventMediaRows",
|
|
26346
|
+
"blockingOnly",
|
|
26347
|
+
"noMover"
|
|
26348
|
+
]);
|
|
26349
|
+
var StorageMigrationFindingSchema = zod.z.object({
|
|
26350
|
+
code: StorageMigrationFindingCodeSchema,
|
|
26351
|
+
storageClass: StorageMigrationClassSchema,
|
|
26352
|
+
/** Human-readable, already carrying the ids and counts. */
|
|
26353
|
+
message: zod.z.string()
|
|
26354
|
+
});
|
|
26318
26355
|
var StorageMigrationPlanSchema = zod.z.object({
|
|
26319
26356
|
destinations: StorageMigrationDestinationsSchema,
|
|
26357
|
+
/** The mode this plan was built for. A plan is only valid for its mode: the
|
|
26358
|
+
* `eventMedia` seal gate and the single-cardinality refusal both depend on
|
|
26359
|
+
* it. */
|
|
26360
|
+
mode: StorageMigrationModeSchema,
|
|
26320
26361
|
moves: zod.z.array(zod.z.object({
|
|
26321
26362
|
storageClass: StorageMigrationClassSchema,
|
|
26322
26363
|
fromLocationId: zod.z.string(),
|
|
26323
26364
|
toLocationId: zod.z.string()
|
|
26324
|
-
}))
|
|
26365
|
+
})),
|
|
26366
|
+
findings: zod.z.array(StorageMigrationFindingSchema)
|
|
26325
26367
|
});
|
|
26326
26368
|
var StorageLocationTypeSchema = zod.z.string().regex(/^[a-z][a-zA-Z0-9-]*$/);
|
|
26327
26369
|
var StorageLocationSchema = zod.z.object({
|
|
@@ -37316,6 +37358,17 @@ var require_dist_BPlfW_CG = __commonJS({
|
|
|
37316
37358
|
kind: "mutation",
|
|
37317
37359
|
auth: "admin"
|
|
37318
37360
|
}),
|
|
37361
|
+
/**
|
|
37362
|
+
* How many media / retrain rows still carry NO `locationId`.
|
|
37363
|
+
*
|
|
37364
|
+
* A NULL row means "wherever `eventMedia` points NOW", so the instant a
|
|
37365
|
+
* repoint moves that pointer every such row reads from the new disk while
|
|
37366
|
+
* its bytes are on the old one — the archive goes dark until a drain
|
|
37367
|
+
* happens to stamp it. This count is what the migration planner's
|
|
37368
|
+
* non-blocking gate reads; `relocateMedia({ mode: 'seal' })` is what drives
|
|
37369
|
+
* it to zero.
|
|
37370
|
+
*/
|
|
37371
|
+
countUnstampedEventMedia: method(zod.z.object({}), UnstampedEventMediaCountSchema, { auth: "admin" }),
|
|
37319
37372
|
/** Every relocate job this addon knows about, newest first (in RAM: the
|
|
37320
37373
|
* move is resumable, so a lost list costs nothing but the display). */
|
|
37321
37374
|
listRelocateMediaJobs: method(zod.z.object({}), zod.z.array(RelocateJobSchema).readonly(), {
|
|
@@ -39768,7 +39821,24 @@ var require_dist_BPlfW_CG = __commonJS({
|
|
|
39768
39821
|
kind: "mutation",
|
|
39769
39822
|
auth: "admin"
|
|
39770
39823
|
}),
|
|
39771
|
-
|
|
39824
|
+
/**
|
|
39825
|
+
* Remove a location record. REFUSES a location that still holds data —
|
|
39826
|
+
* deleting a drained location whose durable rows still name it is how this
|
|
39827
|
+
* hub acquired 2 131 ghost `recordings:high` segments, and playback does
|
|
39828
|
+
* not stat, so the operator sees a silent black window rather than an
|
|
39829
|
+
* error.
|
|
39830
|
+
*
|
|
39831
|
+
* `force` exists because the occupancy check is BEST-EFFORT and refuses on
|
|
39832
|
+
* "unknown" as well as on "occupied" (a read that fails must not authorise
|
|
39833
|
+
* a destruction — D49). A location on a removed disk, on another node, or
|
|
39834
|
+
* behind a remote provider answers "unknown" forever, and a refusal an
|
|
39835
|
+
* operator cannot override is its own failure mode. `force: true` is
|
|
39836
|
+
* logged, loudly, with what the check saw.
|
|
39837
|
+
*/
|
|
39838
|
+
deleteLocation: method(zod.z.object({
|
|
39839
|
+
id: zod.z.string(),
|
|
39840
|
+
force: zod.z.boolean().optional()
|
|
39841
|
+
}), zod.z.void(), {
|
|
39772
39842
|
kind: "mutation",
|
|
39773
39843
|
auth: "admin"
|
|
39774
39844
|
}),
|
|
@@ -52862,6 +52932,12 @@ var require_dist_BPlfW_CG = __commonJS({
|
|
|
52862
52932
|
addonId: null,
|
|
52863
52933
|
access: "create"
|
|
52864
52934
|
},
|
|
52935
|
+
"pipelineAnalytics.countUnstampedEventMedia": {
|
|
52936
|
+
capName: "pipeline-analytics",
|
|
52937
|
+
capScope: "device",
|
|
52938
|
+
addonId: null,
|
|
52939
|
+
access: "view"
|
|
52940
|
+
},
|
|
52865
52941
|
"pipelineAnalytics.deleteDeviceEvents": {
|
|
52866
52942
|
capName: "pipeline-analytics",
|
|
52867
52943
|
capScope: "device",
|
|
@@ -58713,7 +58789,7 @@ var require_alerts_addon = __commonJS({
|
|
|
58713
58789
|
[Symbol.toStringTag]: { value: "Module" }
|
|
58714
58790
|
});
|
|
58715
58791
|
require_chunk_Cek0wNdY();
|
|
58716
|
-
var require_dist10 =
|
|
58792
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
58717
58793
|
function selectExpired(alerts, cutoffMs) {
|
|
58718
58794
|
return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
|
|
58719
58795
|
}
|
|
@@ -59532,7 +59608,7 @@ var require_console_logging = __commonJS({
|
|
|
59532
59608
|
[Symbol.toStringTag]: { value: "Module" }
|
|
59533
59609
|
});
|
|
59534
59610
|
require_chunk_Cek0wNdY();
|
|
59535
|
-
var require_dist10 =
|
|
59611
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
59536
59612
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
59537
59613
|
var LEVEL_RANK = {
|
|
59538
59614
|
debug: 0,
|
|
@@ -59626,7 +59702,7 @@ var require_core_blocks_addon = __commonJS({
|
|
|
59626
59702
|
"use strict";
|
|
59627
59703
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
59628
59704
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
59629
|
-
var require_dist10 =
|
|
59705
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
59630
59706
|
var node_crypto = __require("crypto");
|
|
59631
59707
|
var node_fs_promises = __require("fs/promises");
|
|
59632
59708
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -60523,11 +60599,11 @@ var require_core_blocks = __commonJS({
|
|
|
60523
60599
|
}
|
|
60524
60600
|
});
|
|
60525
60601
|
|
|
60526
|
-
// ../system/dist/retired-settings-keys-
|
|
60527
|
-
var
|
|
60528
|
-
"../system/dist/retired-settings-keys-
|
|
60602
|
+
// ../system/dist/retired-settings-keys-BfAzWvPC.js
|
|
60603
|
+
var require_retired_settings_keys_BfAzWvPC = __commonJS({
|
|
60604
|
+
"../system/dist/retired-settings-keys-BfAzWvPC.js"(exports) {
|
|
60529
60605
|
"use strict";
|
|
60530
|
-
var require_dist10 =
|
|
60606
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
60531
60607
|
function settingsStoreIsAuthoritativeHere(env) {
|
|
60532
60608
|
const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
|
|
60533
60609
|
return raw === "" || raw === "hub";
|
|
@@ -62741,8 +62817,8 @@ var require_device_manager_addon = __commonJS({
|
|
|
62741
62817
|
[Symbol.toStringTag]: { value: "Module" }
|
|
62742
62818
|
});
|
|
62743
62819
|
require_chunk_Cek0wNdY();
|
|
62744
|
-
var require_dist10 =
|
|
62745
|
-
var require_retired_settings_keys =
|
|
62820
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
62821
|
+
var require_retired_settings_keys = require_retired_settings_keys_BfAzWvPC();
|
|
62746
62822
|
var node_crypto = __require("crypto");
|
|
62747
62823
|
var _camstack_types_node = require_node();
|
|
62748
62824
|
var JOB_HISTORY = 20;
|
|
@@ -67491,7 +67567,7 @@ var require_hub_forwarder = __commonJS({
|
|
|
67491
67567
|
[Symbol.toStringTag]: { value: "Module" }
|
|
67492
67568
|
});
|
|
67493
67569
|
require_chunk_Cek0wNdY();
|
|
67494
|
-
var require_dist10 =
|
|
67570
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
67495
67571
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
67496
67572
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
67497
67573
|
var HubForwarderDestination = class {
|
|
@@ -67628,7 +67704,7 @@ var require_liveness_monitor_addon = __commonJS({
|
|
|
67628
67704
|
"use strict";
|
|
67629
67705
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
67630
67706
|
require_chunk_Cek0wNdY();
|
|
67631
|
-
var require_dist10 =
|
|
67707
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
67632
67708
|
var NO_DEVICES = "liveness:no-devices";
|
|
67633
67709
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
67634
67710
|
var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
|
|
@@ -67818,7 +67894,7 @@ var require_local_auth_addon = __commonJS({
|
|
|
67818
67894
|
[Symbol.toStringTag]: { value: "Module" }
|
|
67819
67895
|
});
|
|
67820
67896
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
67821
|
-
var require_dist10 =
|
|
67897
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
67822
67898
|
var node_crypto = __require("crypto");
|
|
67823
67899
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
67824
67900
|
var crypto$1 = __require("crypto");
|
|
@@ -75631,7 +75707,7 @@ var require_loki_logging = __commonJS({
|
|
|
75631
75707
|
[Symbol.toStringTag]: { value: "Module" }
|
|
75632
75708
|
});
|
|
75633
75709
|
require_chunk_Cek0wNdY();
|
|
75634
|
-
var require_dist10 =
|
|
75710
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
75635
75711
|
function sanitizeLabelName(raw) {
|
|
75636
75712
|
const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
|
|
75637
75713
|
return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
|
|
@@ -76196,7 +76272,7 @@ var require_native_metrics_addon = __commonJS({
|
|
|
76196
76272
|
[Symbol.toStringTag]: { value: "Module" }
|
|
76197
76273
|
});
|
|
76198
76274
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
76199
|
-
var require_dist10 =
|
|
76275
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
76200
76276
|
var node_fs_promises = __require("fs/promises");
|
|
76201
76277
|
var node_child_process = __require("child_process");
|
|
76202
76278
|
var node_util = __require("util");
|
|
@@ -78818,7 +78894,7 @@ var require_filesystem_storage_addon = __commonJS({
|
|
|
78818
78894
|
[Symbol.toStringTag]: { value: "Module" }
|
|
78819
78895
|
});
|
|
78820
78896
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
78821
|
-
var require_dist10 =
|
|
78897
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
78822
78898
|
var node_crypto = __require("crypto");
|
|
78823
78899
|
var node_fs_promises = __require("fs/promises");
|
|
78824
78900
|
var node_path = __require("path");
|
|
@@ -79934,8 +80010,8 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
79934
80010
|
[Symbol.toStringTag]: { value: "Module" }
|
|
79935
80011
|
});
|
|
79936
80012
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
79937
|
-
var require_dist10 =
|
|
79938
|
-
var require_retired_settings_keys =
|
|
80013
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
80014
|
+
var require_retired_settings_keys = require_retired_settings_keys_BfAzWvPC();
|
|
79939
80015
|
var node_crypto = __require("crypto");
|
|
79940
80016
|
var node_fs = __require("fs");
|
|
79941
80017
|
var node_module = __require("module");
|
|
@@ -82214,7 +82290,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82214
82290
|
[Symbol.toStringTag]: { value: "Module" }
|
|
82215
82291
|
});
|
|
82216
82292
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
82217
|
-
var require_dist10 =
|
|
82293
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
82218
82294
|
var node_crypto = __require("crypto");
|
|
82219
82295
|
var node_fs_promises = __require("fs/promises");
|
|
82220
82296
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -82397,8 +82473,19 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82397
82473
|
var STORAGE_CLASSES = [
|
|
82398
82474
|
"recordings",
|
|
82399
82475
|
"recordingsLow",
|
|
82400
|
-
"eventMedia"
|
|
82476
|
+
"eventMedia",
|
|
82477
|
+
"backups",
|
|
82478
|
+
"galleryMedia"
|
|
82479
|
+
];
|
|
82480
|
+
var MOVER_CLASSES = [
|
|
82481
|
+
"recordings",
|
|
82482
|
+
"recordingsLow",
|
|
82483
|
+
"eventMedia",
|
|
82484
|
+
"galleryMedia"
|
|
82401
82485
|
];
|
|
82486
|
+
var BLOCKING_ONLY_CLASSES = ["galleryMedia"];
|
|
82487
|
+
var GALLERY_FORBIDDEN_NEIGHBOUR_TYPES = ["recordings", "recordingsLow"];
|
|
82488
|
+
var MOVE_LANES = ["media", "footage"];
|
|
82402
82489
|
var PARTICIPANTS = [
|
|
82403
82490
|
"pipeline",
|
|
82404
82491
|
"recorder",
|
|
@@ -82418,7 +82505,9 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82418
82505
|
this.deps = deps;
|
|
82419
82506
|
}
|
|
82420
82507
|
async plan(input) {
|
|
82508
|
+
const mode = input.mode ?? "blocking";
|
|
82421
82509
|
const moves = [];
|
|
82510
|
+
const findings = [];
|
|
82422
82511
|
for (const storageClass of STORAGE_CLASSES) {
|
|
82423
82512
|
const targetId = input.destinations[storageClass];
|
|
82424
82513
|
if (targetId === void 0) continue;
|
|
@@ -82428,6 +82517,9 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82428
82517
|
if (!target) throw new Error(`Storage location "${targetId}" not found`);
|
|
82429
82518
|
if (target.type !== storageClass) throw new Error(`Storage location "${targetId}" is type "${target.type}", expected "${storageClass}"`);
|
|
82430
82519
|
if (source.id === target.id) throw new Error(`Storage location "${targetId}" is already the "${storageClass}" default`);
|
|
82520
|
+
if (!MOVER_CLASSES.includes(storageClass)) throw new Error(`No mover owns "${storageClass}" \u2014 the migration can repoint its default but cannot move its bytes. Move it by hand, then repoint the default with upsertLocation.`);
|
|
82521
|
+
if (mode === "nonBlocking" && BLOCKING_ONLY_CLASSES.includes(storageClass)) throw new Error(`"${storageClass}" is a single-cardinality storage class: it can never exist at two locations at once, so it cannot be repointed first and drained behind. Use mode "blocking".`);
|
|
82522
|
+
await this.refuseGalleryOntoRecordingsVolume(storageClass, targetId);
|
|
82431
82523
|
moves.push({
|
|
82432
82524
|
storageClass,
|
|
82433
82525
|
fromLocationId: source.id,
|
|
@@ -82436,17 +82528,79 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82436
82528
|
state: null,
|
|
82437
82529
|
error: null
|
|
82438
82530
|
});
|
|
82531
|
+
if (BLOCKING_ONLY_CLASSES.includes(storageClass)) findings.push({
|
|
82532
|
+
code: "blockingOnly",
|
|
82533
|
+
storageClass,
|
|
82534
|
+
message: `"${storageClass}" is single-cardinality: this move is stop-the-world for its whole duration, in every mode. It is a few KB per enrolled sample, which is why that is acceptable.`
|
|
82535
|
+
});
|
|
82536
|
+
findings.push(...await this.deviceFindings(storageClass, source.id, target.id));
|
|
82439
82537
|
}
|
|
82440
82538
|
if (moves.length === 0) throw new Error("select at least one storage class");
|
|
82539
|
+
if (moves.some((move) => move.storageClass === "eventMedia")) {
|
|
82540
|
+
const unstamped = await this.deps.participants.analytics.countUnstamped();
|
|
82541
|
+
if (unstamped.total > 0) {
|
|
82542
|
+
if (mode === "nonBlocking") throw new Error(`${unstamped.total.toString()} event-media row(s) still carry no locationId (${unstamped.media.toString()} media, ${unstamped.retrainFrames.toString()} retrain frames). A non-blocking cutover would silently orphan them. Run the seal first: pipelineAnalytics.relocateMedia({ mode: "seal", toLocationId: "${this.deps.locations.getDefaultLocation("eventMedia")?.id ?? "eventMedia"}" }).`);
|
|
82543
|
+
findings.push({
|
|
82544
|
+
code: "unstampedEventMediaRows",
|
|
82545
|
+
storageClass: "eventMedia",
|
|
82546
|
+
message: `${unstamped.total.toString()} event-media row(s) carry no locationId. This blocking migration stamps them as it moves them, but a non-blocking one would be refused until a seal pass drives the count to zero.`
|
|
82547
|
+
});
|
|
82548
|
+
}
|
|
82549
|
+
}
|
|
82441
82550
|
return {
|
|
82442
82551
|
destinations: input.destinations,
|
|
82552
|
+
mode,
|
|
82443
82553
|
moves: moves.map(({ storageClass, fromLocationId, toLocationId }) => ({
|
|
82444
82554
|
storageClass,
|
|
82445
82555
|
fromLocationId,
|
|
82446
82556
|
toLocationId
|
|
82447
|
-
}))
|
|
82557
|
+
})),
|
|
82558
|
+
findings
|
|
82448
82559
|
};
|
|
82449
82560
|
}
|
|
82561
|
+
/**
|
|
82562
|
+
* HARD refusal: `galleryMedia` onto a volume a recordings class lives on.
|
|
82563
|
+
*
|
|
82564
|
+
* This is the one same-device case that is a refusal rather than a finding,
|
|
82565
|
+
* because its consequence is silent and arrives months later — the gallery is
|
|
82566
|
+
* wiped by the next footage wipe, quota eviction or recordings disk swap, and
|
|
82567
|
+
* nothing at that moment points back at this migration.
|
|
82568
|
+
*
|
|
82569
|
+
* It can only refuse what it can SEE. `deviceKeyOf` is a realpath, so it
|
|
82570
|
+
* catches a symlink, a bind mount and a literally-shared directory — and
|
|
82571
|
+
* misses two distinct directories on one filesystem, and any location pinned
|
|
82572
|
+
* to another node. Those come back `null`/distinct and produce a
|
|
82573
|
+
* `deviceIdentityUnknown` finding instead.
|
|
82574
|
+
*/
|
|
82575
|
+
async refuseGalleryOntoRecordingsVolume(storageClass, targetId) {
|
|
82576
|
+
if (storageClass !== "galleryMedia") return;
|
|
82577
|
+
const targetKey = await this.deviceKey(targetId);
|
|
82578
|
+
if (targetKey === null) return;
|
|
82579
|
+
for (const type of GALLERY_FORBIDDEN_NEIGHBOUR_TYPES) for (const neighbour of this.deps.locations.listLocations({ type })) {
|
|
82580
|
+
if (await this.deviceKey(neighbour.id) !== targetKey) continue;
|
|
82581
|
+
throw new Error(`Storage location "${targetId}" shares a volume with "${neighbour.id}" (${type}). galleryMedia is deliberately NOT on the recordings volume: enrolment images survive every retention sweep, so they must also survive a footage wipe, a quota eviction and a recordings disk swap. Choose a destination on another volume.`);
|
|
82582
|
+
}
|
|
82583
|
+
}
|
|
82584
|
+
/** Same-device observations that are reported, not refused. */
|
|
82585
|
+
async deviceFindings(storageClass, sourceId, targetId) {
|
|
82586
|
+
if (this.deps.deviceKeyOf === void 0) return [];
|
|
82587
|
+
const [sourceKey, targetKey] = await Promise.all([this.deviceKey(sourceId), this.deviceKey(targetId)]);
|
|
82588
|
+
if (sourceKey === null || targetKey === null) return [{
|
|
82589
|
+
code: "deviceIdentityUnknown",
|
|
82590
|
+
storageClass,
|
|
82591
|
+
message: `Could not establish whether "${sourceId}" and "${targetId}" are on the same device (a location with no basePath, or pinned to another node \u2014 the realpath would have to be taken there). Note also that this check is PATH identity, not st_dev: two distinct directories on one filesystem are never detected as one device.`
|
|
82592
|
+
}];
|
|
82593
|
+
if (sourceKey !== targetKey) return [];
|
|
82594
|
+
return [{
|
|
82595
|
+
code: "sharesDeviceWithSource",
|
|
82596
|
+
storageClass,
|
|
82597
|
+
message: `"${targetId}" resolves to the same path as "${sourceId}". The move will re-stamp rows rather than copy bytes, and it buys no redundancy \u2014 both "locations" are one disk.`
|
|
82598
|
+
}];
|
|
82599
|
+
}
|
|
82600
|
+
async deviceKey(locationId) {
|
|
82601
|
+
if (this.deps.deviceKeyOf === void 0) return null;
|
|
82602
|
+
return this.deps.deviceKeyOf(locationId);
|
|
82603
|
+
}
|
|
82450
82604
|
async start(input) {
|
|
82451
82605
|
if (this.startReserved) throw new Error("storage migration is already active");
|
|
82452
82606
|
this.startReserved = true;
|
|
@@ -82463,6 +82617,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82463
82617
|
const job = {
|
|
82464
82618
|
jobId: this.deps.newId(),
|
|
82465
82619
|
phase: "planning",
|
|
82620
|
+
mode: plan.mode,
|
|
82466
82621
|
destinations: input.destinations,
|
|
82467
82622
|
throttleMbps: input.throttleMbps ?? 40,
|
|
82468
82623
|
moves: plan.moves.map((move) => ({
|
|
@@ -82496,7 +82651,8 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82496
82651
|
}
|
|
82497
82652
|
async cancel(jobId) {
|
|
82498
82653
|
const job = await this.status(jobId);
|
|
82499
|
-
|
|
82654
|
+
const cutoverInFlight = job !== null && job.repointed && (job.phase === "refreshing" || job.phase === "resuming");
|
|
82655
|
+
if (!job || isTerminal(job) || cutoverInFlight) return false;
|
|
82500
82656
|
job.cancelRequested = true;
|
|
82501
82657
|
await this.persist(job);
|
|
82502
82658
|
await Promise.all(job.moves.filter((move) => move.moverJobId !== null).map((move) => this.cancelMove(move)));
|
|
@@ -82527,7 +82683,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82527
82683
|
job.pauseLeaseId ??= this.deps.newId();
|
|
82528
82684
|
if (job.repointed && job.phase === "resuming") job.phase = "refreshing";
|
|
82529
82685
|
await this.persist(job);
|
|
82530
|
-
try {
|
|
82686
|
+
if (!runsUnleased(job)) try {
|
|
82531
82687
|
await this.pauseParticipants(job, true);
|
|
82532
82688
|
} catch (err) {
|
|
82533
82689
|
job.error = err instanceof Error ? err.message : String(err);
|
|
@@ -82539,14 +82695,34 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82539
82695
|
this.runPromise;
|
|
82540
82696
|
}
|
|
82541
82697
|
}
|
|
82698
|
+
/**
|
|
82699
|
+
* The state machine. Both modes run the SAME phase handlers; only the order
|
|
82700
|
+
* differs, which is the whole of the non-blocking design — no second mover,
|
|
82701
|
+
* no parallel code path, nothing that can drift between the two.
|
|
82702
|
+
*
|
|
82703
|
+
* blocking: planning → pausing → moving → verifying → repointing →
|
|
82704
|
+
* refreshing → resuming → done
|
|
82705
|
+
* nonBlocking: planning → sealing → pausing → repointing → refreshing →
|
|
82706
|
+
* resuming → draining → verifying → done
|
|
82707
|
+
*
|
|
82708
|
+
* Written as a straight-line sequence of `if (phase === X)` blocks so boot
|
|
82709
|
+
* recovery re-enters at whatever phase the durable record says, exactly as
|
|
82710
|
+
* before.
|
|
82711
|
+
*/
|
|
82542
82712
|
async run(job) {
|
|
82713
|
+
const nonBlocking = job.mode === "nonBlocking";
|
|
82543
82714
|
try {
|
|
82544
|
-
if (job.phase === "planning") await this.setPhase(job, "pausing");
|
|
82715
|
+
if (job.phase === "planning") await this.setPhase(job, nonBlocking ? "sealing" : "pausing");
|
|
82716
|
+
if (job.phase === "sealing") {
|
|
82717
|
+
await this.sealEventMedia(job);
|
|
82718
|
+
if (job.cancelRequested) return this.finishCancelled(job);
|
|
82719
|
+
await this.setPhase(job, "pausing");
|
|
82720
|
+
}
|
|
82545
82721
|
if (job.phase === "pausing") {
|
|
82546
82722
|
job.pauseLeaseId ??= job.jobId;
|
|
82547
82723
|
await this.pauseParticipants(job);
|
|
82548
82724
|
if (job.cancelRequested) return this.finishCancelled(job);
|
|
82549
|
-
await this.setPhase(job, "moving");
|
|
82725
|
+
await this.setPhase(job, nonBlocking ? "repointing" : "moving");
|
|
82550
82726
|
}
|
|
82551
82727
|
if (job.phase === "moving") {
|
|
82552
82728
|
await this.startOrResumeMoves(job);
|
|
@@ -82554,7 +82730,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82554
82730
|
if (job.cancelRequested) return this.finishCancelled(job);
|
|
82555
82731
|
await this.setPhase(job, "verifying");
|
|
82556
82732
|
}
|
|
82557
|
-
if (job.phase === "verifying") {
|
|
82733
|
+
if (job.phase === "verifying" && !nonBlocking) {
|
|
82558
82734
|
await this.verifyMoves(job);
|
|
82559
82735
|
if (job.cancelRequested) return this.finishCancelled(job);
|
|
82560
82736
|
await this.setPhase(job, "repointing");
|
|
@@ -82567,17 +82743,28 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82567
82743
|
}
|
|
82568
82744
|
if (job.phase === "refreshing") {
|
|
82569
82745
|
const leaseId = requireLease(job);
|
|
82570
|
-
if (job.moves.some((move) => move.storageClass
|
|
82571
|
-
if (job.moves.some((move) => move.storageClass === "
|
|
82746
|
+
if (job.moves.some((move) => laneOf(move.storageClass) === "footage")) await this.deps.participants.recorder.refresh(leaseId);
|
|
82747
|
+
if (job.moves.some((move) => laneOf(move.storageClass) === "media")) await this.deps.participants.analytics.refresh(leaseId);
|
|
82572
82748
|
await this.setPhase(job, "resuming");
|
|
82573
82749
|
}
|
|
82574
82750
|
if (job.phase === "resuming") {
|
|
82575
82751
|
await this.resumeParticipants(job);
|
|
82752
|
+
await this.setPhase(job, nonBlocking ? "draining" : "done");
|
|
82753
|
+
}
|
|
82754
|
+
if (job.phase === "draining") {
|
|
82755
|
+
await this.startOrResumeMoves(job);
|
|
82756
|
+
await this.waitForMoves(job);
|
|
82757
|
+
if (job.cancelRequested) return this.finishCancelled(job);
|
|
82758
|
+
await this.setPhase(job, "verifying");
|
|
82759
|
+
}
|
|
82760
|
+
if (job.phase === "verifying" && nonBlocking) {
|
|
82761
|
+
await this.verifyMoves(job);
|
|
82762
|
+
if (job.cancelRequested) return this.finishCancelled(job);
|
|
82576
82763
|
await this.setPhase(job, "done");
|
|
82577
82764
|
}
|
|
82578
82765
|
} catch (err) {
|
|
82579
82766
|
job.error = err instanceof Error ? err.message : String(err);
|
|
82580
|
-
if (job.repointed) {
|
|
82767
|
+
if (job.repointed && job.pausedParticipants.length > 0) {
|
|
82581
82768
|
job.phase = "refreshing";
|
|
82582
82769
|
job.finishedAt = null;
|
|
82583
82770
|
await this.persist(job);
|
|
@@ -82626,21 +82813,28 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82626
82813
|
}
|
|
82627
82814
|
}
|
|
82628
82815
|
}
|
|
82816
|
+
/**
|
|
82817
|
+
* Start whatever is startable, honouring the ONE constraint the engines
|
|
82818
|
+
* impose: each is single-flight.
|
|
82819
|
+
*
|
|
82820
|
+
* Two lanes — the recorder's footage engine and post-analysis's media engine
|
|
82821
|
+
* — run in parallel with each other and strictly FIFO within themselves.
|
|
82822
|
+
* `galleryMedia` shares the media lane with `eventMedia` because it shares
|
|
82823
|
+
* the same `MediaRelocateEngine`, which refuses a second running job; starting
|
|
82824
|
+
* both would fail the second one instantly.
|
|
82825
|
+
*/
|
|
82629
82826
|
async startOrResumeMoves(job) {
|
|
82630
82827
|
if (job.cancelRequested) return;
|
|
82631
|
-
const
|
|
82632
|
-
|
|
82633
|
-
|
|
82634
|
-
|
|
82635
|
-
|
|
82636
|
-
|
|
82637
|
-
|
|
82638
|
-
|
|
82639
|
-
|
|
82640
|
-
|
|
82641
|
-
move.moverJobId = await this.startMove(job, move);
|
|
82642
|
-
await this.persist(job);
|
|
82643
|
-
return;
|
|
82828
|
+
for (const lane of MOVE_LANES) {
|
|
82829
|
+
const laneMoves = job.moves.filter((move) => laneOf(move.storageClass) === lane);
|
|
82830
|
+
for (let index = 0; index < laneMoves.length; index++) {
|
|
82831
|
+
const move = laneMoves[index];
|
|
82832
|
+
if (move === void 0 || move.moverJobId !== null) continue;
|
|
82833
|
+
if (!laneMoves.slice(0, index).every((predecessor) => predecessor.state === "done")) break;
|
|
82834
|
+
move.moverJobId = await this.startMove(job, move);
|
|
82835
|
+
await this.persist(job);
|
|
82836
|
+
break;
|
|
82837
|
+
}
|
|
82644
82838
|
}
|
|
82645
82839
|
}
|
|
82646
82840
|
async waitForMoves(job) {
|
|
@@ -82651,7 +82845,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82651
82845
|
let complete = true;
|
|
82652
82846
|
for (const [index, move] of job.moves.entries()) {
|
|
82653
82847
|
if (job.cancelRequested && move.moverJobId === null) continue;
|
|
82654
|
-
if (
|
|
82848
|
+
if (job.moves.slice(0, index).filter((predecessor) => laneOf(predecessor.storageClass) === laneOf(move.storageClass)).some((predecessor) => predecessor.state !== "done")) {
|
|
82655
82849
|
complete = false;
|
|
82656
82850
|
continue;
|
|
82657
82851
|
}
|
|
@@ -82685,30 +82879,92 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82685
82879
|
await sleep(pollMs);
|
|
82686
82880
|
}
|
|
82687
82881
|
}
|
|
82882
|
+
/**
|
|
82883
|
+
* Nail every unstamped `eventMedia`/retrain row to the location its bytes are
|
|
82884
|
+
* ALREADY on, before anything repoints. Runs with every writer up — it moves
|
|
82885
|
+
* no bytes at all.
|
|
82886
|
+
*
|
|
82887
|
+
* The gate is the RE-COUNT, not the seal job's terminal state: a seal that
|
|
82888
|
+
* failed some rows still finishes, and "finished" is not "there are none
|
|
82889
|
+
* left". A non-zero count here fails the job while nothing has been paused
|
|
82890
|
+
* and nothing has been repointed, which is the cheapest possible place to
|
|
82891
|
+
* discover it.
|
|
82892
|
+
*
|
|
82893
|
+
* The seal's own mover job id is deliberately NOT durable. It is idempotent
|
|
82894
|
+
* and cheap, so a coordinator restart mid-seal simply re-runs the whole
|
|
82895
|
+
* phase — and the count that follows is what actually decides, so a lost job
|
|
82896
|
+
* id cannot make the gate pass.
|
|
82897
|
+
*/
|
|
82898
|
+
async sealEventMedia(job) {
|
|
82899
|
+
const move = job.moves.find((candidate) => candidate.storageClass === "eventMedia");
|
|
82900
|
+
if (move === void 0) return;
|
|
82901
|
+
if ((await this.deps.participants.analytics.countUnstamped()).total > 0) {
|
|
82902
|
+
const started = await this.deps.participants.analytics.startDrain({
|
|
82903
|
+
toLocationId: move.fromLocationId,
|
|
82904
|
+
mode: "seal"
|
|
82905
|
+
});
|
|
82906
|
+
await this.waitForSeal(job, started.jobId);
|
|
82907
|
+
if (job.cancelRequested) return;
|
|
82908
|
+
}
|
|
82909
|
+
const after = await this.deps.participants.analytics.countUnstamped();
|
|
82910
|
+
if (after.total > 0) throw new Error(`event-media seal left ${after.total.toString()} row(s) without a locationId (${after.media.toString()} media, ${after.retrainFrames.toString()} retrain frames). A non-blocking cutover would silently orphan them; nothing has been paused or repointed.`);
|
|
82911
|
+
}
|
|
82912
|
+
async waitForSeal(job, moverJobId) {
|
|
82913
|
+
const sleep = this.deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
82914
|
+
const pollMs = this.deps.pollMs ?? 250;
|
|
82915
|
+
for (; ; ) {
|
|
82916
|
+
if (job.cancelRequested) {
|
|
82917
|
+
await this.deps.participants.analytics.cancelMove(moverJobId);
|
|
82918
|
+
return;
|
|
82919
|
+
}
|
|
82920
|
+
const status = await this.deps.participants.analytics.getMove(moverJobId);
|
|
82921
|
+
if (status === null || status.state === "done" || status.state === "cancelled") return;
|
|
82922
|
+
if (status.state === "failed") throw new Error(status.error ?? "event-media seal failed");
|
|
82923
|
+
await sleep(pollMs);
|
|
82924
|
+
}
|
|
82925
|
+
}
|
|
82688
82926
|
async verifyMoves(job) {
|
|
82689
82927
|
for (const move of job.moves) if (move.state !== "done") throw new Error(`${move.storageClass} move did not complete verification`);
|
|
82690
82928
|
}
|
|
82929
|
+
/**
|
|
82930
|
+
* Arm one class's mover.
|
|
82931
|
+
*
|
|
82932
|
+
* `draining` runs after every writer has been resumed, so there is no lease
|
|
82933
|
+
* left to present — it goes through the participants' UNLEASED entry points
|
|
82934
|
+
* (`recording.relocateFootage` / `pipelineAnalytics.relocateMedia`), which
|
|
82935
|
+
* are the same engines, not a second mover.
|
|
82936
|
+
*/
|
|
82691
82937
|
async startMove(job, move) {
|
|
82692
|
-
|
|
82693
|
-
|
|
82694
|
-
|
|
82695
|
-
|
|
82696
|
-
|
|
82697
|
-
|
|
82938
|
+
const unleased = runsUnleased(job);
|
|
82939
|
+
if (laneOf(move.storageClass) === "media") {
|
|
82940
|
+
const input2 = {
|
|
82941
|
+
toLocationId: move.toLocationId,
|
|
82942
|
+
throttleMbps: job.throttleMbps,
|
|
82943
|
+
mode: move.storageClass === "galleryMedia" ? "gallery" : "move"
|
|
82944
|
+
};
|
|
82945
|
+
return (unleased ? await this.deps.participants.analytics.startDrain(input2) : await this.deps.participants.analytics.startMove({
|
|
82946
|
+
...input2,
|
|
82947
|
+
leaseId: requireLease(job)
|
|
82948
|
+
})).jobId;
|
|
82949
|
+
}
|
|
82950
|
+
const input = {
|
|
82698
82951
|
fromLocationId: move.fromLocationId,
|
|
82699
82952
|
toLocationId: move.toLocationId,
|
|
82700
|
-
footageClass: move.storageClass,
|
|
82701
|
-
throttleMbps: job.throttleMbps
|
|
82953
|
+
footageClass: move.storageClass === "recordingsLow" ? "recordingsLow" : "recordings",
|
|
82954
|
+
throttleMbps: job.throttleMbps
|
|
82955
|
+
};
|
|
82956
|
+
return (unleased ? await this.deps.participants.recorder.startDrain(input) : await this.deps.participants.recorder.startMove({
|
|
82957
|
+
...input,
|
|
82702
82958
|
leaseId: requireLease(job)
|
|
82703
82959
|
})).jobId;
|
|
82704
82960
|
}
|
|
82705
82961
|
async moveStatus(move) {
|
|
82706
82962
|
if (move.moverJobId === null) return null;
|
|
82707
|
-
return move.storageClass === "
|
|
82963
|
+
return laneOf(move.storageClass) === "media" ? this.deps.participants.analytics.getMove(move.moverJobId) : this.deps.participants.recorder.getMove(move.moverJobId);
|
|
82708
82964
|
}
|
|
82709
82965
|
async cancelMove(move) {
|
|
82710
82966
|
if (move.moverJobId === null) return;
|
|
82711
|
-
if (move.storageClass === "
|
|
82967
|
+
if (laneOf(move.storageClass) === "media") await this.deps.participants.analytics.cancelMove(move.moverJobId);
|
|
82712
82968
|
else await this.deps.participants.recorder.cancelMove(move.moverJobId);
|
|
82713
82969
|
}
|
|
82714
82970
|
participant(name) {
|
|
@@ -82737,6 +82993,13 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82737
82993
|
function isTerminal(job) {
|
|
82738
82994
|
return job.phase === "done" || job.phase === "failed" || job.phase === "cancelled";
|
|
82739
82995
|
}
|
|
82996
|
+
function laneOf(storageClass) {
|
|
82997
|
+
return storageClass === "eventMedia" || storageClass === "galleryMedia" ? "media" : "footage";
|
|
82998
|
+
}
|
|
82999
|
+
function runsUnleased(job) {
|
|
83000
|
+
if (job.phase === "sealing" || job.phase === "draining") return true;
|
|
83001
|
+
return job.phase === "verifying" && job.mode === "nonBlocking";
|
|
83002
|
+
}
|
|
82740
83003
|
function buildStorageLocationRegistry(perAddon) {
|
|
82741
83004
|
const map = /* @__PURE__ */ new Map();
|
|
82742
83005
|
for (const addonDeclarations of perAddon) for (const declaration of addonDeclarations) {
|
|
@@ -82842,6 +83105,14 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82842
83105
|
* early-boot path that predates the resolver wiring.
|
|
82843
83106
|
*/
|
|
82844
83107
|
nodeLocalResolver = null;
|
|
83108
|
+
/**
|
|
83109
|
+
* Injected occupancy probe (see {@link LocationOccupancyProbe}). `null` until
|
|
83110
|
+
* {@link setOccupancyProbe} runs, and a `null` probe answers `unknown` — so a
|
|
83111
|
+
* service with no probe REFUSES an unforced delete rather than performing one
|
|
83112
|
+
* it could not check. The check is the point; a check that defaults to
|
|
83113
|
+
* "permit" is a paragraph, not a guard.
|
|
83114
|
+
*/
|
|
83115
|
+
occupancyProbe = null;
|
|
82845
83116
|
/** `localNodeId` with any forked-child `/addon` suffix stripped. */
|
|
82846
83117
|
localNode;
|
|
82847
83118
|
/**
|
|
@@ -82941,6 +83212,15 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
82941
83212
|
this.nodeLocalResolver = resolver;
|
|
82942
83213
|
}
|
|
82943
83214
|
/**
|
|
83215
|
+
* Inject the "does this location still hold anything" probe read by
|
|
83216
|
+
* {@link deleteLocation}. Absent → every delete sees `unknown` and therefore
|
|
83217
|
+
* REFUSES without `force`, which is the safe direction for a service wired
|
|
83218
|
+
* without one.
|
|
83219
|
+
*/
|
|
83220
|
+
setOccupancyProbe(probe) {
|
|
83221
|
+
this.occupancyProbe = probe;
|
|
83222
|
+
}
|
|
83223
|
+
/**
|
|
82944
83224
|
* The full set of addon-declared storage locations, as aggregated by
|
|
82945
83225
|
* the kernel and injected via {@link setRegistry}. Powers the admin-UI
|
|
82946
83226
|
* Data screen's per-declaration grouping. Empty until the registry is
|
|
@@ -83164,13 +83444,22 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83164
83444
|
* Persistence (Task 6) mirrors the delete asynchronously, with errors
|
|
83165
83445
|
* routed to the logger — see `upsertLocation` for the rationale.
|
|
83166
83446
|
*/
|
|
83167
|
-
deleteLocation(id) {
|
|
83447
|
+
async deleteLocation(id, options) {
|
|
83168
83448
|
const loc = this.locations.get(id);
|
|
83169
83449
|
if (!loc) throw new Error(`Storage location "${id}" not found`);
|
|
83170
83450
|
if (loc.isSystem) throw new Error(`Storage location "${id}" is system-managed and cannot be deleted. Edit its config (path / providerId) instead.`);
|
|
83171
83451
|
if (loc.isDefault) {
|
|
83172
83452
|
if (![...this.locations.values()].find((l) => l.type === loc.type && l.id !== id && l.isDefault)) throw new Error(`Cannot delete default location "${id}" for type "${loc.type}" \u2014 promote another location to default first`);
|
|
83173
83453
|
}
|
|
83454
|
+
const occupancy = await this.probeOccupancy(loc);
|
|
83455
|
+
if (occupancy !== "empty") {
|
|
83456
|
+
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`);
|
|
83457
|
+
this.logger.warn("storage-orchestrator: FORCED delete of a location that was not verified empty", { meta: {
|
|
83458
|
+
id,
|
|
83459
|
+
type: loc.type,
|
|
83460
|
+
occupancy
|
|
83461
|
+
} });
|
|
83462
|
+
}
|
|
83174
83463
|
this.locations.delete(id);
|
|
83175
83464
|
if (this.locationStore) this.locationStore.delete(id).catch((err) => {
|
|
83176
83465
|
this.logger.error("storage-orchestrator: delete persistence failed", { meta: {
|
|
@@ -83180,6 +83469,23 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83180
83469
|
});
|
|
83181
83470
|
}
|
|
83182
83471
|
/**
|
|
83472
|
+
* Run the injected occupancy probe, converting "no probe" and "the probe
|
|
83473
|
+
* threw" into `unknown` rather than into a permit.
|
|
83474
|
+
*/
|
|
83475
|
+
async probeOccupancy(location) {
|
|
83476
|
+
const probe = this.occupancyProbe;
|
|
83477
|
+
if (probe === null) return "unknown";
|
|
83478
|
+
try {
|
|
83479
|
+
return await probe(location);
|
|
83480
|
+
} catch (err) {
|
|
83481
|
+
this.logger.warn("storage-orchestrator: occupancy probe failed", { meta: {
|
|
83482
|
+
id: location.id,
|
|
83483
|
+
error: err instanceof Error ? err.message : String(err)
|
|
83484
|
+
} });
|
|
83485
|
+
return "unknown";
|
|
83486
|
+
}
|
|
83487
|
+
}
|
|
83488
|
+
/**
|
|
83183
83489
|
* Remove system-seeded locations whose type is no longer declared by any
|
|
83184
83490
|
* addon (stale defaults from a removed location type). Operator-added
|
|
83185
83491
|
* (non-system) locations of an undeclared type are KEPT but warned — the
|
|
@@ -83575,6 +83881,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83575
83881
|
});
|
|
83576
83882
|
this.service = service;
|
|
83577
83883
|
service.setNodeLocalResolver((providerId) => this.nodeLocalByProvider.get(providerId));
|
|
83884
|
+
service.setOccupancyProbe((location) => this.locationOccupancy(location));
|
|
83578
83885
|
await service.initialize();
|
|
83579
83886
|
const provider = {
|
|
83580
83887
|
listLocations: async ({ type }) => {
|
|
@@ -83587,8 +83894,8 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83587
83894
|
getDefaultLocation: async ({ type }) => service.getDefaultLocation(type),
|
|
83588
83895
|
listLocationDeclarations: async () => service.listDeclarations(),
|
|
83589
83896
|
upsertLocation: async (input) => service.upsertLocation(input),
|
|
83590
|
-
deleteLocation: async ({ id }) => {
|
|
83591
|
-
service.deleteLocation(id);
|
|
83897
|
+
deleteLocation: async ({ id, force }) => {
|
|
83898
|
+
await service.deleteLocation(id, { force: force === true });
|
|
83592
83899
|
},
|
|
83593
83900
|
testLocation: async ({ id }) => {
|
|
83594
83901
|
const loc = service.getLocationById(id);
|
|
@@ -83751,6 +84058,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83751
84058
|
await this.ctx.api.recording.resumeForStorageMigration.mutate({ leaseId });
|
|
83752
84059
|
},
|
|
83753
84060
|
startMove: (input) => this.ctx.api.recording.startStorageMigrationMove.mutate(input),
|
|
84061
|
+
startDrain: (input) => this.ctx.api.recording.relocateFootage.mutate(input),
|
|
83754
84062
|
getMove: (jobId) => this.ctx.api.recording.getStorageMigrationMoveStatus.query({ jobId }),
|
|
83755
84063
|
cancelMove: async (jobId) => (await this.ctx.api.recording.cancelStorageMigrationMove.mutate({ jobId })).cancelled,
|
|
83756
84064
|
refresh: async (leaseId) => {
|
|
@@ -83765,6 +84073,8 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83765
84073
|
await this.ctx.api.pipelineAnalytics.resumeForStorageMigration.mutate({ leaseId });
|
|
83766
84074
|
},
|
|
83767
84075
|
startMove: (input) => this.ctx.api.pipelineAnalytics.startStorageMigrationMove.mutate(input),
|
|
84076
|
+
startDrain: (input) => this.ctx.api.pipelineAnalytics.relocateMedia.mutate(input),
|
|
84077
|
+
countUnstamped: () => this.ctx.api.pipelineAnalytics.countUnstampedEventMedia.query({}),
|
|
83768
84078
|
getMove: (jobId) => this.ctx.api.pipelineAnalytics.getStorageMigrationMoveStatus.query({ jobId }),
|
|
83769
84079
|
cancelMove: async (jobId) => (await this.ctx.api.pipelineAnalytics.cancelStorageMigrationMove.mutate({ jobId })).cancelled,
|
|
83770
84080
|
refresh: async (leaseId) => {
|
|
@@ -83773,7 +84083,8 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83773
84083
|
}
|
|
83774
84084
|
},
|
|
83775
84085
|
now: () => Date.now(),
|
|
83776
|
-
newId: () => (0, node_crypto.randomUUID)()
|
|
84086
|
+
newId: () => (0, node_crypto.randomUUID)(),
|
|
84087
|
+
deviceKeyOf: (locationId) => this.locationDeviceKey(locationId)
|
|
83777
84088
|
});
|
|
83778
84089
|
this.migration = migration;
|
|
83779
84090
|
const migrationProvider = {
|
|
@@ -83988,6 +84299,44 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
83988
84299
|
return node_path.resolve(bp);
|
|
83989
84300
|
}
|
|
83990
84301
|
}
|
|
84302
|
+
/**
|
|
84303
|
+
* Does this location still hold anything? A SHALLOW `readdir` of its
|
|
84304
|
+
* `basePath` — one syscall, never a walk. `/recordings` holds one entry per
|
|
84305
|
+
* camera, so this is cheap on a volume with millions of segments, and a
|
|
84306
|
+
* `find`-style walk of a saturated recordings disk is precisely what must
|
|
84307
|
+
* not happen here.
|
|
84308
|
+
*
|
|
84309
|
+
* **What it cannot see, stated rather than papered over:**
|
|
84310
|
+
*
|
|
84311
|
+
* - A location on ANOTHER node, or behind a remote provider (S3 / SFTP /
|
|
84312
|
+
* WebDAV): its bytes are not on this filesystem and this probe answers
|
|
84313
|
+
* `unknown`, never `empty`.
|
|
84314
|
+
* - A location with no `basePath` — same answer.
|
|
84315
|
+
* - The difference between "holds live footage" and "holds one stray
|
|
84316
|
+
* lock-file". It measures BYTES ON DISK, not durable rows, so it refuses
|
|
84317
|
+
* a location holding orphan files that no index names. That direction is
|
|
84318
|
+
* the safe one, and `force` is the escape hatch for it.
|
|
84319
|
+
*
|
|
84320
|
+
* A missing root (`ENOENT`) is `empty`, not `unknown`: an unmounted disk and
|
|
84321
|
+
* a deleted directory are indistinguishable here, and if the record's own
|
|
84322
|
+
* path does not exist there is nothing this delete can strand. Every OTHER
|
|
84323
|
+
* errno — `EACCES`, `EIO`, a stale NFS handle — is `unknown` and refuses.
|
|
84324
|
+
*/
|
|
84325
|
+
async locationOccupancy(location) {
|
|
84326
|
+
if ((location.nodeId === void 0 || location.nodeId === "" ? HUB_NODE_ID : location.nodeId) !== (this.service?.getLocalNodeId() ?? HUB_NODE_ID)) return "unknown";
|
|
84327
|
+
const basePath = this.locationBasePath(location.id);
|
|
84328
|
+
if (basePath === null) return "unknown";
|
|
84329
|
+
try {
|
|
84330
|
+
return (await node_fs_promises.readdir(basePath)).length > 0 ? "occupied" : "empty";
|
|
84331
|
+
} catch (err) {
|
|
84332
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") return "empty";
|
|
84333
|
+
this.ctx.logger.warn("storage-orchestrator: could not read a location root to check it", { meta: {
|
|
84334
|
+
id: location.id,
|
|
84335
|
+
error: err instanceof Error ? err.message : String(err)
|
|
84336
|
+
} });
|
|
84337
|
+
return "unknown";
|
|
84338
|
+
}
|
|
84339
|
+
}
|
|
83991
84340
|
/** Free capacity (%) on a location's volume via `statfs`; 100 (guard inert) when unstattable. */
|
|
83992
84341
|
async locationFreePercent(locationId) {
|
|
83993
84342
|
const bp = this.locationBasePath(locationId);
|
|
@@ -84095,7 +84444,7 @@ var require_system_config_addon = __commonJS({
|
|
|
84095
84444
|
[Symbol.toStringTag]: { value: "Module" }
|
|
84096
84445
|
});
|
|
84097
84446
|
require_chunk_Cek0wNdY();
|
|
84098
|
-
var require_dist10 =
|
|
84447
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
84099
84448
|
var SECTION_TITLES = {
|
|
84100
84449
|
server: "Server",
|
|
84101
84450
|
auth: "Authentication"
|
|
@@ -102156,7 +102505,7 @@ var require_winston_logging = __commonJS({
|
|
|
102156
102505
|
[Symbol.toStringTag]: { value: "Module" }
|
|
102157
102506
|
});
|
|
102158
102507
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
102159
|
-
var require_dist10 =
|
|
102508
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
102160
102509
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
102161
102510
|
var node_path = __require("path");
|
|
102162
102511
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -104099,9 +104448,9 @@ var require_event_category_BaEgqJNv = __commonJS({
|
|
|
104099
104448
|
}
|
|
104100
104449
|
});
|
|
104101
104450
|
|
|
104102
|
-
// ../types/dist/sleep-
|
|
104103
|
-
var
|
|
104104
|
-
"../types/dist/sleep-
|
|
104451
|
+
// ../types/dist/sleep-CJrvRDlD.js
|
|
104452
|
+
var require_sleep_CJrvRDlD = __commonJS({
|
|
104453
|
+
"../types/dist/sleep-CJrvRDlD.js"(exports) {
|
|
104105
104454
|
"use strict";
|
|
104106
104455
|
var require_event_category = require_event_category_BaEgqJNv();
|
|
104107
104456
|
var zod = require_zod();
|
|
@@ -106801,6 +107150,7 @@ var require_sleep_DUxF5DdC = __commonJS({
|
|
|
106801
107150
|
getStorageMigrationMoveStatus: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getStorageMigrationMoveStatus", "query", input),
|
|
106802
107151
|
cancelStorageMigrationMove: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "cancelStorageMigrationMove", "mutation", input),
|
|
106803
107152
|
relocateMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "relocateMedia", "mutation", input),
|
|
107153
|
+
countUnstampedEventMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "countUnstampedEventMedia", "query", input),
|
|
106804
107154
|
listRelocateMediaJobs: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRelocateMediaJobs", "query", input),
|
|
106805
107155
|
cancelRelocateMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "cancelRelocateMedia", "mutation", input),
|
|
106806
107156
|
listOpsLog: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listOpsLog", "query", input),
|
|
@@ -107765,7 +108115,7 @@ var require_addon = __commonJS({
|
|
|
107765
108115
|
"use strict";
|
|
107766
108116
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
107767
108117
|
var require_event_category = require_event_category_BaEgqJNv();
|
|
107768
|
-
var require_sleep =
|
|
108118
|
+
var require_sleep = require_sleep_CJrvRDlD();
|
|
107769
108119
|
var require_err_msg = require_err_msg_COpsHMw2();
|
|
107770
108120
|
var CAP_INPUT_DEFAULTS = Object.freeze({
|
|
107771
108121
|
"addons": { "getLogs": { "limit": 100 } },
|
|
@@ -114644,12 +114994,12 @@ var require_dist2 = __commonJS({
|
|
|
114644
114994
|
}
|
|
114645
114995
|
});
|
|
114646
114996
|
|
|
114647
|
-
// ../system/dist/manifest-python-deps-
|
|
114648
|
-
var
|
|
114649
|
-
"../system/dist/manifest-python-deps-
|
|
114997
|
+
// ../system/dist/manifest-python-deps-DVODn-qc.js
|
|
114998
|
+
var require_manifest_python_deps_DVODn_qc = __commonJS({
|
|
114999
|
+
"../system/dist/manifest-python-deps-DVODn-qc.js"(exports) {
|
|
114650
115000
|
"use strict";
|
|
114651
115001
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
114652
|
-
|
|
115002
|
+
require_dist_CDgIzo82();
|
|
114653
115003
|
var node_crypto = __require("crypto");
|
|
114654
115004
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
114655
115005
|
var _camstack_types_node = require_node();
|
|
@@ -114755,6 +115105,152 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
114755
115105
|
const top = window2.top.map((d) => `${d.childId}:${d.sent}/${d.suppressed}`).join(",");
|
|
114756
115106
|
return ` fanoutMode=uds:${window2.udsMode}/cross-node:${window2.crossNodeMode} children=${window2.childrenConnected} childrenUndeclared=${window2.childrenUndeclared} fanoutSent=${window2.fanoutSent} fanoutSuppressed=${window2.fanoutSuppressed} crossNodeDelivered=${window2.crossNodeDelivered} crossNodeSuppressed=${window2.crossNodeSuppressed}` + (top.length === 0 ? "" : ` fanoutTop=${top}`);
|
|
114757
115107
|
}
|
|
115108
|
+
function createSocketDirectionCounters() {
|
|
115109
|
+
return {
|
|
115110
|
+
reqMessages: 0,
|
|
115111
|
+
reqBytes: 0,
|
|
115112
|
+
resMessages: 0,
|
|
115113
|
+
resBytes: 0,
|
|
115114
|
+
evtMessages: 0,
|
|
115115
|
+
evtBytes: 0
|
|
115116
|
+
};
|
|
115117
|
+
}
|
|
115118
|
+
function recordSocketFrame(counters, kind, bytes) {
|
|
115119
|
+
if (kind === "evt") {
|
|
115120
|
+
counters.evtMessages += 1;
|
|
115121
|
+
counters.evtBytes += bytes;
|
|
115122
|
+
return;
|
|
115123
|
+
}
|
|
115124
|
+
if (kind === "req") {
|
|
115125
|
+
counters.reqMessages += 1;
|
|
115126
|
+
counters.reqBytes += bytes;
|
|
115127
|
+
return;
|
|
115128
|
+
}
|
|
115129
|
+
counters.resMessages += 1;
|
|
115130
|
+
counters.resBytes += bytes;
|
|
115131
|
+
}
|
|
115132
|
+
function sampleSocketDirection(counters) {
|
|
115133
|
+
return {
|
|
115134
|
+
reqMessages: counters.reqMessages,
|
|
115135
|
+
reqBytes: counters.reqBytes,
|
|
115136
|
+
resMessages: counters.resMessages,
|
|
115137
|
+
resBytes: counters.resBytes,
|
|
115138
|
+
evtMessages: counters.evtMessages,
|
|
115139
|
+
evtBytes: counters.evtBytes
|
|
115140
|
+
};
|
|
115141
|
+
}
|
|
115142
|
+
var EMPTY_SOCKET_DIRECTION = {
|
|
115143
|
+
reqMessages: 0,
|
|
115144
|
+
reqBytes: 0,
|
|
115145
|
+
resMessages: 0,
|
|
115146
|
+
resBytes: 0,
|
|
115147
|
+
evtMessages: 0,
|
|
115148
|
+
evtBytes: 0
|
|
115149
|
+
};
|
|
115150
|
+
function socketDirectionMessages(sample) {
|
|
115151
|
+
return sample.reqMessages + sample.resMessages + sample.evtMessages;
|
|
115152
|
+
}
|
|
115153
|
+
function socketDirectionBytes(sample) {
|
|
115154
|
+
return sample.reqBytes + sample.resBytes + sample.evtBytes;
|
|
115155
|
+
}
|
|
115156
|
+
function createSocketPlaneReader(sources) {
|
|
115157
|
+
return () => {
|
|
115158
|
+
try {
|
|
115159
|
+
const registry = sources.registry();
|
|
115160
|
+
if (registry === null) return void 0;
|
|
115161
|
+
const listed = registry.listChildren();
|
|
115162
|
+
const peers = [];
|
|
115163
|
+
for (const child of listed) {
|
|
115164
|
+
const traffic = registry.getChildSocketTraffic(child.childId);
|
|
115165
|
+
if (traffic === null) continue;
|
|
115166
|
+
peers.push({
|
|
115167
|
+
peerId: child.childId,
|
|
115168
|
+
tx: traffic.tx,
|
|
115169
|
+
rx: traffic.rx
|
|
115170
|
+
});
|
|
115171
|
+
}
|
|
115172
|
+
return {
|
|
115173
|
+
peers,
|
|
115174
|
+
peersConnected: listed.length
|
|
115175
|
+
};
|
|
115176
|
+
} catch {
|
|
115177
|
+
return;
|
|
115178
|
+
}
|
|
115179
|
+
};
|
|
115180
|
+
}
|
|
115181
|
+
var SOCKET_PLANE_TOP_N = 5;
|
|
115182
|
+
var EMPTY_SOCKET_PLANE_BASELINE = { peers: /* @__PURE__ */ new Map() };
|
|
115183
|
+
function diffDirection(current, before) {
|
|
115184
|
+
const previous = before ?? EMPTY_SOCKET_DIRECTION;
|
|
115185
|
+
return {
|
|
115186
|
+
reqMessages: Math.max(0, current.reqMessages - previous.reqMessages),
|
|
115187
|
+
reqBytes: Math.max(0, current.reqBytes - previous.reqBytes),
|
|
115188
|
+
resMessages: Math.max(0, current.resMessages - previous.resMessages),
|
|
115189
|
+
resBytes: Math.max(0, current.resBytes - previous.resBytes),
|
|
115190
|
+
evtMessages: Math.max(0, current.evtMessages - previous.evtMessages),
|
|
115191
|
+
evtBytes: Math.max(0, current.evtBytes - previous.evtBytes)
|
|
115192
|
+
};
|
|
115193
|
+
}
|
|
115194
|
+
function addDirection(into, add) {
|
|
115195
|
+
return {
|
|
115196
|
+
reqMessages: into.reqMessages + add.reqMessages,
|
|
115197
|
+
reqBytes: into.reqBytes + add.reqBytes,
|
|
115198
|
+
resMessages: into.resMessages + add.resMessages,
|
|
115199
|
+
resBytes: into.resBytes + add.resBytes,
|
|
115200
|
+
evtMessages: into.evtMessages + add.evtMessages,
|
|
115201
|
+
evtBytes: into.evtBytes + add.evtBytes
|
|
115202
|
+
};
|
|
115203
|
+
}
|
|
115204
|
+
function peerMessages(delta) {
|
|
115205
|
+
return socketDirectionMessages(delta.tx) + socketDirectionMessages(delta.rx);
|
|
115206
|
+
}
|
|
115207
|
+
function diffSocketPlane(baseline, current, topN = 5) {
|
|
115208
|
+
const deltas = [];
|
|
115209
|
+
let tx = EMPTY_SOCKET_DIRECTION;
|
|
115210
|
+
let rx = EMPTY_SOCKET_DIRECTION;
|
|
115211
|
+
for (const sample of current.peers) {
|
|
115212
|
+
const before = baseline.peers.get(sample.peerId);
|
|
115213
|
+
const delta = {
|
|
115214
|
+
peerId: sample.peerId,
|
|
115215
|
+
tx: diffDirection(sample.tx, before?.tx),
|
|
115216
|
+
rx: diffDirection(sample.rx, before?.rx)
|
|
115217
|
+
};
|
|
115218
|
+
tx = addDirection(tx, delta.tx);
|
|
115219
|
+
rx = addDirection(rx, delta.rx);
|
|
115220
|
+
if (peerMessages(delta) > 0) deltas.push(delta);
|
|
115221
|
+
}
|
|
115222
|
+
return {
|
|
115223
|
+
peersConnected: current.peersConnected,
|
|
115224
|
+
peersMeasured: current.peers.length,
|
|
115225
|
+
tx,
|
|
115226
|
+
rx,
|
|
115227
|
+
top: deltas.toSorted((a, b) => peerMessages(b) - peerMessages(a) || a.peerId.localeCompare(b.peerId)).slice(0, topN)
|
|
115228
|
+
};
|
|
115229
|
+
}
|
|
115230
|
+
function createSocketPlaneMeter(reader, topN = 5) {
|
|
115231
|
+
let baseline = EMPTY_SOCKET_PLANE_BASELINE;
|
|
115232
|
+
return { read: () => {
|
|
115233
|
+
const current = reader();
|
|
115234
|
+
if (current === void 0) return void 0;
|
|
115235
|
+
const window2 = diffSocketPlane(baseline, current, topN);
|
|
115236
|
+
baseline = { peers: new Map(current.peers.map((p) => [p.peerId, p])) };
|
|
115237
|
+
return window2;
|
|
115238
|
+
} };
|
|
115239
|
+
}
|
|
115240
|
+
function kb(bytes) {
|
|
115241
|
+
return Math.round(bytes / 1024);
|
|
115242
|
+
}
|
|
115243
|
+
function formatDirection(sample) {
|
|
115244
|
+
return `req:${sample.reqMessages}/${kb(sample.reqBytes)}kB,res:${sample.resMessages}/${kb(sample.resBytes)}kB,evt:${sample.evtMessages}/${kb(sample.evtBytes)}kB`;
|
|
115245
|
+
}
|
|
115246
|
+
function formatPeer(delta) {
|
|
115247
|
+
return `${delta.peerId}:tx=${delta.tx.reqMessages}/${delta.tx.resMessages}/${delta.tx.evtMessages}:rx=${delta.rx.reqMessages}/${delta.rx.resMessages}/${delta.rx.evtMessages}:kB=${kb(socketDirectionBytes(delta.tx))}/${kb(socketDirectionBytes(delta.rx))}`;
|
|
115248
|
+
}
|
|
115249
|
+
function formatSocketPlane(window2) {
|
|
115250
|
+
if (window2 === void 0) return "";
|
|
115251
|
+
const top = window2.top.map(formatPeer).join(",");
|
|
115252
|
+
return ` socketPeers=${window2.peersMeasured}/${window2.peersConnected} socketTx=${formatDirection(window2.tx)} socketRx=${formatDirection(window2.rx)}` + (top.length === 0 ? "" : ` socketTop=${top}`);
|
|
115253
|
+
}
|
|
114758
115254
|
var DECISIVE_HEAP_SPACES = ["old_space", "large_object_space"];
|
|
114759
115255
|
var HEAP_SPACE_REPORT_MIN_MB = 32;
|
|
114760
115256
|
var BYTES_PER_MB = 1048576;
|
|
@@ -114968,6 +115464,7 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
114968
115464
|
function startHeapWatch(label = HUB_MAIN_HEAP_WATCH_LABEL, sink = consoleSink, intervalMs = HEAP_WATCH_INTERVAL_MS, reclaimOptions, loopDelay = createLoopDelayMeter(), execArgv = process.execArgv, announceCeilingOrigin = true, rssBudget, probes) {
|
|
114969
115465
|
const readSpaces = probes?.readSpaces ?? readHeapSpaces;
|
|
114970
115466
|
const eventPlane = probes?.eventPlane === void 0 ? void 0 : createEventPlaneMeter(probes.eventPlane);
|
|
115467
|
+
const socketPlane = probes?.socketPlane === void 0 ? void 0 : createSocketPlaneMeter(probes.socketPlane);
|
|
114971
115468
|
const readMemory = reclaimOptions?.readMemory ?? (() => process.memoryUsage());
|
|
114972
115469
|
const now = reclaimOptions?.now ?? (() => Date.now());
|
|
114973
115470
|
const triggerMb = reclaimOptions?.triggerMb ?? 1024;
|
|
@@ -115003,7 +115500,7 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
115003
115500
|
return;
|
|
115004
115501
|
}
|
|
115005
115502
|
const after = read();
|
|
115006
|
-
sink.info(`[mem] reclaim ${label} stranded=${strandedMb(sample)}MB rss=${sample.rssMb}MB\u2192${after.rssMb}MB freed=${sample.rssMb - after.rssMb}MB arrayBuffers=${sample.arrayBuffersMb}MB\u2192${after.arrayBuffersMb}MB took=${now() - startedAt}ms`);
|
|
115503
|
+
sink.info(`[mem] reclaim ${label} stranded=${strandedMb(sample)}MB rss=${sample.rssMb}MB\u2192${after.rssMb}MB freed=${sample.rssMb - after.rssMb}MB arrayBuffers=${sample.arrayBuffersMb}MB\u2192${after.arrayBuffersMb}MB took=${now() - startedAt}ms heapUsed=${sample.heapUsedMb}MB\u2192${after.heapUsedMb}MB`);
|
|
115007
115504
|
if (passesAtFloor >= 6 && !steadyStateAnnounced) {
|
|
115008
115505
|
steadyStateAnnounced = true;
|
|
115009
115506
|
const nextFloorMs = reclaimIntervalMs(passesAtFloor, minIntervalMs, steadyStateIntervalMs);
|
|
@@ -115045,7 +115542,7 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
115045
115542
|
const due = at - lastLoggedAt >= intervalMs;
|
|
115046
115543
|
if (mode === "escalated" || due) {
|
|
115047
115544
|
lastLoggedAt = at;
|
|
115048
|
-
const line = format2(label, sample, loopDelay?.read(), budgetMb, `${formatHeapSpaces(readSpaces())}${formatEventPlane(eventPlane?.read())}`);
|
|
115545
|
+
const line = format2(label, sample, loopDelay?.read(), budgetMb, `${formatHeapSpaces(readSpaces())}${formatEventPlane(eventPlane?.read())}` + formatSocketPlane(socketPlane?.read()));
|
|
115049
115546
|
if (sample.nearLimit) sink.warn(`${line} \u2014 APPROACHING HEAP LIMIT`);
|
|
115050
115547
|
else if (mode === "escalated") sink.warn(`${line} \u2014 heap elevated, sampling every ${probeIntervalMs}ms`);
|
|
115051
115548
|
else sink.info(line);
|
|
@@ -118259,6 +118756,33 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
118259
118756
|
chunks = [];
|
|
118260
118757
|
/** Sum of `chunks[i].byteLength` — tracked so length checks cost nothing. */
|
|
118261
118758
|
buffered = 0;
|
|
118759
|
+
/** Backing store for {@link lastFrameBytes}; see that getter. */
|
|
118760
|
+
sizes = [];
|
|
118761
|
+
/**
|
|
118762
|
+
* On-the-wire byte length (4-byte prefix included) of each frame returned by
|
|
118763
|
+
* the most recent {@link push}, index-aligned with the returned array.
|
|
118764
|
+
*
|
|
118765
|
+
* It exists because the frames come back DECODED, and a decoded value has no
|
|
118766
|
+
* memory of how many bytes it cost — so a caller counting received bytes per
|
|
118767
|
+
* message kind (`SocketChannel`, feeding the `[mem]` line's per-peer socket
|
|
118768
|
+
* attribution) could otherwise only charge a whole `data` chunk, which may
|
|
118769
|
+
* hold several frames of different kinds, or half of one.
|
|
118770
|
+
*
|
|
118771
|
+
* **Only the first `frames.length` entries are meaningful**, and the array is
|
|
118772
|
+
* never truncated. That is deliberate and it was measured on Node 24.17:
|
|
118773
|
+
* `sizes.length = 0` + `push` costs **35.1–35.9 ns** per call — V8 shrinks the
|
|
118774
|
+
* backing store and the next call re-grows it — which is 3 % of
|
|
118775
|
+
* `FrameDecoder.push` itself and 15× the counter it feeds. Writing in place
|
|
118776
|
+
* costs **0.06–0.13 ns**. On a path that runs ~22 000 times a second, an
|
|
118777
|
+
* instrument may not be the most expensive thing on the line it measures.
|
|
118778
|
+
*
|
|
118779
|
+
* Read it immediately after the `push` that produced it — it is a window onto
|
|
118780
|
+
* the decoder, not a value. Safe because `push` is only ever driven by a
|
|
118781
|
+
* socket `data` event, which Node never delivers re-entrantly.
|
|
118782
|
+
*/
|
|
118783
|
+
get lastFrameBytes() {
|
|
118784
|
+
return this.sizes;
|
|
118785
|
+
}
|
|
118262
118786
|
push(chunk) {
|
|
118263
118787
|
if (chunk.byteLength > 0) {
|
|
118264
118788
|
this.chunks.push(chunk);
|
|
@@ -118276,6 +118800,7 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
118276
118800
|
body = Buffer.allocUnsafeSlow(len);
|
|
118277
118801
|
frame.copy(body, 0, HEADER_BYTES);
|
|
118278
118802
|
} else body = frame.subarray(HEADER_BYTES);
|
|
118803
|
+
this.sizes[frames.length] = total;
|
|
118279
118804
|
frames.push(decode(body));
|
|
118280
118805
|
}
|
|
118281
118806
|
return frames;
|
|
@@ -118355,6 +118880,11 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
118355
118880
|
};
|
|
118356
118881
|
closed = false;
|
|
118357
118882
|
closeFired = false;
|
|
118883
|
+
/** Frames written to the peer, cumulative, split by kind. See
|
|
118884
|
+
* {@link readTraffic} and `transport/socket-traffic.ts`. */
|
|
118885
|
+
txCounters = createSocketDirectionCounters();
|
|
118886
|
+
/** Frames read from the peer, cumulative, split by kind. */
|
|
118887
|
+
rxCounters = createSocketDirectionCounters();
|
|
118358
118888
|
constructor(socket) {
|
|
118359
118889
|
this.socket = socket;
|
|
118360
118890
|
socket.on("data", (chunk) => this.onData(chunk));
|
|
@@ -118422,14 +118952,41 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
118422
118952
|
this.closed = true;
|
|
118423
118953
|
this.socket.destroy();
|
|
118424
118954
|
}
|
|
118955
|
+
/**
|
|
118956
|
+
* This channel's cumulative traffic, both directions, split by frame kind.
|
|
118957
|
+
*
|
|
118958
|
+
* Cumulative for the life of the channel: the reporting layer subtracts the
|
|
118959
|
+
* previous reading, because the question is a RATE ("which peer receives the
|
|
118960
|
+
* 22 500 writes/s") and not a total since boot.
|
|
118961
|
+
*/
|
|
118962
|
+
readTraffic() {
|
|
118963
|
+
return {
|
|
118964
|
+
tx: sampleSocketDirection(this.txCounters),
|
|
118965
|
+
rx: sampleSocketDirection(this.rxCounters)
|
|
118966
|
+
};
|
|
118967
|
+
}
|
|
118425
118968
|
send(frame) {
|
|
118426
118969
|
if (this.closed) return;
|
|
118427
|
-
|
|
118970
|
+
const encoded = encodeFrame(frame);
|
|
118971
|
+
recordSocketFrame(this.txCounters, frame.k, encoded.byteLength);
|
|
118972
|
+
this.socket.write(encoded);
|
|
118428
118973
|
}
|
|
118429
118974
|
onData(chunk) {
|
|
118430
|
-
|
|
118975
|
+
const frames = this.decoder.push(chunk);
|
|
118976
|
+
const sizes = this.decoder.lastFrameBytes;
|
|
118977
|
+
for (let i = 0; i < frames.length; i += 1) this.handleFrame(frames[i], sizes[i] ?? 0);
|
|
118431
118978
|
}
|
|
118432
|
-
|
|
118979
|
+
/**
|
|
118980
|
+
* `wireBytes` is the frame's on-the-wire length, counted HERE rather than in
|
|
118981
|
+
* `onData` on purpose: a peer that sends a frame this decoder can parse but
|
|
118982
|
+
* whose shape is not a `Frame` throws on `frame.k`, and inside this async
|
|
118983
|
+
* method that stays the rejected promise it has always been. Reading `.k`
|
|
118984
|
+
* one frame earlier, synchronously in the socket's `data` handler, would
|
|
118985
|
+
* turn the same malformed frame into an uncaught exception on the transport
|
|
118986
|
+
* — a diagnostic that can crash the process it watches.
|
|
118987
|
+
*/
|
|
118988
|
+
async handleFrame(frame, wireBytes) {
|
|
118989
|
+
recordSocketFrame(this.rxCounters, frame.k, wireBytes);
|
|
118433
118990
|
if (frame.k === "req") {
|
|
118434
118991
|
try {
|
|
118435
118992
|
const result = await this.requestHandler(frame.body);
|
|
@@ -119150,6 +119707,22 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
119150
119707
|
};
|
|
119151
119708
|
}
|
|
119152
119709
|
/**
|
|
119710
|
+
* This child's socket traffic — bytes and messages, both directions, split by
|
|
119711
|
+
* frame kind — or `null` when the child is gone or its channel keeps no
|
|
119712
|
+
* counters (an in-process or test transport).
|
|
119713
|
+
*
|
|
119714
|
+
* The event counters above cover ONE kind of frame and, once surfaced on
|
|
119715
|
+
* 2026-08-29, accounted for 4% of hub-main's ~44 000 socket syscalls/s. This
|
|
119716
|
+
* is the same question asked of every frame the channel moves, and it is the
|
|
119717
|
+
* only per-peer attribution that exists: `/proc` reports one `rchar`/`wchar`
|
|
119718
|
+
* pair for the whole process and has no per-socket breakdown.
|
|
119719
|
+
*/
|
|
119720
|
+
getChildSocketTraffic(childId) {
|
|
119721
|
+
const entry = this.children.get(childId);
|
|
119722
|
+
if (entry === void 0) return null;
|
|
119723
|
+
return entry.channel.readTraffic?.() ?? null;
|
|
119724
|
+
}
|
|
119725
|
+
/**
|
|
119153
119726
|
* The regime the counters above were produced under, read once from
|
|
119154
119727
|
* `CAMSTACK_UDS_EVENT_FANOUT` at construction.
|
|
119155
119728
|
*
|
|
@@ -121645,6 +122218,18 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
121645
122218
|
return DeviceRegistry;
|
|
121646
122219
|
}
|
|
121647
122220
|
});
|
|
122221
|
+
Object.defineProperty(exports, "EMPTY_SOCKET_DIRECTION", {
|
|
122222
|
+
enumerable: true,
|
|
122223
|
+
get: function() {
|
|
122224
|
+
return EMPTY_SOCKET_DIRECTION;
|
|
122225
|
+
}
|
|
122226
|
+
});
|
|
122227
|
+
Object.defineProperty(exports, "EMPTY_SOCKET_PLANE_BASELINE", {
|
|
122228
|
+
enumerable: true,
|
|
122229
|
+
get: function() {
|
|
122230
|
+
return EMPTY_SOCKET_PLANE_BASELINE;
|
|
122231
|
+
}
|
|
122232
|
+
});
|
|
121648
122233
|
Object.defineProperty(exports, "EVENT_PLANE_TOP_N", {
|
|
121649
122234
|
enumerable: true,
|
|
121650
122235
|
get: function() {
|
|
@@ -121777,6 +122362,12 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
121777
122362
|
return RUNNER_RSS_BUDGET_ENV;
|
|
121778
122363
|
}
|
|
121779
122364
|
});
|
|
122365
|
+
Object.defineProperty(exports, "SOCKET_PLANE_TOP_N", {
|
|
122366
|
+
enumerable: true,
|
|
122367
|
+
get: function() {
|
|
122368
|
+
return SOCKET_PLANE_TOP_N;
|
|
122369
|
+
}
|
|
122370
|
+
});
|
|
121780
122371
|
Object.defineProperty(exports, "SocketChannel", {
|
|
121781
122372
|
enumerable: true,
|
|
121782
122373
|
get: function() {
|
|
@@ -121975,6 +122566,24 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
121975
122566
|
return createParentUnownedCallHandler;
|
|
121976
122567
|
}
|
|
121977
122568
|
});
|
|
122569
|
+
Object.defineProperty(exports, "createSocketDirectionCounters", {
|
|
122570
|
+
enumerable: true,
|
|
122571
|
+
get: function() {
|
|
122572
|
+
return createSocketDirectionCounters;
|
|
122573
|
+
}
|
|
122574
|
+
});
|
|
122575
|
+
Object.defineProperty(exports, "createSocketPlaneMeter", {
|
|
122576
|
+
enumerable: true,
|
|
122577
|
+
get: function() {
|
|
122578
|
+
return createSocketPlaneMeter;
|
|
122579
|
+
}
|
|
122580
|
+
});
|
|
122581
|
+
Object.defineProperty(exports, "createSocketPlaneReader", {
|
|
122582
|
+
enumerable: true,
|
|
122583
|
+
get: function() {
|
|
122584
|
+
return createSocketPlaneReader;
|
|
122585
|
+
}
|
|
122586
|
+
});
|
|
121978
122587
|
Object.defineProperty(exports, "createUdsAddonContext", {
|
|
121979
122588
|
enumerable: true,
|
|
121980
122589
|
get: function() {
|
|
@@ -122029,6 +122638,12 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
122029
122638
|
return diffEventPlane;
|
|
122030
122639
|
}
|
|
122031
122640
|
});
|
|
122641
|
+
Object.defineProperty(exports, "diffSocketPlane", {
|
|
122642
|
+
enumerable: true,
|
|
122643
|
+
get: function() {
|
|
122644
|
+
return diffSocketPlane;
|
|
122645
|
+
}
|
|
122646
|
+
});
|
|
122032
122647
|
Object.defineProperty(exports, "emitHeapDiagnosticReport", {
|
|
122033
122648
|
enumerable: true,
|
|
122034
122649
|
get: function() {
|
|
@@ -122053,6 +122668,12 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
122053
122668
|
return formatHeapSpaces;
|
|
122054
122669
|
}
|
|
122055
122670
|
});
|
|
122671
|
+
Object.defineProperty(exports, "formatSocketPlane", {
|
|
122672
|
+
enumerable: true,
|
|
122673
|
+
get: function() {
|
|
122674
|
+
return formatSocketPlane;
|
|
122675
|
+
}
|
|
122676
|
+
});
|
|
122056
122677
|
Object.defineProperty(exports, "getBrokerEventBus", {
|
|
122057
122678
|
enumerable: true,
|
|
122058
122679
|
get: function() {
|
|
@@ -122191,6 +122812,12 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
122191
122812
|
return reclaimIntervalMs;
|
|
122192
122813
|
}
|
|
122193
122814
|
});
|
|
122815
|
+
Object.defineProperty(exports, "recordSocketFrame", {
|
|
122816
|
+
enumerable: true,
|
|
122817
|
+
get: function() {
|
|
122818
|
+
return recordSocketFrame;
|
|
122819
|
+
}
|
|
122820
|
+
});
|
|
122194
122821
|
Object.defineProperty(exports, "registerEventBusService", {
|
|
122195
122822
|
enumerable: true,
|
|
122196
122823
|
get: function() {
|
|
@@ -122221,6 +122848,12 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
122221
122848
|
return runNpm;
|
|
122222
122849
|
}
|
|
122223
122850
|
});
|
|
122851
|
+
Object.defineProperty(exports, "sampleSocketDirection", {
|
|
122852
|
+
enumerable: true,
|
|
122853
|
+
get: function() {
|
|
122854
|
+
return sampleSocketDirection;
|
|
122855
|
+
}
|
|
122856
|
+
});
|
|
122224
122857
|
Object.defineProperty(exports, "selectReportedSpaces", {
|
|
122225
122858
|
enumerable: true,
|
|
122226
122859
|
get: function() {
|
|
@@ -122257,6 +122890,18 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
|
|
|
122257
122890
|
return shouldReclaim;
|
|
122258
122891
|
}
|
|
122259
122892
|
});
|
|
122893
|
+
Object.defineProperty(exports, "socketDirectionBytes", {
|
|
122894
|
+
enumerable: true,
|
|
122895
|
+
get: function() {
|
|
122896
|
+
return socketDirectionBytes;
|
|
122897
|
+
}
|
|
122898
|
+
});
|
|
122899
|
+
Object.defineProperty(exports, "socketDirectionMessages", {
|
|
122900
|
+
enumerable: true,
|
|
122901
|
+
get: function() {
|
|
122902
|
+
return socketDirectionMessages;
|
|
122903
|
+
}
|
|
122904
|
+
});
|
|
122260
122905
|
Object.defineProperty(exports, "startHeapWatch", {
|
|
122261
122906
|
enumerable: true,
|
|
122262
122907
|
get: function() {
|
|
@@ -126095,7 +126740,7 @@ var require_dist3 = __commonJS({
|
|
|
126095
126740
|
"use strict";
|
|
126096
126741
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
126097
126742
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
126098
|
-
var require_dist10 =
|
|
126743
|
+
var require_dist10 = require_dist_CDgIzo82();
|
|
126099
126744
|
var require_builtins_alerts_alerts_addon = require_alerts_addon();
|
|
126100
126745
|
require_alerts();
|
|
126101
126746
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
@@ -126121,7 +126766,7 @@ var require_dist3 = __commonJS({
|
|
|
126121
126766
|
var require_builtins_winston_logging_index = require_winston_logging();
|
|
126122
126767
|
var require_file_data_plane = require_file_data_plane_DO8KbxCe();
|
|
126123
126768
|
var require_tls$1 = require_tls_BxQlomxd();
|
|
126124
|
-
var require_manifest_python_deps =
|
|
126769
|
+
var require_manifest_python_deps = require_manifest_python_deps_DVODn_qc();
|
|
126125
126770
|
var require_resource_monitor = require_resource_monitor_CdnzxBLP();
|
|
126126
126771
|
var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
|
|
126127
126772
|
var zod = require_zod();
|
|
@@ -206502,6 +207147,8 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
206502
207147
|
exports.DeviceManagerAddon = require_builtins_device_manager_device_manager_addon.DeviceManagerAddon;
|
|
206503
207148
|
exports.DeviceRegistry = require_manifest_python_deps.DeviceRegistry;
|
|
206504
207149
|
exports.DeviceStore = require_builtins_sqlite_storage_index.DeviceStore$1;
|
|
207150
|
+
exports.EMPTY_SOCKET_DIRECTION = require_manifest_python_deps.EMPTY_SOCKET_DIRECTION;
|
|
207151
|
+
exports.EMPTY_SOCKET_PLANE_BASELINE = require_manifest_python_deps.EMPTY_SOCKET_PLANE_BASELINE;
|
|
206505
207152
|
exports.EVENT_PLANE_TOP_N = require_manifest_python_deps.EVENT_PLANE_TOP_N;
|
|
206506
207153
|
exports.EVENT_TOPIC_PREFIX = require_manifest_python_deps.EVENT_TOPIC_PREFIX;
|
|
206507
207154
|
exports.EngineManagerResolver = EngineManagerResolver;
|
|
@@ -206575,6 +207222,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
206575
207222
|
exports.ReplEngine = ReplEngine;
|
|
206576
207223
|
exports.RingBuffer = RingBuffer;
|
|
206577
207224
|
exports.SERVER_AUTH_OID = require_tls$1.SERVER_AUTH_OID;
|
|
207225
|
+
exports.SOCKET_PLANE_TOP_N = require_manifest_python_deps.SOCKET_PLANE_TOP_N;
|
|
206578
207226
|
exports.ScopedLogger = ScopedLogger;
|
|
206579
207227
|
exports.ScopedTokenManager = require_builtins_local_auth_local_auth_addon.ScopedTokenManager;
|
|
206580
207228
|
exports.SocketChannel = require_manifest_python_deps.SocketChannel;
|
|
@@ -206660,6 +207308,9 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
206660
207308
|
exports.createReadinessService = createReadinessService;
|
|
206661
207309
|
exports.createReadinessServiceForRegistry = createReadinessServiceForRegistry;
|
|
206662
207310
|
exports.createScopedProcessManager = createScopedProcessManager;
|
|
207311
|
+
exports.createSocketDirectionCounters = require_manifest_python_deps.createSocketDirectionCounters;
|
|
207312
|
+
exports.createSocketPlaneMeter = require_manifest_python_deps.createSocketPlaneMeter;
|
|
207313
|
+
exports.createSocketPlaneReader = require_manifest_python_deps.createSocketPlaneReader;
|
|
206663
207314
|
exports.createStreamProbeBrokerService = createStreamProbeBrokerService;
|
|
206664
207315
|
exports.createUdsAddonContext = require_manifest_python_deps.createUdsAddonContext;
|
|
206665
207316
|
exports.createUdsEventBridge = require_manifest_python_deps.createUdsEventBridge;
|
|
@@ -206673,6 +207324,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
206673
207324
|
exports.describeRss = require_manifest_python_deps.describeRss;
|
|
206674
207325
|
exports.detectWorkspacePackagesDir = detectWorkspacePackagesDir;
|
|
206675
207326
|
exports.diffEventPlane = require_manifest_python_deps.diffEventPlane;
|
|
207327
|
+
exports.diffSocketPlane = require_manifest_python_deps.diffSocketPlane;
|
|
206676
207328
|
Object.defineProperty(exports, "downloadBinary", {
|
|
206677
207329
|
enumerable: true,
|
|
206678
207330
|
get: function() {
|
|
@@ -206720,6 +207372,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
206720
207372
|
exports.formatEventPlane = require_manifest_python_deps.formatEventPlane;
|
|
206721
207373
|
exports.formatHeapSpaces = require_manifest_python_deps.formatHeapSpaces;
|
|
206722
207374
|
exports.formatLogLine = require_formatter.formatLogLine;
|
|
207375
|
+
exports.formatSocketPlane = require_manifest_python_deps.formatSocketPlane;
|
|
206723
207376
|
exports.getBrokerEventBus = require_manifest_python_deps.getBrokerEventBus;
|
|
206724
207377
|
exports.getCapUsageRegistry = require_manifest_python_deps.getCapUsageRegistry;
|
|
206725
207378
|
Object.defineProperty(exports, "getFfmpegDownloadUrl", {
|
|
@@ -206799,6 +207452,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
206799
207452
|
exports.readTlsMode = require_tls$1.readTlsMode;
|
|
206800
207453
|
exports.readinessKey = require_dist10.readinessKey;
|
|
206801
207454
|
exports.reclaimIntervalMs = require_manifest_python_deps.reclaimIntervalMs;
|
|
207455
|
+
exports.recordSocketFrame = require_manifest_python_deps.recordSocketFrame;
|
|
206802
207456
|
exports.registerEventBusService = require_manifest_python_deps.registerEventBusService;
|
|
206803
207457
|
exports.registerLanHttpHandler = require_tls$1.registerLanHttpHandler;
|
|
206804
207458
|
exports.reissueTlsLeaf = require_tls$1.reissueTlsLeaf;
|
|
@@ -206807,6 +207461,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
206807
207461
|
exports.resolveNpmInvocation = require_manifest_python_deps.resolveNpmInvocation;
|
|
206808
207462
|
exports.runHubAddonBoot = runHubAddonBoot;
|
|
206809
207463
|
exports.runNpm = require_manifest_python_deps.runNpm;
|
|
207464
|
+
exports.sampleSocketDirection = require_manifest_python_deps.sampleSocketDirection;
|
|
206810
207465
|
exports.scheduleSelfRestart = scheduleSelfRestart;
|
|
206811
207466
|
exports.scopeKey = require_dist10.scopeKey;
|
|
206812
207467
|
exports.scopesAllowAddon = require_dist10.scopesAllowAddon;
|
|
@@ -206817,6 +207472,8 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
206817
207472
|
exports.setHubConnected = require_manifest_python_deps.setHubConnected;
|
|
206818
207473
|
exports.setNodeEventInterest = require_manifest_python_deps.setNodeEventInterest;
|
|
206819
207474
|
exports.shouldReclaim = require_manifest_python_deps.shouldReclaim;
|
|
207475
|
+
exports.socketDirectionBytes = require_manifest_python_deps.socketDirectionBytes;
|
|
207476
|
+
exports.socketDirectionMessages = require_manifest_python_deps.socketDirectionMessages;
|
|
206820
207477
|
exports.startHeapWatch = require_manifest_python_deps.startHeapWatch;
|
|
206821
207478
|
exports.startRunnerHeapWatch = require_manifest_python_deps.startRunnerHeapWatch;
|
|
206822
207479
|
exports.strandedMb = require_manifest_python_deps.strandedMb;
|
|
@@ -206858,7 +207515,7 @@ var require_dist4 = __commonJS({
|
|
|
206858
207515
|
"use strict";
|
|
206859
207516
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
206860
207517
|
var require_event_category = require_event_category_BaEgqJNv();
|
|
206861
|
-
var require_sleep =
|
|
207518
|
+
var require_sleep = require_sleep_CJrvRDlD();
|
|
206862
207519
|
var require_canonical_hash = require_canonical_hash_DNV8S5ET();
|
|
206863
207520
|
var require_enums2 = require_enums();
|
|
206864
207521
|
var require_err_msg = require_err_msg_COpsHMw2();
|
|
@@ -208179,29 +208836,50 @@ var require_dist4 = __commonJS({
|
|
|
208179
208836
|
});
|
|
208180
208837
|
var StorageMigrationLeaseInputSchema = zod.z.object({ leaseId: zod.z.string().min(1) });
|
|
208181
208838
|
var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: zod.z.string().min(1) });
|
|
208839
|
+
var MediaRelocateModeSchema = zod.z.enum([
|
|
208840
|
+
"move",
|
|
208841
|
+
"seal",
|
|
208842
|
+
"gallery"
|
|
208843
|
+
]);
|
|
208182
208844
|
var RelocateMediaInputSchema = zod.z.object({
|
|
208183
208845
|
toLocationId: zod.z.string(),
|
|
208184
|
-
throttleMbps: zod.z.number().min(1).max(1e3).optional()
|
|
208846
|
+
throttleMbps: zod.z.number().min(1).max(1e3).optional(),
|
|
208847
|
+
/** Omitted = `move`, the pre-existing behaviour. */
|
|
208848
|
+
mode: MediaRelocateModeSchema.optional()
|
|
208849
|
+
});
|
|
208850
|
+
var UnstampedEventMediaCountSchema = zod.z.object({
|
|
208851
|
+
media: zod.z.number().int().nonnegative(),
|
|
208852
|
+
retrainFrames: zod.z.number().int().nonnegative(),
|
|
208853
|
+
total: zod.z.number().int().nonnegative()
|
|
208185
208854
|
});
|
|
208186
208855
|
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: zod.z.string().min(1) });
|
|
208187
208856
|
var StorageMigrationClassSchema = zod.z.enum([
|
|
208188
208857
|
"recordings",
|
|
208189
208858
|
"recordingsLow",
|
|
208190
|
-
"eventMedia"
|
|
208859
|
+
"eventMedia",
|
|
208860
|
+
"backups",
|
|
208861
|
+
"galleryMedia"
|
|
208191
208862
|
]);
|
|
208192
208863
|
var StorageMigrationDestinationsSchema = zod.z.object({
|
|
208193
208864
|
recordings: zod.z.string().min(1).optional(),
|
|
208194
208865
|
recordingsLow: zod.z.string().min(1).optional(),
|
|
208195
|
-
eventMedia: zod.z.string().min(1).optional()
|
|
208866
|
+
eventMedia: zod.z.string().min(1).optional(),
|
|
208867
|
+
backups: zod.z.string().min(1).optional(),
|
|
208868
|
+
galleryMedia: zod.z.string().min(1).optional()
|
|
208196
208869
|
}).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
|
|
208870
|
+
var StorageMigrationModeSchema = zod.z.enum(["blocking", "nonBlocking"]);
|
|
208197
208871
|
var StorageMigrationInputSchema = zod.z.object({
|
|
208198
208872
|
destinations: StorageMigrationDestinationsSchema,
|
|
208199
|
-
throttleMbps: zod.z.number().min(1).max(1e3).optional()
|
|
208873
|
+
throttleMbps: zod.z.number().min(1).max(1e3).optional(),
|
|
208874
|
+
/** Omitted = `blocking`, which stays the default. */
|
|
208875
|
+
mode: StorageMigrationModeSchema.optional()
|
|
208200
208876
|
});
|
|
208201
208877
|
var StorageMigrationPhaseSchema = zod.z.enum([
|
|
208202
208878
|
"planning",
|
|
208879
|
+
"sealing",
|
|
208203
208880
|
"pausing",
|
|
208204
208881
|
"moving",
|
|
208882
|
+
"draining",
|
|
208205
208883
|
"verifying",
|
|
208206
208884
|
"repointing",
|
|
208207
208885
|
"refreshing",
|
|
@@ -208226,6 +208904,9 @@ var require_dist4 = __commonJS({
|
|
|
208226
208904
|
var StorageMigrationJobSchema = zod.z.object({
|
|
208227
208905
|
jobId: zod.z.string(),
|
|
208228
208906
|
phase: StorageMigrationPhaseSchema,
|
|
208907
|
+
/** Which order this job is running. `status` is the only place an operator
|
|
208908
|
+
* can tell a seconds-long cutover from a thirty-hour one. */
|
|
208909
|
+
mode: StorageMigrationModeSchema,
|
|
208229
208910
|
destinations: StorageMigrationDestinationsSchema,
|
|
208230
208911
|
throttleMbps: zod.z.number(),
|
|
208231
208912
|
moves: zod.z.array(StorageMigrationMoveSchema),
|
|
@@ -208238,13 +208919,31 @@ var require_dist4 = __commonJS({
|
|
|
208238
208919
|
finishedAt: zod.z.number().nullable(),
|
|
208239
208920
|
error: zod.z.string().nullable()
|
|
208240
208921
|
});
|
|
208922
|
+
var StorageMigrationFindingCodeSchema = zod.z.enum([
|
|
208923
|
+
"sharesDeviceWithSource",
|
|
208924
|
+
"deviceIdentityUnknown",
|
|
208925
|
+
"unstampedEventMediaRows",
|
|
208926
|
+
"blockingOnly",
|
|
208927
|
+
"noMover"
|
|
208928
|
+
]);
|
|
208929
|
+
var StorageMigrationFindingSchema = zod.z.object({
|
|
208930
|
+
code: StorageMigrationFindingCodeSchema,
|
|
208931
|
+
storageClass: StorageMigrationClassSchema,
|
|
208932
|
+
/** Human-readable, already carrying the ids and counts. */
|
|
208933
|
+
message: zod.z.string()
|
|
208934
|
+
});
|
|
208241
208935
|
var StorageMigrationPlanSchema = zod.z.object({
|
|
208242
208936
|
destinations: StorageMigrationDestinationsSchema,
|
|
208937
|
+
/** The mode this plan was built for. A plan is only valid for its mode: the
|
|
208938
|
+
* `eventMedia` seal gate and the single-cardinality refusal both depend on
|
|
208939
|
+
* it. */
|
|
208940
|
+
mode: StorageMigrationModeSchema,
|
|
208243
208941
|
moves: zod.z.array(zod.z.object({
|
|
208244
208942
|
storageClass: StorageMigrationClassSchema,
|
|
208245
208943
|
fromLocationId: zod.z.string(),
|
|
208246
208944
|
toLocationId: zod.z.string()
|
|
208247
|
-
}))
|
|
208945
|
+
})),
|
|
208946
|
+
findings: zod.z.array(StorageMigrationFindingSchema)
|
|
208248
208947
|
});
|
|
208249
208948
|
var SUB_DETECTION_TYPES = ["face", "plate"];
|
|
208250
208949
|
var RECOGNITION_TYPES = [
|
|
@@ -221220,6 +221919,17 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
221220
221919
|
kind: "mutation",
|
|
221221
221920
|
auth: "admin"
|
|
221222
221921
|
}),
|
|
221922
|
+
/**
|
|
221923
|
+
* How many media / retrain rows still carry NO `locationId`.
|
|
221924
|
+
*
|
|
221925
|
+
* A NULL row means "wherever `eventMedia` points NOW", so the instant a
|
|
221926
|
+
* repoint moves that pointer every such row reads from the new disk while
|
|
221927
|
+
* its bytes are on the old one — the archive goes dark until a drain
|
|
221928
|
+
* happens to stamp it. This count is what the migration planner's
|
|
221929
|
+
* non-blocking gate reads; `relocateMedia({ mode: 'seal' })` is what drives
|
|
221930
|
+
* it to zero.
|
|
221931
|
+
*/
|
|
221932
|
+
countUnstampedEventMedia: require_sleep.method(zod.z.object({}), UnstampedEventMediaCountSchema, { auth: "admin" }),
|
|
221223
221933
|
/** Every relocate job this addon knows about, newest first (in RAM: the
|
|
221224
221934
|
* move is resumable, so a lost list costs nothing but the display). */
|
|
221225
221935
|
listRelocateMediaJobs: require_sleep.method(zod.z.object({}), zod.z.array(RelocateJobSchema).readonly(), {
|
|
@@ -223836,7 +224546,24 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
223836
224546
|
kind: "mutation",
|
|
223837
224547
|
auth: "admin"
|
|
223838
224548
|
}),
|
|
223839
|
-
|
|
224549
|
+
/**
|
|
224550
|
+
* Remove a location record. REFUSES a location that still holds data —
|
|
224551
|
+
* deleting a drained location whose durable rows still name it is how this
|
|
224552
|
+
* hub acquired 2 131 ghost `recordings:high` segments, and playback does
|
|
224553
|
+
* not stat, so the operator sees a silent black window rather than an
|
|
224554
|
+
* error.
|
|
224555
|
+
*
|
|
224556
|
+
* `force` exists because the occupancy check is BEST-EFFORT and refuses on
|
|
224557
|
+
* "unknown" as well as on "occupied" (a read that fails must not authorise
|
|
224558
|
+
* a destruction — D49). A location on a removed disk, on another node, or
|
|
224559
|
+
* behind a remote provider answers "unknown" forever, and a refusal an
|
|
224560
|
+
* operator cannot override is its own failure mode. `force: true` is
|
|
224561
|
+
* logged, loudly, with what the check saw.
|
|
224562
|
+
*/
|
|
224563
|
+
deleteLocation: require_sleep.method(zod.z.object({
|
|
224564
|
+
id: zod.z.string(),
|
|
224565
|
+
force: zod.z.boolean().optional()
|
|
224566
|
+
}), zod.z.void(), {
|
|
223840
224567
|
kind: "mutation",
|
|
223841
224568
|
auth: "admin"
|
|
223842
224569
|
}),
|
|
@@ -239875,6 +240602,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
239875
240602
|
addonId: null,
|
|
239876
240603
|
access: "create"
|
|
239877
240604
|
},
|
|
240605
|
+
"pipelineAnalytics.countUnstampedEventMedia": {
|
|
240606
|
+
capName: "pipeline-analytics",
|
|
240607
|
+
capScope: "device",
|
|
240608
|
+
addonId: null,
|
|
240609
|
+
access: "view"
|
|
240610
|
+
},
|
|
239878
240611
|
"pipelineAnalytics.deleteDeviceEvents": {
|
|
239879
240612
|
capName: "pipeline-analytics",
|
|
239880
240613
|
capScope: "device",
|
|
@@ -248898,6 +249631,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
248898
249631
|
exports.MediaPlayerRepeatSchema = MediaPlayerRepeatSchema;
|
|
248899
249632
|
exports.MediaPlayerStateSchema = MediaPlayerStateSchema;
|
|
248900
249633
|
exports.MediaPlayerStatusSchema = MediaPlayerStatusSchema;
|
|
249634
|
+
exports.MediaRelocateModeSchema = MediaRelocateModeSchema;
|
|
248901
249635
|
exports.MeshPeerSchema = MeshPeerSchema;
|
|
248902
249636
|
exports.MeshStatusSchema = MeshStatusSchema;
|
|
248903
249637
|
exports.MethodAccessSchema = MethodAccessSchema;
|
|
@@ -249275,11 +250009,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
249275
250009
|
exports.StorageLocationTypeSchema = StorageLocationTypeSchema;
|
|
249276
250010
|
exports.StorageMigrationClassSchema = StorageMigrationClassSchema;
|
|
249277
250011
|
exports.StorageMigrationDestinationsSchema = StorageMigrationDestinationsSchema;
|
|
250012
|
+
exports.StorageMigrationFindingCodeSchema = StorageMigrationFindingCodeSchema;
|
|
250013
|
+
exports.StorageMigrationFindingSchema = StorageMigrationFindingSchema;
|
|
249278
250014
|
exports.StorageMigrationFootageMoveInputSchema = StorageMigrationFootageMoveInputSchema;
|
|
249279
250015
|
exports.StorageMigrationInputSchema = StorageMigrationInputSchema;
|
|
249280
250016
|
exports.StorageMigrationJobSchema = StorageMigrationJobSchema;
|
|
249281
250017
|
exports.StorageMigrationLeaseInputSchema = StorageMigrationLeaseInputSchema;
|
|
249282
250018
|
exports.StorageMigrationMediaMoveInputSchema = StorageMigrationMediaMoveInputSchema;
|
|
250019
|
+
exports.StorageMigrationModeSchema = StorageMigrationModeSchema;
|
|
249283
250020
|
exports.StorageMigrationMoveSchema = StorageMigrationMoveSchema;
|
|
249284
250021
|
exports.StorageMigrationParticipantSchema = StorageMigrationParticipantSchema;
|
|
249285
250022
|
exports.StorageMigrationPhaseSchema = StorageMigrationPhaseSchema;
|
|
@@ -249355,6 +250092,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
249355
250092
|
exports.UNIT_TABLE = UNIT_TABLE;
|
|
249356
250093
|
exports.UnifiedBrokerInfoSchema = BrokerInfoSchema$1;
|
|
249357
250094
|
exports.UnitConversionError = UnitConversionError;
|
|
250095
|
+
exports.UnstampedEventMediaCountSchema = UnstampedEventMediaCountSchema;
|
|
249358
250096
|
exports.UpdateIntegrationInputSchema = UpdateIntegrationInputSchema;
|
|
249359
250097
|
exports.UpdateStatusSchema = UpdateStatusSchema;
|
|
249360
250098
|
exports.UpdateUserInputSchema = UpdateUserInputSchema;
|
|
@@ -421823,6 +422561,17 @@ var require_main4 = __commonJS({
|
|
|
421823
422561
|
return broker === void 0 ? null : (0, system_1.getMoleculerEventStats)(broker);
|
|
421824
422562
|
},
|
|
421825
422563
|
crossNodeMode: () => (0, system_1.readMoleculerFanoutMode)()
|
|
422564
|
+
}),
|
|
422565
|
+
// The fan-out counters above answered their own question and sharpened
|
|
422566
|
+
// this one: the event plane is 4% of this process's ~44 000 socket
|
|
422567
|
+
// syscalls/s. The other 96% had no instrument — `/proc` reports one
|
|
422568
|
+
// rchar/wchar pair per PROCESS and has no per-socket breakdown, so the
|
|
422569
|
+
// best attribution available was a correlation with `hub/pipeline-
|
|
422570
|
+
// analytics` computed from a runner whose rchar also counts page-cache
|
|
422571
|
+
// reads. These counters live on the channel itself, so the attribution is
|
|
422572
|
+
// per peer and per frame kind. Same lazy registry, same reason.
|
|
422573
|
+
socketPlane: (0, system_1.createSocketPlaneReader)({
|
|
422574
|
+
registry: () => moleculerForEventPlane?.childRegistry ?? null
|
|
421826
422575
|
})
|
|
421827
422576
|
});
|
|
421828
422577
|
cleanupOrphanProcesses();
|